mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-05-02 00:53:08 +00:00
Migrate device-sessions from Redis to PostgreSQL.
This migrates the device-sessions from Redis into PostgreSQL. This fixes a performance issue in case the same DevAddr is reused many times (e.g. devices rejoining very often or a NetID with small DevAddr space). There were two issues: The Redis key containing the DevAddr -> DevEUIs mapping could contain DevEUIs that no longer used the DevAddr. This mapping would only expire from the Redis database after none of the devices would use the DevAddr for more than the configured device_session_ttl. The other issue with the previous approach was that on for example a Type 7 NetID, a single DevAddr could be re-used multiple times. As each device-session could be stored on a different Redis Cluster instance, there was no option to retrieve all device-sessions at once. Thus a high re-usage of a single DevAddr would cause an increase in Redis queries. Both issues are solved by moving the device-session into PostgreSQL as the DevAddr is a column of the device record and thus filtering on this DevAddr would always result in the devices using that DevAddr, as well all device-sessions for a DevAddr can be retrieved by a single query. Note that to migrate the device-sessions, you must run: chirpstack -c path/to/config migrate-device-sessions-to-postgres A nice side-effect is that a PostgreSQL backup / restore will also restore the device connectivity. Closes #362 and #74.
This commit is contained in:
parent
fc30a3b7e5
commit
098f8db4c6
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -876,6 +876,7 @@ dependencies = [
|
|||||||
name = "chirpstack_api"
|
name = "chirpstack_api"
|
||||||
version = "4.7.0-test.3"
|
version = "4.7.0-test.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"diesel",
|
||||||
"hex",
|
"hex",
|
||||||
"pbjson",
|
"pbjson",
|
||||||
"pbjson-build",
|
"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";
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
message DeviceSession {
|
message DeviceSession {
|
||||||
// Device EUI.
|
|
||||||
bytes dev_eui = 1;
|
|
||||||
|
|
||||||
// Device address.
|
// Device address.
|
||||||
bytes dev_addr = 2;
|
bytes dev_addr = 2;
|
||||||
|
|
||||||
// Join EUI.
|
|
||||||
bytes join_eui = 3;
|
|
||||||
|
|
||||||
// LoRaWAN mac-version.
|
// LoRaWAN mac-version.
|
||||||
common.MacVersion mac_version = 4;
|
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"]
|
default = ["api", "json"]
|
||||||
api = ["tonic/transport", "tonic-build/transport", "tokio"]
|
api = ["tonic/transport", "tonic-build/transport", "tokio"]
|
||||||
json = ["pbjson", "pbjson-types", "serde"]
|
json = ["pbjson", "pbjson-types", "serde"]
|
||||||
|
diesel = ["dep:diesel"]
|
||||||
internal = []
|
internal = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -25,6 +26,7 @@ tokio = { version = "1.32", features = ["macros"], optional = true }
|
|||||||
pbjson = { version = "0.6", optional = true }
|
pbjson = { version = "0.6", optional = true }
|
||||||
pbjson-types = { version = "0.6", optional = true }
|
pbjson-types = { version = "0.6", optional = true }
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", optional = true }
|
||||||
|
diesel = { version = "2.1", features = ["postgres_backend"], optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = { version = "0.10", features = ["prost"], default-features = false }
|
tonic-build = { version = "0.10", features = ["prost"], default-features = false }
|
||||||
|
14
api/rust/build.rs
vendored
14
api/rust/build.rs
vendored
@ -73,13 +73,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// internal
|
// internal
|
||||||
tonic_build::configure()
|
{
|
||||||
|
let mut builder = tonic_build::configure()
|
||||||
.out_dir(out_dir.join("internal"))
|
.out_dir(out_dir.join("internal"))
|
||||||
.file_descriptor_set_path(out_dir.join("internal").join("proto_descriptor.bin"))
|
.file_descriptor_set_path(out_dir.join("internal").join("proto_descriptor.bin"))
|
||||||
.compile_well_known_types(true)
|
.compile_well_known_types(true)
|
||||||
.extern_path(".google.protobuf", well_known_types_path)
|
.extern_path(".google.protobuf", well_known_types_path)
|
||||||
.extern_path(".common", "crate::common")
|
.extern_path(".common", "crate::common");
|
||||||
.compile(
|
|
||||||
|
#[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
|
&[cs_dir
|
||||||
.join("internal")
|
.join("internal")
|
||||||
.join("internal.proto")
|
.join("internal.proto")
|
||||||
@ -90,6 +97,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
proto_dir.join("google").to_str().unwrap(),
|
proto_dir.join("google").to_str().unwrap(),
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "json")]
|
#[cfg(feature = "json")]
|
||||||
{
|
{
|
||||||
|
@ -7,15 +7,9 @@ import "gw/gw.proto";
|
|||||||
import "google/protobuf/timestamp.proto";
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
message DeviceSession {
|
message DeviceSession {
|
||||||
// Device EUI.
|
|
||||||
bytes dev_eui = 1;
|
|
||||||
|
|
||||||
// Device address.
|
// Device address.
|
||||||
bytes dev_addr = 2;
|
bytes dev_addr = 2;
|
||||||
|
|
||||||
// Join EUI.
|
|
||||||
bytes join_eui = 3;
|
|
||||||
|
|
||||||
// LoRaWAN mac-version.
|
// LoRaWAN mac-version.
|
||||||
common.MacVersion mac_version = 4;
|
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")]
|
#[cfg(feature = "json")]
|
||||||
include!(concat!(env!("OUT_DIR"), "/internal/internal.serde.rs"));
|
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 {
|
impl DeviceSession {
|
||||||
pub fn get_a_f_cnt_down(&self) -> u32 {
|
pub fn get_a_f_cnt_down(&self) -> u32 {
|
||||||
if self.mac_version().to_string().starts_with("1.0") {
|
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 }
|
], default-features = true }
|
||||||
|
|
||||||
# ChirpStack API definitions
|
# 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"] }
|
lrwn = { path = "../lrwn", features = ["serde", "diesel", "regions", "crypto"] }
|
||||||
backend = { path = "../backend" }
|
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::downlink::data_fns;
|
||||||
use crate::helpers::errors::PrintFullError;
|
use crate::helpers::errors::PrintFullError;
|
||||||
use crate::storage::{
|
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::{
|
use crate::uplink::{
|
||||||
data_sns, error::Error as UplinkError, helpers, join_sns, RoamingMetaData, UplinkFrameSet,
|
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
|
// 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 pr_lifetime = roaming::get_passive_roaming_lifetime(sender_id)?;
|
||||||
let kek_label = roaming::get_passive_roaming_kek_label(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") {
|
let nwk_s_key = if ds.mac_version().to_string().starts_with("1.0") {
|
||||||
Some(keywrap::wrap(
|
Some(keywrap::wrap(
|
||||||
@ -343,7 +344,7 @@ async fn _handle_pr_start_req_data(
|
|||||||
base: pl
|
base: pl
|
||||||
.base
|
.base
|
||||||
.to_base_payload_result(backend::ResultCode::Success, ""),
|
.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() {
|
lifetime: if pr_lifetime.is_zero() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -15,10 +15,11 @@ use lrwn::{AES128Key, DevAddr, EUI64};
|
|||||||
use super::auth::validator;
|
use super::auth::validator;
|
||||||
use super::error::ToStatus;
|
use super::error::ToStatus;
|
||||||
use super::helpers::{self, FromProto, ToProto};
|
use super::helpers::{self, FromProto, ToProto};
|
||||||
use crate::storage::error::Error;
|
|
||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
device::{self, DeviceClass},
|
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};
|
use crate::{codec, devaddr::get_random_dev_addr};
|
||||||
|
|
||||||
@ -514,7 +515,6 @@ impl DeviceService for Device {
|
|||||||
|
|
||||||
let mut ds = internal::DeviceSession {
|
let mut ds = internal::DeviceSession {
|
||||||
region_config_id: "".to_string(),
|
region_config_id: "".to_string(),
|
||||||
dev_eui: dev_eui.to_vec(),
|
|
||||||
dev_addr: dev_addr.to_vec(),
|
dev_addr: dev_addr.to_vec(),
|
||||||
mac_version: dp.mac_version.to_proto().into(),
|
mac_version: dp.mac_version.to_proto().into(),
|
||||||
s_nwk_s_int_key: s_nwk_s_int_key.to_vec(),
|
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);
|
dp.reset_session_to_boot_params(&mut ds);
|
||||||
|
|
||||||
device_session::save(&ds).await.map_err(|e| e.status())?;
|
let mut device_changeset = device::DeviceChangeset {
|
||||||
|
device_session: Some(Some(ds)),
|
||||||
// Set device DevAddr.
|
dev_addr: Some(Some(dev_addr)),
|
||||||
device::set_dev_addr(dev_eui, dev_addr)
|
secondary_dev_addr: Some(None),
|
||||||
.await
|
..Default::default()
|
||||||
.map_err(|e| e.status())?;
|
};
|
||||||
|
|
||||||
// Flush queue (if configured).
|
// Flush queue (if configured).
|
||||||
if dp.flush_queue_on_activate {
|
if dp.flush_queue_on_activate {
|
||||||
@ -548,14 +548,14 @@ 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.
|
// 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") {
|
if dp.supports_class_c && dp.mac_version.to_string().starts_with("1.0") {
|
||||||
let _ = device::set_enabled_class(&dev_eui, DeviceClass::C)
|
device_changeset.enabled_class = Some(DeviceClass::C);
|
||||||
.await
|
|
||||||
.map_err(|e| e.status())?;
|
|
||||||
} else {
|
} else {
|
||||||
let _ = device::set_enabled_class(&dev_eui, DeviceClass::A)
|
device_changeset.enabled_class = Some(DeviceClass::A);
|
||||||
|
}
|
||||||
|
|
||||||
|
device::partial_update(dev_eui, &device_changeset)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.status())?;
|
.map_err(|e| e.status())?;
|
||||||
}
|
|
||||||
|
|
||||||
let mut resp = Response::new(());
|
let mut resp = Response::new(());
|
||||||
resp.metadata_mut()
|
resp.metadata_mut()
|
||||||
@ -581,7 +581,16 @@ impl DeviceService for Device {
|
|||||||
device_queue::flush_for_dev_eui(&dev_eui)
|
device_queue::flush_for_dev_eui(&dev_eui)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.status())?;
|
.map_err(|e| e.status())?;
|
||||||
device_session::delete(&dev_eui)
|
|
||||||
|
device::partial_update(
|
||||||
|
dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
|
dev_addr: Some(None),
|
||||||
|
secondary_dev_addr: Some(None),
|
||||||
|
device_session: Some(None),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.status())?;
|
.map_err(|e| e.status())?;
|
||||||
|
|
||||||
@ -606,25 +615,24 @@ impl DeviceService for Device {
|
|||||||
)
|
)
|
||||||
.await?;
|
.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,
|
Ok(v) => v,
|
||||||
Err(e) => match e {
|
Err(StorageError::NotFound(_)) => {
|
||||||
Error::NotFound(_) => {
|
|
||||||
return Ok(Response::new(api::GetDeviceActivationResponse {
|
return Ok(Response::new(api::GetDeviceActivationResponse {
|
||||||
device_activation: None,
|
device_activation: None,
|
||||||
join_server_context: None,
|
join_server_context: None,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
_ => {
|
Err(e) => {
|
||||||
return Err(e.status());
|
return Err(e.status());
|
||||||
}
|
}
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut resp = Response::new(api::GetDeviceActivationResponse {
|
let mut resp = Response::new(api::GetDeviceActivationResponse {
|
||||||
device_activation: Some(api::DeviceActivation {
|
device_activation: Some(api::DeviceActivation {
|
||||||
dev_eui: hex::encode(&ds.dev_eui),
|
dev_eui: d.dev_eui.to_string(),
|
||||||
dev_addr: hex::encode(&ds.dev_addr),
|
dev_addr: d.get_dev_addr().map_err(|e| e.status())?.to_string(),
|
||||||
app_s_key: match &ds.app_s_key {
|
app_s_key: match &ds.app_s_key {
|
||||||
Some(v) => hex::encode(&v.aes_key),
|
Some(v) => hex::encode(&v.aes_key),
|
||||||
None => "".to_string(),
|
None => "".to_string(),
|
||||||
@ -1188,7 +1196,14 @@ impl DeviceService for Device {
|
|||||||
)
|
)
|
||||||
.await?;
|
.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)
|
let max_f_cnt_down_queue = device_queue::get_max_f_cnt_down(dev_eui)
|
||||||
.await
|
.await
|
||||||
@ -1210,7 +1225,7 @@ pub mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::api::auth::validator::RequestValidator;
|
use crate::api::auth::validator::RequestValidator;
|
||||||
use crate::api::auth::AuthID;
|
use crate::api::auth::AuthID;
|
||||||
use crate::storage::{application, tenant, user};
|
use crate::storage::{application, device, tenant, user};
|
||||||
use crate::test;
|
use crate::test;
|
||||||
use lrwn::NetID;
|
use lrwn::NetID;
|
||||||
|
|
||||||
@ -1526,12 +1541,18 @@ pub mod test {
|
|||||||
assert!(get_activation_resp.get_ref().device_activation.is_none());
|
assert!(get_activation_resp.get_ref().device_activation.is_none());
|
||||||
|
|
||||||
// test get activation with JS session-key ID.
|
// test get activation with JS session-key ID.
|
||||||
device_session::save(&internal::DeviceSession {
|
device::partial_update(
|
||||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
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],
|
dev_addr: vec![1, 2, 3, 4],
|
||||||
js_session_key_id: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
js_session_key_id: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let get_activation_req = get_request(
|
let get_activation_req = get_request(
|
||||||
@ -1550,17 +1571,23 @@ pub mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// test activation with AppSKey key-envelope.
|
// test activation with AppSKey key-envelope.
|
||||||
device_session::save(&internal::DeviceSession {
|
device::partial_update(
|
||||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
dev.dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
|
device_session: Some(Some(internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
dev_addr: vec![1, 2, 3, 4],
|
||||||
app_s_key: Some(common::KeyEnvelope {
|
app_s_key: Some(common::KeyEnvelope {
|
||||||
kek_label: "test-key".into(),
|
kek_label: "test-key".into(),
|
||||||
aes_key: vec![8, 7, 6, 5, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1],
|
aes_key: vec![8, 7, 6, 5, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1],
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let get_activation_req = get_request(
|
let get_activation_req = get_request(
|
||||||
&u.id,
|
&u.id,
|
||||||
api::GetDeviceActivationRequest {
|
api::GetDeviceActivationRequest {
|
||||||
|
@ -185,12 +185,6 @@ pub fn run() {
|
|||||||
{{/each}}
|
{{/each}}
|
||||||
]
|
]
|
||||||
|
|
||||||
# Device session expiration.
|
|
||||||
#
|
|
||||||
# The TTL value defines the time after which a device-session expires
|
|
||||||
# after no activity.
|
|
||||||
device_session_ttl="{{ network.device_session_ttl }}"
|
|
||||||
|
|
||||||
# Time to wait for uplink de-duplication.
|
# Time to wait for uplink de-duplication.
|
||||||
#
|
#
|
||||||
# This is the time that ChirpStack will wait for other gateways to receive
|
# This is the time that ChirpStack will wait for other gateways to receive
|
||||||
|
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 configfile;
|
||||||
pub mod create_api_key;
|
pub mod create_api_key;
|
||||||
pub mod import_legacy_lorawan_devices_repository;
|
pub mod import_legacy_lorawan_devices_repository;
|
||||||
|
pub mod migrate_ds_to_pg;
|
||||||
pub mod print_ds;
|
pub mod print_ds;
|
||||||
pub mod root;
|
pub mod root;
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
use crate::storage;
|
use crate::storage;
|
||||||
use crate::storage::device_session;
|
use crate::storage::device;
|
||||||
use lrwn::EUI64;
|
use lrwn::EUI64;
|
||||||
|
|
||||||
pub async fn run(dev_eui: &EUI64) -> Result<()> {
|
pub async fn run(dev_eui: &EUI64) -> Result<()> {
|
||||||
storage::setup().await.context("Setup storage")?;
|
storage::setup().await.context("Setup storage")?;
|
||||||
let ds = device_session::get(dev_eui)
|
|
||||||
.await
|
let d = device::get(dev_eui).await.context("Get device")?;
|
||||||
.context("Get device-session")?;
|
let ds = d.get_device_session()?;
|
||||||
let json = serde_json::to_string_pretty(&ds)?;
|
let json = serde_json::to_string_pretty(&ds)?;
|
||||||
println!("{}", json);
|
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>,
|
relay_context: Option<&'a RelayContext>,
|
||||||
tenant: &'a tenant::Tenant,
|
tenant: &'a tenant::Tenant,
|
||||||
device: &'a device::Device,
|
device: &'a device::Device,
|
||||||
device_session: &'a internal::DeviceSession,
|
|
||||||
join_accept: &'a PhyPayload,
|
join_accept: &'a PhyPayload,
|
||||||
network_conf: config::RegionNetwork,
|
network_conf: config::RegionNetwork,
|
||||||
region_conf: Arc<Box<dyn lrwn::region::Region + Sync + Send>>,
|
region_conf: Arc<Box<dyn lrwn::region::Region + Sync + Send>>,
|
||||||
@ -36,20 +35,12 @@ impl JoinAccept<'_> {
|
|||||||
ufs: &UplinkFrameSet,
|
ufs: &UplinkFrameSet,
|
||||||
tenant: &tenant::Tenant,
|
tenant: &tenant::Tenant,
|
||||||
device: &device::Device,
|
device: &device::Device,
|
||||||
device_session: &internal::DeviceSession,
|
|
||||||
join_accept: &PhyPayload,
|
join_accept: &PhyPayload,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let downlink_id: u32 = rand::thread_rng().gen();
|
let downlink_id: u32 = rand::thread_rng().gen();
|
||||||
let span = span!(Level::INFO, "join_accept", downlink_id = downlink_id);
|
let span = span!(Level::INFO, "join_accept", downlink_id = downlink_id);
|
||||||
|
|
||||||
let fut = JoinAccept::_handle(
|
let fut = JoinAccept::_handle(downlink_id, ufs, tenant, device, join_accept);
|
||||||
downlink_id,
|
|
||||||
ufs,
|
|
||||||
tenant,
|
|
||||||
device,
|
|
||||||
device_session,
|
|
||||||
join_accept,
|
|
||||||
);
|
|
||||||
fut.instrument(span).await
|
fut.instrument(span).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +49,6 @@ impl JoinAccept<'_> {
|
|||||||
ufs: &UplinkFrameSet,
|
ufs: &UplinkFrameSet,
|
||||||
tenant: &tenant::Tenant,
|
tenant: &tenant::Tenant,
|
||||||
device: &device::Device,
|
device: &device::Device,
|
||||||
device_session: &internal::DeviceSession,
|
|
||||||
join_accept: &PhyPayload,
|
join_accept: &PhyPayload,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let downlink_id: u32 = rand::thread_rng().gen();
|
let downlink_id: u32 = rand::thread_rng().gen();
|
||||||
@ -68,15 +58,8 @@ impl JoinAccept<'_> {
|
|||||||
downlink_id = downlink_id
|
downlink_id = downlink_id
|
||||||
);
|
);
|
||||||
|
|
||||||
let fut = JoinAccept::_handle_relayed(
|
let fut =
|
||||||
downlink_id,
|
JoinAccept::_handle_relayed(downlink_id, relay_ctx, ufs, tenant, device, join_accept);
|
||||||
relay_ctx,
|
|
||||||
ufs,
|
|
||||||
tenant,
|
|
||||||
device,
|
|
||||||
device_session,
|
|
||||||
join_accept,
|
|
||||||
);
|
|
||||||
fut.instrument(span).await
|
fut.instrument(span).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +68,6 @@ impl JoinAccept<'_> {
|
|||||||
ufs: &UplinkFrameSet,
|
ufs: &UplinkFrameSet,
|
||||||
tenant: &tenant::Tenant,
|
tenant: &tenant::Tenant,
|
||||||
device: &device::Device,
|
device: &device::Device,
|
||||||
device_session: &internal::DeviceSession,
|
|
||||||
join_accept: &PhyPayload,
|
join_accept: &PhyPayload,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut ctx = JoinAccept {
|
let mut ctx = JoinAccept {
|
||||||
@ -93,7 +75,6 @@ impl JoinAccept<'_> {
|
|||||||
relay_context: None,
|
relay_context: None,
|
||||||
tenant,
|
tenant,
|
||||||
device,
|
device,
|
||||||
device_session,
|
|
||||||
join_accept,
|
join_accept,
|
||||||
network_conf: config::get_region_network(&ufs.region_config_id)?,
|
network_conf: config::get_region_network(&ufs.region_config_id)?,
|
||||||
region_conf: region::get(&ufs.region_config_id)?,
|
region_conf: region::get(&ufs.region_config_id)?,
|
||||||
@ -122,7 +103,6 @@ impl JoinAccept<'_> {
|
|||||||
ufs: &UplinkFrameSet,
|
ufs: &UplinkFrameSet,
|
||||||
tenant: &tenant::Tenant,
|
tenant: &tenant::Tenant,
|
||||||
device: &device::Device,
|
device: &device::Device,
|
||||||
device_session: &internal::DeviceSession,
|
|
||||||
join_accept: &PhyPayload,
|
join_accept: &PhyPayload,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut ctx = JoinAccept {
|
let mut ctx = JoinAccept {
|
||||||
@ -130,7 +110,6 @@ impl JoinAccept<'_> {
|
|||||||
relay_context: Some(relay_ctx),
|
relay_context: Some(relay_ctx),
|
||||||
tenant,
|
tenant,
|
||||||
device,
|
device,
|
||||||
device_session,
|
|
||||||
join_accept,
|
join_accept,
|
||||||
network_conf: config::get_region_network(&ufs.region_config_id)?,
|
network_conf: config::get_region_network(&ufs.region_config_id)?,
|
||||||
region_conf: region::get(&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 gw_down = self.downlink_gateway.as_ref().unwrap();
|
||||||
let relay_ctx = self.relay_context.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 {
|
let mut tx_info = chirpstack_api::gw::DownlinkTxInfo {
|
||||||
board: gw_down.board,
|
board: gw_down.board,
|
||||||
@ -311,7 +291,7 @@ impl JoinAccept<'_> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Get RX1 DR offset.
|
// 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.
|
// get RX1 DR.
|
||||||
let rx1_dr_index = self
|
let rx1_dr_index = self
|
||||||
@ -337,8 +317,8 @@ impl JoinAccept<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set timestamp.
|
// Set timestamp.
|
||||||
let delay = if relay_ctx.device_session.rx1_delay > 0 {
|
let delay = if relay_ds.rx1_delay > 0 {
|
||||||
Duration::from_secs(relay_ctx.device_session.rx1_delay as u64)
|
Duration::from_secs(relay_ds.rx1_delay as u64)
|
||||||
} else {
|
} else {
|
||||||
self.region_conf.get_defaults().rx1_delay
|
self.region_conf.get_defaults().rx1_delay
|
||||||
};
|
};
|
||||||
@ -415,9 +395,10 @@ impl JoinAccept<'_> {
|
|||||||
|
|
||||||
let gw_down = self.downlink_gateway.as_ref().unwrap();
|
let gw_down = self.downlink_gateway.as_ref().unwrap();
|
||||||
let relay_ctx = self.relay_context.unwrap();
|
let relay_ctx = self.relay_context.unwrap();
|
||||||
|
let relay_ds = relay_ctx.device.get_device_session()?;
|
||||||
|
|
||||||
// Get frequency.
|
// Get frequency.
|
||||||
let frequency = relay_ctx.device_session.rx2_frequency;
|
let frequency = relay_ds.rx2_frequency;
|
||||||
|
|
||||||
let mut tx_info = chirpstack_api::gw::DownlinkTxInfo {
|
let mut tx_info = chirpstack_api::gw::DownlinkTxInfo {
|
||||||
board: gw_down.board,
|
board: gw_down.board,
|
||||||
@ -428,7 +409,7 @@ impl JoinAccept<'_> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// get RX2 DR
|
// 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)?;
|
let rx2_dr = self.region_conf.get_data_rate(rx2_dr_index)?;
|
||||||
|
|
||||||
// set DR to tx_info
|
// set DR to tx_info
|
||||||
@ -444,8 +425,8 @@ impl JoinAccept<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set timestamp.
|
// Set timestamp.
|
||||||
let delay = if relay_ctx.device_session.rx1_delay > 0 {
|
let delay = if relay_ds.rx1_delay > 0 {
|
||||||
Duration::from_secs(relay_ctx.device_session.rx1_delay as u64 + 1)
|
Duration::from_secs(relay_ds.rx1_delay as u64 + 1)
|
||||||
} else {
|
} else {
|
||||||
self.region_conf.get_defaults().rx2_delay
|
self.region_conf.get_defaults().rx2_delay
|
||||||
};
|
};
|
||||||
@ -481,6 +462,7 @@ impl JoinAccept<'_> {
|
|||||||
trace!("Setting ForwardDownlinkReq frame");
|
trace!("Setting ForwardDownlinkReq frame");
|
||||||
|
|
||||||
let relay_ctx = self.relay_context.as_ref().unwrap();
|
let relay_ctx = self.relay_context.as_ref().unwrap();
|
||||||
|
let relay_ds = relay_ctx.device.get_device_session()?;
|
||||||
|
|
||||||
let mut relay_phy = lrwn::PhyPayload {
|
let mut relay_phy = lrwn::PhyPayload {
|
||||||
mhdr: lrwn::MHDR {
|
mhdr: lrwn::MHDR {
|
||||||
@ -489,16 +471,11 @@ impl JoinAccept<'_> {
|
|||||||
},
|
},
|
||||||
payload: lrwn::Payload::MACPayload(lrwn::MACPayload {
|
payload: lrwn::Payload::MACPayload(lrwn::MACPayload {
|
||||||
fhdr: lrwn::FHDR {
|
fhdr: lrwn::FHDR {
|
||||||
devaddr: lrwn::DevAddr::from_slice(&relay_ctx.device_session.dev_addr)?,
|
devaddr: relay_ctx.device.get_dev_addr()?,
|
||||||
f_cnt: if relay_ctx
|
f_cnt: if relay_ds.mac_version().to_string().starts_with("1.0") {
|
||||||
.device_session
|
relay_ds.n_f_cnt_down
|
||||||
.mac_version()
|
|
||||||
.to_string()
|
|
||||||
.starts_with("1.0")
|
|
||||||
{
|
|
||||||
relay_ctx.device_session.n_f_cnt_down
|
|
||||||
} else {
|
} else {
|
||||||
relay_ctx.device_session.a_f_cnt_down
|
relay_ds.a_f_cnt_down
|
||||||
},
|
},
|
||||||
f_ctrl: lrwn::FCtrl {
|
f_ctrl: lrwn::FCtrl {
|
||||||
adr: !self.network_conf.adr_disabled,
|
adr: !self.network_conf.adr_disabled,
|
||||||
@ -517,17 +494,15 @@ impl JoinAccept<'_> {
|
|||||||
mic: None,
|
mic: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
relay_phy.encrypt_frm_payload(&lrwn::AES128Key::from_slice(
|
relay_phy.encrypt_frm_payload(&lrwn::AES128Key::from_slice(&relay_ds.nwk_s_enc_key)?)?;
|
||||||
&relay_ctx.device_session.nwk_s_enc_key,
|
|
||||||
)?)?;
|
|
||||||
|
|
||||||
// Set MIC.
|
// Set MIC.
|
||||||
// If this is an ACK, then FCntUp has already been incremented by one. If
|
// 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.
|
// this is not an ACK, then DownlinkDataMIC will zero out ConfFCnt.
|
||||||
relay_phy.set_downlink_data_mic(
|
relay_phy.set_downlink_data_mic(
|
||||||
relay_ctx.device_session.mac_version().from_proto(),
|
relay_ds.mac_version().from_proto(),
|
||||||
relay_ctx.device_session.f_cnt_up - 1,
|
relay_ds.f_cnt_up - 1,
|
||||||
&lrwn::AES128Key::from_slice(&relay_ctx.device_session.s_nwk_s_int_key)?,
|
&lrwn::AES128Key::from_slice(&relay_ds.s_nwk_s_int_key)?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let relay_phy_b = relay_phy.to_vec()?;
|
let relay_phy_b = relay_phy.to_vec()?;
|
||||||
@ -551,12 +526,13 @@ impl JoinAccept<'_> {
|
|||||||
|
|
||||||
async fn save_downlink_frame(&self) -> Result<()> {
|
async fn save_downlink_frame(&self) -> Result<()> {
|
||||||
trace!("Saving downlink frame");
|
trace!("Saving downlink frame");
|
||||||
|
let ds = self.device.get_device_session()?;
|
||||||
|
|
||||||
let df = chirpstack_api::internal::DownlinkFrame {
|
let df = chirpstack_api::internal::DownlinkFrame {
|
||||||
dev_eui: self.device.dev_eui.to_be_bytes().to_vec(),
|
dev_eui: self.device.dev_eui.to_be_bytes().to_vec(),
|
||||||
downlink_id: self.downlink_frame.downlink_id,
|
downlink_id: self.downlink_frame.downlink_id,
|
||||||
downlink_frame: Some(self.downlink_frame.clone()),
|
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()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -571,14 +547,15 @@ impl JoinAccept<'_> {
|
|||||||
trace!("Saving ForwardDownlinkReq frame");
|
trace!("Saving ForwardDownlinkReq frame");
|
||||||
|
|
||||||
let relay_ctx = self.relay_context.as_ref().unwrap();
|
let relay_ctx = self.relay_context.as_ref().unwrap();
|
||||||
|
let relay_ds = relay_ctx.device.get_device_session()?;
|
||||||
|
|
||||||
let df = chirpstack_api::internal::DownlinkFrame {
|
let df = chirpstack_api::internal::DownlinkFrame {
|
||||||
dev_eui: relay_ctx.device.dev_eui.to_be_bytes().to_vec(),
|
dev_eui: relay_ctx.device.dev_eui.to_be_bytes().to_vec(),
|
||||||
downlink_id: self.downlink_frame.downlink_id,
|
downlink_id: self.downlink_frame.downlink_id,
|
||||||
downlink_frame: Some(self.downlink_frame.clone()),
|
downlink_frame: Some(self.downlink_frame.clone()),
|
||||||
nwk_s_enc_key: relay_ctx.device_session.nwk_s_enc_key.clone(),
|
nwk_s_enc_key: relay_ds.nwk_s_enc_key.clone(),
|
||||||
a_f_cnt_down: relay_ctx.device_session.get_a_f_cnt_down(),
|
a_f_cnt_down: relay_ds.get_a_f_cnt_down(),
|
||||||
n_f_cnt_down: relay_ctx.device_session.n_f_cnt_down,
|
n_f_cnt_down: relay_ds.n_f_cnt_down,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,7 +9,9 @@ use crate::api::helpers::ToProto;
|
|||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
application,
|
application,
|
||||||
device::{self, DeviceClass},
|
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 crate::{integration, stream};
|
||||||
use chirpstack_api::{common, gw, integration as integration_pb, internal, stream as stream_pb};
|
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>,
|
downlink_frame_item: Option<gw::DownlinkFrameItem>,
|
||||||
phy_payload: Option<PhyPayload>,
|
phy_payload: Option<PhyPayload>,
|
||||||
phy_payload_relayed: Option<PhyPayload>,
|
phy_payload_relayed: Option<PhyPayload>,
|
||||||
device_session: Option<internal::DeviceSession>,
|
|
||||||
device_session_relayed: Option<internal::DeviceSession>,
|
|
||||||
tenant: Option<tenant::Tenant>,
|
tenant: Option<tenant::Tenant>,
|
||||||
tenant_relayed: Option<tenant::Tenant>,
|
tenant_relayed: Option<tenant::Tenant>,
|
||||||
application: Option<application::Application>,
|
application: Option<application::Application>,
|
||||||
@ -66,8 +66,6 @@ impl TxAck {
|
|||||||
downlink_frame_item: None,
|
downlink_frame_item: None,
|
||||||
phy_payload: None,
|
phy_payload: None,
|
||||||
phy_payload_relayed: None,
|
phy_payload_relayed: None,
|
||||||
device_session: None,
|
|
||||||
device_session_relayed: None,
|
|
||||||
tenant: None,
|
tenant: None,
|
||||||
tenant_relayed: None,
|
tenant_relayed: None,
|
||||||
application: None,
|
application: None,
|
||||||
@ -88,10 +86,7 @@ impl TxAck {
|
|||||||
|
|
||||||
if ctx.is_error() {
|
if ctx.is_error() {
|
||||||
if ctx.is_application_payload() || ctx.is_mac_only_downlink() {
|
if ctx.is_application_payload() || ctx.is_mac_only_downlink() {
|
||||||
ctx.get_device().await?;
|
ctx.get_device_data().await?;
|
||||||
ctx.get_device_profile().await?;
|
|
||||||
ctx.get_application().await?;
|
|
||||||
ctx.get_tenant().await?;
|
|
||||||
ctx.log_tx_ack_error().await?;
|
ctx.log_tx_ack_error().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,12 +94,10 @@ impl TxAck {
|
|||||||
ctx.delete_multicast_group_queue_item().await?;
|
ctx.delete_multicast_group_queue_item().await?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if ctx.is_application_payload() || ctx.is_mac_only_downlink() {
|
||||||
|
ctx.get_device_data().await?;
|
||||||
|
|
||||||
if ctx.is_application_payload() {
|
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?;
|
ctx.get_device_queue_item().await?;
|
||||||
if ctx.is_unconfirmed_downlink() {
|
if ctx.is_unconfirmed_downlink() {
|
||||||
ctx.delete_device_queue_item().await?;
|
ctx.delete_device_queue_item().await?;
|
||||||
@ -116,13 +109,13 @@ impl TxAck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.increment_a_f_cnt_down()?;
|
ctx.increment_a_f_cnt_down()?;
|
||||||
ctx.save_device_session().await?;
|
|
||||||
ctx.send_tx_ack_event().await?;
|
ctx.send_tx_ack_event().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.is_mac_only_downlink() {
|
if ctx.is_mac_only_downlink() {
|
||||||
ctx.get_device_session().await?;
|
|
||||||
ctx.increment_n_f_cnt_down()?;
|
ctx.increment_n_f_cnt_down()?;
|
||||||
|
}
|
||||||
|
|
||||||
ctx.save_device_session().await?;
|
ctx.save_device_session().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,24 +133,22 @@ impl TxAck {
|
|||||||
|
|
||||||
async fn _handle_relayed(&mut self) -> Result<()> {
|
async fn _handle_relayed(&mut self) -> Result<()> {
|
||||||
self.get_phy_payload_relayed()?;
|
self.get_phy_payload_relayed()?;
|
||||||
|
self.get_device_data().await?; // the device-data of the relay
|
||||||
|
|
||||||
if self.is_error() {
|
if self.is_error() {
|
||||||
// We log the tx ack error under the relay as this is the device to which the downlink
|
// We log the tx ack error under the relay as this is the device to which the downlink
|
||||||
// is sent.
|
// 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?;
|
self.log_tx_ack_error().await?;
|
||||||
} else {
|
} else {
|
||||||
// First handle the relay frame-counter increment.
|
// First handle the relay frame-counter increment.
|
||||||
self.get_device_session().await?;
|
|
||||||
self.increment_a_f_cnt_down()?;
|
self.increment_a_f_cnt_down()?;
|
||||||
self.save_device_session().await?;
|
self.save_device_session().await?;
|
||||||
|
|
||||||
|
// Get data of relayed device.
|
||||||
|
self.get_device_data_relayed().await?;
|
||||||
|
|
||||||
// Handle end-device frame-counter increment + queue item.
|
// Handle end-device frame-counter increment + queue item.
|
||||||
if self.is_application_payload_relayed() {
|
if self.is_application_payload_relayed() {
|
||||||
self.get_device_session_relayed().await?;
|
|
||||||
self.get_device_queue_item().await?;
|
self.get_device_queue_item().await?;
|
||||||
if self.is_unconfirmed_downlink_relayed() {
|
if self.is_unconfirmed_downlink_relayed() {
|
||||||
self.delete_device_queue_item().await?;
|
self.delete_device_queue_item().await?;
|
||||||
@ -172,13 +163,9 @@ impl TxAck {
|
|||||||
self.save_device_session_relayed().await?;
|
self.save_device_session_relayed().await?;
|
||||||
|
|
||||||
// Log tx ack event.
|
// Log tx ack event.
|
||||||
self.get_device_relayed().await?;
|
self.get_device_data_relayed().await?;
|
||||||
self.get_device_profile_relayed().await?;
|
|
||||||
self.get_application_relayed().await?;
|
|
||||||
self.get_tenant_relayed().await?;
|
|
||||||
self.send_tx_ack_event_relayed().await?;
|
self.send_tx_ack_event_relayed().await?;
|
||||||
} else if self.is_mac_only_downlink_relayed() {
|
} else if self.is_mac_only_downlink_relayed() {
|
||||||
self.get_device_session_relayed().await?;
|
|
||||||
self.increment_n_f_cnt_down_relayed()?;
|
self.increment_n_f_cnt_down_relayed()?;
|
||||||
self.save_device_session_relayed().await?;
|
self.save_device_session_relayed().await?;
|
||||||
}
|
}
|
||||||
@ -225,75 +212,29 @@ impl TxAck {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_device_session(&mut self) -> Result<()> {
|
async fn get_device_data(&mut self) -> Result<()> {
|
||||||
trace!("Getting device-session");
|
trace!("Getting device data");
|
||||||
let dev_eui = EUI64::from_slice(&self.downlink_frame.as_ref().unwrap().dev_eui)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_device_session_relayed(&mut self) -> Result<()> {
|
async fn get_device_data_relayed(&mut self) -> Result<()> {
|
||||||
trace!("Getting relayed device-session");
|
trace!("Getting relayed device data");
|
||||||
let dev_eui = EUI64::from_slice(&self.downlink_frame.as_ref().unwrap().dev_eui_relayed)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,7 +294,8 @@ impl TxAck {
|
|||||||
fn set_device_session_conf_f_cnt(&mut self) -> Result<()> {
|
fn set_device_session_conf_f_cnt(&mut self) -> Result<()> {
|
||||||
trace!("Setting device-session conf_f_cnt");
|
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();
|
let qi = self.device_queue_item.as_ref().unwrap();
|
||||||
|
|
||||||
ds.conf_f_cnt = match qi.f_cnt_down {
|
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<()> {
|
fn set_device_session_conf_f_cnt_relayed(&mut self) -> Result<()> {
|
||||||
trace!("Setting relayed device-session conf_f_cnt");
|
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();
|
let qi = self.device_queue_item.as_ref().unwrap();
|
||||||
|
|
||||||
ds.conf_f_cnt = match qi.f_cnt_down {
|
ds.conf_f_cnt = match qi.f_cnt_down {
|
||||||
@ -387,7 +330,8 @@ impl TxAck {
|
|||||||
fn increment_a_f_cnt_down(&mut self) -> Result<()> {
|
fn increment_a_f_cnt_down(&mut self) -> Result<()> {
|
||||||
trace!("Incrementing a_f_cnt_down");
|
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);
|
ds.set_a_f_cnt_down(self.downlink_frame.as_ref().unwrap().a_f_cnt_down + 1);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -396,7 +340,8 @@ impl TxAck {
|
|||||||
fn increment_a_f_cnt_down_relayed(&mut self) -> Result<()> {
|
fn increment_a_f_cnt_down_relayed(&mut self) -> Result<()> {
|
||||||
trace!("Incrementing relayed a_f_cnt_down");
|
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);
|
ds.set_a_f_cnt_down(ds.get_a_f_cnt_down() + 1);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -405,7 +350,8 @@ impl TxAck {
|
|||||||
fn increment_n_f_cnt_down(&mut self) -> Result<()> {
|
fn increment_n_f_cnt_down(&mut self) -> Result<()> {
|
||||||
trace!("Incrementing n_f_cnt_down");
|
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;
|
ds.n_f_cnt_down += 1;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -414,7 +360,8 @@ impl TxAck {
|
|||||||
fn increment_n_f_cnt_down_relayed(&mut self) -> Result<()> {
|
fn increment_n_f_cnt_down_relayed(&mut self) -> Result<()> {
|
||||||
trace!("Incrementing relayed n_f_cnt_down");
|
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;
|
ds.n_f_cnt_down += 1;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -422,13 +369,35 @@ impl TxAck {
|
|||||||
|
|
||||||
async fn save_device_session(&self) -> Result<()> {
|
async fn save_device_session(&self) -> Result<()> {
|
||||||
trace!("Saving device-session");
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save_device_session_relayed(&self) -> Result<()> {
|
async fn save_device_session_relayed(&self) -> Result<()> {
|
||||||
trace!("Saving relayed device-session");
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,11 +5,13 @@ use crate::storage::device;
|
|||||||
use chirpstack_api::internal;
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
_block: &lrwn::MACCommandSet,
|
_block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let dev_eui = dev.dev_eui;
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Expected pending ConfigureFwdLimitReq mac-command"));
|
return Err(anyhow!("Expected pending ConfigureFwdLimitReq mac-command"));
|
||||||
}
|
}
|
||||||
@ -27,7 +29,7 @@ pub fn handle(
|
|||||||
ds.relay = Some(internal::Relay::default());
|
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 {
|
if let Some(relay) = &mut ds.relay {
|
||||||
relay.join_req_limit_reload_rate = req_pl.reload_rate.join_req_reload_rate as u32;
|
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 {
|
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(
|
let resp = handle(
|
||||||
&device::Device::default(),
|
&mut dev,
|
||||||
&mut ds,
|
|
||||||
&tst.configure_fwd_limit_ans,
|
&tst.configure_fwd_limit_ans,
|
||||||
tst.configure_fwd_limit_req.as_ref(),
|
tst.configure_fwd_limit_req.as_ref(),
|
||||||
);
|
);
|
||||||
@ -130,7 +134,12 @@ mod test {
|
|||||||
assert_eq!(true, resp.unwrap().is_none());
|
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 anyhow::Result;
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
use crate::storage::{device, device_session};
|
use crate::storage::device;
|
||||||
use chirpstack_api::internal;
|
|
||||||
use lrwn::EUI64;
|
use lrwn::EUI64;
|
||||||
|
|
||||||
pub async fn handle(
|
pub async fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
block: &lrwn::MACCommandSet,
|
block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let dev_eui = dev.dev_eui;
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Expected pending CtrlUplinkListReq mac-command"));
|
return Err(anyhow!("Expected pending CtrlUplinkListReq mac-command"));
|
||||||
}
|
}
|
||||||
@ -49,7 +50,7 @@ pub async fn handle(
|
|||||||
if ans_pl.uplink_list_idx_ack {
|
if ans_pl.uplink_list_idx_ack {
|
||||||
if let Some(relay) = &mut ds.relay {
|
if let Some(relay) = &mut ds.relay {
|
||||||
info!(
|
info!(
|
||||||
dev_eui = %dev.dev_eui,
|
dev_eui = %dev_eui,
|
||||||
uplink_list_idx = req_pl.ctrl_uplink_action.uplink_list_idx,
|
uplink_list_idx = req_pl.ctrl_uplink_action.uplink_list_idx,
|
||||||
ctrl_uplink_action = action,
|
ctrl_uplink_action = action,
|
||||||
w_f_cnt = ans_pl.w_fcnt,
|
w_f_cnt = ans_pl.w_fcnt,
|
||||||
@ -59,12 +60,20 @@ pub async fn handle(
|
|||||||
if action == 0 {
|
if action == 0 {
|
||||||
for rd in &relay.devices {
|
for rd in &relay.devices {
|
||||||
if req_pl.ctrl_uplink_action.uplink_list_idx as u32 == rd.index {
|
if req_pl.ctrl_uplink_action.uplink_list_idx as u32 == rd.index {
|
||||||
let mut ds =
|
let dev_eui = EUI64::from_slice(&rd.dev_eui)?;
|
||||||
device_session::get(&EUI64::from_slice(&rd.dev_eui)?).await?;
|
let mut d = device::get(&dev_eui).await?;
|
||||||
|
let ds = d.get_device_session_mut()?;
|
||||||
if let Some(relay) = &mut ds.relay {
|
if let Some(relay) = &mut ds.relay {
|
||||||
relay.w_f_cnt = ans_pl.w_fcnt;
|
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 {
|
} else if action == 1 {
|
||||||
@ -75,7 +84,7 @@ pub async fn handle(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
dev_eui = %dev.dev_eui,
|
dev_eui = %dev_eui,
|
||||||
uplink_list_idx = req_pl.ctrl_uplink_action.uplink_list_idx,
|
uplink_list_idx = req_pl.ctrl_uplink_action.uplink_list_idx,
|
||||||
"CtrlUplinkListReq not acknowledged",
|
"CtrlUplinkListReq not acknowledged",
|
||||||
);
|
);
|
||||||
@ -88,7 +97,9 @@ pub async fn handle(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::storage;
|
||||||
use crate::test;
|
use crate::test;
|
||||||
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
@ -104,6 +115,39 @@ mod test {
|
|||||||
async fn test_response() {
|
async fn test_response() {
|
||||||
let _handle = test::prepare().await;
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "acked, nothing pending".into(),
|
name: "acked, nothing pending".into(),
|
||||||
@ -120,7 +164,6 @@ mod test {
|
|||||||
},
|
},
|
||||||
device_session_ed: internal::DeviceSession {
|
device_session_ed: internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
dev_addr: vec![1, 2, 3, 4],
|
||||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
relay: Some(internal::Relay {
|
relay: Some(internal::Relay {
|
||||||
w_f_cnt: 1,
|
w_f_cnt: 1,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -136,7 +179,6 @@ mod test {
|
|||||||
]),
|
]),
|
||||||
expected_device_session_ed: internal::DeviceSession {
|
expected_device_session_ed: internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
dev_addr: vec![1, 2, 3, 4],
|
||||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
relay: Some(internal::Relay {
|
relay: Some(internal::Relay {
|
||||||
w_f_cnt: 1,
|
w_f_cnt: 1,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -160,7 +202,6 @@ mod test {
|
|||||||
},
|
},
|
||||||
device_session_ed: internal::DeviceSession {
|
device_session_ed: internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
dev_addr: vec![1, 2, 3, 4],
|
||||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
relay: Some(internal::Relay {
|
relay: Some(internal::Relay {
|
||||||
w_f_cnt: 1,
|
w_f_cnt: 1,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -183,7 +224,6 @@ mod test {
|
|||||||
]),
|
]),
|
||||||
expected_device_session_ed: internal::DeviceSession {
|
expected_device_session_ed: internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
dev_addr: vec![1, 2, 3, 4],
|
||||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
relay: Some(internal::Relay {
|
relay: Some(internal::Relay {
|
||||||
w_f_cnt: 10,
|
w_f_cnt: 10,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -207,7 +247,6 @@ mod test {
|
|||||||
},
|
},
|
||||||
device_session_ed: internal::DeviceSession {
|
device_session_ed: internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
dev_addr: vec![1, 2, 3, 4],
|
||||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
ctrl_uplink_list_req: Some(lrwn::MACCommandSet::new(vec![
|
ctrl_uplink_list_req: Some(lrwn::MACCommandSet::new(vec![
|
||||||
@ -226,7 +265,6 @@ mod test {
|
|||||||
]),
|
]),
|
||||||
expected_device_session_ed: internal::DeviceSession {
|
expected_device_session_ed: internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
dev_addr: vec![1, 2, 3, 4],
|
||||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
expected_error: None,
|
expected_error: None,
|
||||||
@ -236,12 +274,23 @@ mod test {
|
|||||||
for tst in &tests {
|
for tst in &tests {
|
||||||
println!("> {}", tst.name);
|
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(
|
let resp = handle(
|
||||||
&device::Device::default(),
|
&mut relay_dev,
|
||||||
&mut ds,
|
|
||||||
&tst.ctrl_uplink_list_ans,
|
&tst.ctrl_uplink_list_ans,
|
||||||
tst.ctrl_uplink_list_req.as_ref(),
|
tst.ctrl_uplink_list_req.as_ref(),
|
||||||
)
|
)
|
||||||
@ -254,11 +303,9 @@ mod test {
|
|||||||
assert_eq!(true, resp.unwrap().is_none());
|
assert_eq!(true, resp.unwrap().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
let ds =
|
let d = device::get(&dev.dev_eui).await.unwrap();
|
||||||
device_session::get(&EUI64::from_slice(&tst.device_session_ed.dev_eui).unwrap())
|
let ds = d.get_device_session().unwrap();
|
||||||
.await
|
assert_eq!(&tst.expected_device_session_ed, ds);
|
||||||
.unwrap();
|
|
||||||
assert_eq!(tst.expected_device_session_ed, ds);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,15 +23,18 @@ pub async fn handle(
|
|||||||
if let lrwn::MACCommand::DevStatusAns(pl) = mac {
|
if let lrwn::MACCommand::DevStatusAns(pl) = mac {
|
||||||
info!(dev_eui = %dev.dev_eui, battery = pl.battery, margin = pl.margin, "DevStatusAns received");
|
info!(dev_eui = %dev.dev_eui, battery = pl.battery, margin = pl.margin, "DevStatusAns received");
|
||||||
|
|
||||||
device::set_status(
|
device::partial_update(
|
||||||
&dev.dev_eui,
|
dev.dev_eui,
|
||||||
pl.margin as i32,
|
&device::DeviceChangeset {
|
||||||
pl.battery == 0,
|
margin: Some(pl.margin as i32),
|
||||||
if pl.battery > 0 && pl.battery < 255 {
|
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()?;
|
let v: BigDecimal = ((pl.battery as f32) / 254.0 * 100.0).try_into()?;
|
||||||
Some(v.with_scale(2))
|
Some(v.with_scale(2))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -10,11 +10,14 @@ pub async fn handle(
|
|||||||
.first()
|
.first()
|
||||||
.ok_or_else(|| anyhow!("Expected DeviceModeInd"))?;
|
.ok_or_else(|| anyhow!("Expected DeviceModeInd"))?;
|
||||||
if let lrwn::MACCommand::DeviceModeInd(pl) = mac {
|
if let lrwn::MACCommand::DeviceModeInd(pl) = mac {
|
||||||
device::set_enabled_class(
|
device::partial_update(
|
||||||
&dev.dev_eui,
|
dev.dev_eui,
|
||||||
match pl.class {
|
&device::DeviceChangeset {
|
||||||
|
enabled_class: Some(match pl.class {
|
||||||
lrwn::DeviceModeClass::ClassA => DeviceClass::A,
|
lrwn::DeviceModeClass::ClassA => DeviceClass::A,
|
||||||
lrwn::DeviceModeClass::ClassC => DeviceClass::C,
|
lrwn::DeviceModeClass::ClassC => DeviceClass::C,
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -5,11 +5,13 @@ use crate::storage::device;
|
|||||||
use chirpstack_api::internal;
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
block: &lrwn::MACCommandSet,
|
block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let dev_eui = dev.dev_eui;
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Expected pending EndDeviceConfReq mac-command"));
|
return Err(anyhow!("Expected pending EndDeviceConfReq mac-command"));
|
||||||
}
|
}
|
||||||
@ -41,7 +43,7 @@ pub fn handle(
|
|||||||
&& ans_pl.second_ch_idx_ack
|
&& ans_pl.second_ch_idx_ack
|
||||||
&& ans_pl.backoff_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 {
|
if let Some(relay) = &mut ds.relay {
|
||||||
relay.ed_activation_mode =
|
relay.ed_activation_mode =
|
||||||
@ -57,7 +59,7 @@ pub fn handle(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
dev_eui = %dev.dev_eui,
|
dev_eui = %dev_eui,
|
||||||
second_ch_freq_ack = ans_pl.second_ch_freq_ack,
|
second_ch_freq_ack = ans_pl.second_ch_freq_ack,
|
||||||
second_ch_dr_ack = ans_pl.second_ch_dr_ack,
|
second_ch_dr_ack = ans_pl.second_ch_dr_ack,
|
||||||
second_ch_idx_ack = ans_pl.second_ch_idx_ack,
|
second_ch_idx_ack = ans_pl.second_ch_idx_ack,
|
||||||
@ -175,10 +177,12 @@ mod test {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for tst in &tests {
|
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(
|
let resp = handle(
|
||||||
&device::Device::default(),
|
&mut dev,
|
||||||
&mut ds,
|
|
||||||
&tst.end_device_conf_ans,
|
&tst.end_device_conf_ans,
|
||||||
tst.end_device_conf_req.as_ref(),
|
tst.end_device_conf_req.as_ref(),
|
||||||
);
|
);
|
||||||
@ -190,7 +194,12 @@ mod test {
|
|||||||
assert_eq!(true, resp.unwrap().is_none());
|
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 tracing::{info, warn};
|
||||||
|
|
||||||
use crate::storage::device;
|
use crate::storage::device;
|
||||||
use chirpstack_api::internal;
|
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
block: &lrwn::MACCommandSet,
|
block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Expected pending FilterListReq mac-command"));
|
return Err(anyhow!("Expected pending FilterListReq mac-command"));
|
||||||
}
|
}
|
||||||
@ -68,6 +68,7 @@ pub fn handle(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
@ -214,13 +215,11 @@ mod test {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for tst in &tests {
|
for tst in &tests {
|
||||||
let mut ds = tst.device_session.clone();
|
let mut dev = device::Device {
|
||||||
let resp = handle(
|
device_session: Some(tst.device_session.clone()),
|
||||||
&device::Device::default(),
|
..Default::default()
|
||||||
&mut ds,
|
};
|
||||||
&tst.filter_list_ans,
|
let resp = handle(&mut dev, &tst.filter_list_ans, tst.filter_list_req.as_ref());
|
||||||
tst.filter_list_req.as_ref(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(e) = &tst.expected_error {
|
if let Some(e) = &tst.expected_error {
|
||||||
assert_eq!(true, resp.is_err(), "{}", tst.name);
|
assert_eq!(true, resp.is_err(), "{}", tst.name);
|
||||||
@ -229,7 +228,12 @@ mod test {
|
|||||||
assert_eq!(true, resp.unwrap().is_none());
|
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::region;
|
||||||
use crate::storage::device;
|
use crate::storage::device;
|
||||||
use crate::uplink::UplinkFrameSet;
|
use crate::uplink::UplinkFrameSet;
|
||||||
use chirpstack_api::internal;
|
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
uplink_frame_set: &UplinkFrameSet,
|
uplink_frame_set: &UplinkFrameSet,
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
block: &lrwn::MACCommandSet,
|
block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let dev_eui = dev.dev_eui;
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Expected pending LinkADRReq mac-command"));
|
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.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>>();
|
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 {
|
} else if !ds.adr && ch_mask_ack {
|
||||||
// In case the device has ADR disabled, at least it must acknowledge the
|
// In case the device has ADR disabled, at least it must acknowledge the
|
||||||
// channel-mask. It does not have to acknowledge the other parameters.
|
// 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;
|
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 {
|
} else {
|
||||||
// increase the error counter
|
// increase the error counter
|
||||||
let count = ds
|
let count = ds
|
||||||
@ -153,6 +154,7 @@ pub fn handle(
|
|||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::region;
|
use crate::region;
|
||||||
|
use chirpstack_api::internal;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -357,11 +359,11 @@ pub mod test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for tst in &tests {
|
for tst in &tests {
|
||||||
let dev = device::Device {
|
let mut dev = device::Device {
|
||||||
dev_eui: lrwn::EUI64::from_str("0102030405060708").unwrap(),
|
dev_eui: lrwn::EUI64::from_str("0102030405060708").unwrap(),
|
||||||
|
device_session: Some(tst.device_session.clone()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut ds = tst.device_session.clone();
|
|
||||||
let block = lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRAns(
|
let block = lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRAns(
|
||||||
tst.link_adr_ans.clone(),
|
tst.link_adr_ans.clone(),
|
||||||
)]);
|
)]);
|
||||||
@ -372,7 +374,7 @@ pub mod test {
|
|||||||
None => None,
|
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 {
|
if let Some(e) = &tst.expected_error {
|
||||||
assert_eq!(true, res.is_err(), "{}", tst.name);
|
assert_eq!(true, res.is_err(), "{}", tst.name);
|
||||||
assert_eq!(e, &format!("{}", res.err().unwrap()), "{}", 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!(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::helpers::errors::PrintFullError;
|
||||||
use crate::storage::{application, device, device_profile, mac_command, tenant};
|
use crate::storage::{application, device, device_profile, mac_command, tenant};
|
||||||
use crate::uplink::UplinkFrameSet;
|
use crate::uplink::UplinkFrameSet;
|
||||||
use chirpstack_api::internal;
|
|
||||||
use lrwn::EUI64;
|
|
||||||
|
|
||||||
pub mod configure_fwd_limit;
|
pub mod configure_fwd_limit;
|
||||||
pub mod ctrl_uplink_list;
|
pub mod ctrl_uplink_list;
|
||||||
@ -41,15 +39,13 @@ pub async fn handle_uplink<'a>(
|
|||||||
tenant: &tenant::Tenant,
|
tenant: &tenant::Tenant,
|
||||||
app: &application::Application,
|
app: &application::Application,
|
||||||
dp: &device_profile::DeviceProfile,
|
dp: &device_profile::DeviceProfile,
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
) -> Result<(Vec<lrwn::MACCommandSet>, bool)> {
|
) -> Result<(Vec<lrwn::MACCommandSet>, bool)> {
|
||||||
let conf = config::get();
|
let conf = config::get();
|
||||||
if conf.network.mac_commands_disabled {
|
if conf.network.mac_commands_disabled {
|
||||||
return Ok((Vec::new(), false));
|
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 cids: Vec<lrwn::CID> = Vec::new(); // to maintain the CID order
|
||||||
let mut blocks: HashMap<lrwn::CID, lrwn::MACCommandSet> = HashMap::new();
|
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.
|
// 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,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Delete the pending mac-command.
|
// Delete the pending mac-command.
|
||||||
if pending.is_some() {
|
if pending.is_some() {
|
||||||
if let Err(e) = mac_command::delete_pending(&dev_eui, cid).await {
|
if let Err(e) = mac_command::delete_pending(&dev.dev_eui, cid).await {
|
||||||
error!(dev_eui = %dev_eui, cid = %cid, error = %e, "Delete pending mac-command error");
|
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,
|
app,
|
||||||
dp,
|
dp,
|
||||||
dev,
|
dev,
|
||||||
ds,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -135,8 +130,7 @@ async fn handle(
|
|||||||
tenant: &tenant::Tenant,
|
tenant: &tenant::Tenant,
|
||||||
app: &application::Application,
|
app: &application::Application,
|
||||||
dp: &device_profile::DeviceProfile,
|
dp: &device_profile::DeviceProfile,
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
match cid {
|
match cid {
|
||||||
lrwn::CID::DevStatusAns => {
|
lrwn::CID::DevStatusAns => {
|
||||||
@ -144,30 +138,26 @@ async fn handle(
|
|||||||
}
|
}
|
||||||
lrwn::CID::DeviceModeInd => device_mode_ind::handle(dev, block).await,
|
lrwn::CID::DeviceModeInd => device_mode_ind::handle(dev, block).await,
|
||||||
lrwn::CID::DeviceTimeReq => device_time::handle(uplink_frame_set, dev, block),
|
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::LinkCheckReq => link_check::handle(uplink_frame_set, dev, block),
|
||||||
lrwn::CID::NewChannelAns => new_channel::handle(dev, ds, block, pending_block),
|
lrwn::CID::NewChannelAns => new_channel::handle(dev, block, pending_block),
|
||||||
lrwn::CID::PingSlotChannelAns => ping_slot_channel::handle(dev, ds, block, pending_block),
|
lrwn::CID::PingSlotChannelAns => ping_slot_channel::handle(dev, block, pending_block),
|
||||||
lrwn::CID::PingSlotInfoReq => ping_slot_info::handle(dev, ds, block),
|
lrwn::CID::PingSlotInfoReq => ping_slot_info::handle(dev, block),
|
||||||
lrwn::CID::RejoinParamSetupAns => rejoin_param_setup::handle(dev, ds, block, pending_block),
|
lrwn::CID::RejoinParamSetupAns => rejoin_param_setup::handle(dev, block, pending_block),
|
||||||
lrwn::CID::RekeyInd => rekey::handle(dev, block),
|
lrwn::CID::RekeyInd => rekey::handle(dev, block),
|
||||||
lrwn::CID::ResetInd => reset::handle(dev, dp, ds, block),
|
lrwn::CID::ResetInd => reset::handle(dev, dp, block),
|
||||||
lrwn::CID::RxParamSetupAns => rx_param_setup::handle(dev, ds, block, pending_block),
|
lrwn::CID::RxParamSetupAns => rx_param_setup::handle(dev, block, pending_block),
|
||||||
lrwn::CID::RxTimingSetupAns => rx_timing_setup::handle(dev, ds, block, pending_block),
|
lrwn::CID::RxTimingSetupAns => rx_timing_setup::handle(dev, block, pending_block),
|
||||||
lrwn::CID::TxParamSetupAns => tx_param_setup::handle(dev, ds, block, pending_block),
|
lrwn::CID::TxParamSetupAns => tx_param_setup::handle(dev, block, pending_block),
|
||||||
lrwn::CID::RelayConfAns => relay_conf::handle(dev, ds, block, pending_block),
|
lrwn::CID::RelayConfAns => relay_conf::handle(dev, block, pending_block),
|
||||||
lrwn::CID::EndDeviceConfAns => end_device_conf::handle(dev, ds, block, pending_block),
|
lrwn::CID::EndDeviceConfAns => end_device_conf::handle(dev, block, pending_block),
|
||||||
lrwn::CID::FilterListAns => filter_list::handle(dev, ds, block, pending_block),
|
lrwn::CID::FilterListAns => filter_list::handle(dev, block, pending_block),
|
||||||
lrwn::CID::UpdateUplinkListAns => update_uplink_list::handle(dev, ds, block, pending_block),
|
lrwn::CID::UpdateUplinkListAns => update_uplink_list::handle(dev, block, pending_block),
|
||||||
lrwn::CID::ConfigureFwdLimitAns => {
|
lrwn::CID::ConfigureFwdLimitAns => configure_fwd_limit::handle(dev, block, pending_block),
|
||||||
configure_fwd_limit::handle(dev, ds, block, pending_block)
|
|
||||||
}
|
|
||||||
lrwn::CID::NotifyNewEndDeviceReq => {
|
lrwn::CID::NotifyNewEndDeviceReq => {
|
||||||
notify_new_end_device::handle(tenant, dp, app, dev, block).await
|
notify_new_end_device::handle(tenant, dp, app, dev, block).await
|
||||||
}
|
}
|
||||||
lrwn::CID::CtrlUplinkListAns => {
|
lrwn::CID::CtrlUplinkListAns => ctrl_uplink_list::handle(dev, block, pending_block).await,
|
||||||
ctrl_uplink_list::handle(dev, ds, block, pending_block).await
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
warn!(cid = %cid, "Unexpected CID");
|
warn!(cid = %cid, "Unexpected CID");
|
||||||
// Return OK, we don't want to break out of the uplink handling.
|
// Return OK, we don't want to break out of the uplink handling.
|
||||||
@ -180,6 +170,8 @@ async fn handle(
|
|||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::config;
|
use crate::config;
|
||||||
|
use chirpstack_api::internal;
|
||||||
|
use lrwn::EUI64;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@ -213,16 +205,18 @@ pub mod test {
|
|||||||
let t: tenant::Tenant = Default::default();
|
let t: tenant::Tenant = Default::default();
|
||||||
let app: application::Application = Default::default();
|
let app: application::Application = Default::default();
|
||||||
let dp: device_profile::DeviceProfile = Default::default();
|
let dp: device_profile::DeviceProfile = Default::default();
|
||||||
let dev: device::Device = Default::default();
|
let mut dev = device::Device {
|
||||||
let mut ds = internal::DeviceSession {
|
dev_eui: EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
dev_eui: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
device_session: Some(internal::DeviceSession {
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// must respond
|
// must respond
|
||||||
let cmds = lrwn::MACCommandSet::new(vec![lrwn::MACCommand::RxTimingSetupAns]);
|
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
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(0, resp.len());
|
assert_eq!(0, resp.len());
|
||||||
@ -233,7 +227,7 @@ pub mod test {
|
|||||||
conf.network.mac_commands_disabled = true;
|
conf.network.mac_commands_disabled = true;
|
||||||
config::set(conf);
|
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
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(0, resp.len());
|
assert_eq!(0, resp.len());
|
||||||
|
@ -62,11 +62,13 @@ pub fn request(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
block: &lrwn::MACCommandSet,
|
block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let dev_eui = dev.dev_eui;
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Expected pending NewChannelReq"));
|
return Err(anyhow!("Expected pending NewChannelReq"));
|
||||||
}
|
}
|
||||||
@ -115,7 +117,7 @@ pub fn handle(
|
|||||||
.push(req_pl.ch_index as u32);
|
.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 {
|
} else {
|
||||||
let count = ds
|
let count = ds
|
||||||
.mac_command_error_count
|
.mac_command_error_count
|
||||||
@ -124,7 +126,7 @@ pub fn handle(
|
|||||||
*count += 1;
|
*count += 1;
|
||||||
|
|
||||||
warn!(
|
warn!(
|
||||||
dev_eui = %dev.dev_eui,
|
dev_eui = %dev_eui,
|
||||||
freq = req_pl.freq,
|
freq = req_pl.freq,
|
||||||
channel = req_pl.ch_index,
|
channel = req_pl.ch_index,
|
||||||
min_dr = req_pl.min_dr,
|
min_dr = req_pl.min_dr,
|
||||||
@ -469,16 +471,12 @@ pub mod test {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for tst in &tests {
|
for tst in &tests {
|
||||||
let mut ds = tst.device_session.clone();
|
let mut dev = device::Device {
|
||||||
|
device_session: Some(tst.device_session.clone()),
|
||||||
let res = handle(
|
|
||||||
&device::Device {
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
};
|
||||||
&mut ds,
|
|
||||||
&tst.new_channel_ans,
|
let res = handle(&mut dev, &tst.new_channel_ans, tst.new_channel_req.as_ref());
|
||||||
tst.new_channel_req.as_ref(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(e) = &tst.expected_error {
|
if let Some(e) = &tst.expected_error {
|
||||||
assert_eq!(true, res.is_err(), "{}", tst.name);
|
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!(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 tracing::{info, warn};
|
||||||
|
|
||||||
use crate::storage::device;
|
use crate::storage::device;
|
||||||
use chirpstack_api::internal;
|
|
||||||
|
|
||||||
pub fn request(dr: u8, freq: u32) -> lrwn::MACCommandSet {
|
pub fn request(dr: u8, freq: u32) -> lrwn::MACCommandSet {
|
||||||
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::PingSlotChannelReq(
|
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::PingSlotChannelReq(
|
||||||
@ -11,11 +10,12 @@ pub fn request(dr: u8, freq: u32) -> lrwn::MACCommandSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
block: &lrwn::MACCommandSet,
|
block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Pending PingSlotChannelReq expected"));
|
return Err(anyhow!("Pending PingSlotChannelReq expected"));
|
||||||
}
|
}
|
||||||
@ -66,6 +66,7 @@ pub fn handle(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
@ -181,12 +182,12 @@ pub mod test {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for tst in &tests {
|
for tst in &tests {
|
||||||
let mut ds = tst.device_session.clone();
|
let mut dev = device::Device {
|
||||||
let resp = handle(
|
device_session: Some(tst.device_session.clone()),
|
||||||
&device::Device {
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
};
|
||||||
&mut ds,
|
let resp = handle(
|
||||||
|
&mut dev,
|
||||||
&tst.ping_slot_channel_ans,
|
&tst.ping_slot_channel_ans,
|
||||||
tst.ping_slot_channel_req.as_ref(),
|
tst.ping_slot_channel_req.as_ref(),
|
||||||
);
|
);
|
||||||
@ -198,7 +199,10 @@ pub mod test {
|
|||||||
assert_eq!(true, resp.unwrap().is_none());
|
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 tracing::info;
|
||||||
|
|
||||||
use crate::storage::device;
|
use crate::storage::device;
|
||||||
use chirpstack_api::internal;
|
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
block: &lrwn::MACCommandSet,
|
block: &lrwn::MACCommandSet,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let dev_eui = dev.dev_eui;
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
let mac = (**block)
|
let mac = (**block)
|
||||||
.first()
|
.first()
|
||||||
.ok_or_else(|| anyhow!("MACCommandSet is empty"))?;
|
.ok_or_else(|| anyhow!("MACCommandSet is empty"))?;
|
||||||
@ -21,7 +22,7 @@ pub fn handle(
|
|||||||
|
|
||||||
ds.class_b_ping_slot_nb = 1 << (7 - pl.periodicity);
|
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![
|
Ok(Some(lrwn::MACCommandSet::new(vec![
|
||||||
lrwn::MACCommand::PingSlotInfoAns,
|
lrwn::MACCommand::PingSlotInfoAns,
|
||||||
@ -31,22 +32,19 @@ pub fn handle(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_handle() {
|
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(
|
let block = lrwn::MACCommandSet::new(vec![lrwn::MACCommand::PingSlotInfoReq(
|
||||||
lrwn::PingSlotInfoReqPayload { periodicity: 3 },
|
lrwn::PingSlotInfoReqPayload { periodicity: 3 },
|
||||||
)]);
|
)]);
|
||||||
let res = handle(
|
let res = handle(&mut dev, &block).unwrap();
|
||||||
&device::Device {
|
assert_eq!(16, dev.get_device_session().unwrap().class_b_ping_slot_nb);
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
&mut ds,
|
|
||||||
&block,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(16, ds.class_b_ping_slot_nb);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(lrwn::MACCommandSet::new(vec![
|
Some(lrwn::MACCommandSet::new(vec![
|
||||||
lrwn::MACCommand::PingSlotInfoAns,
|
lrwn::MACCommand::PingSlotInfoAns,
|
||||||
|
@ -2,7 +2,6 @@ use anyhow::Result;
|
|||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
use crate::storage::device;
|
use crate::storage::device;
|
||||||
use chirpstack_api::internal;
|
|
||||||
|
|
||||||
pub fn request(max_time_n: u8, max_count_n: u8) -> lrwn::MACCommandSet {
|
pub fn request(max_time_n: u8, max_count_n: u8) -> lrwn::MACCommandSet {
|
||||||
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::RejoinParamSetupReq(
|
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(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
block: &lrwn::MACCommandSet,
|
block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Pending RejoinParamSetupReq expected"));
|
return Err(anyhow!("Pending RejoinParamSetupReq expected"));
|
||||||
}
|
}
|
||||||
@ -57,6 +57,7 @@ pub fn handle(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
@ -159,12 +160,12 @@ pub mod test {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for tst in &tests {
|
for tst in &tests {
|
||||||
let mut ds = tst.device_session.clone();
|
let mut dev = device::Device {
|
||||||
let resp = handle(
|
device_session: Some(tst.device_session.clone()),
|
||||||
&device::Device {
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
};
|
||||||
&mut ds,
|
let resp = handle(
|
||||||
|
&mut dev,
|
||||||
&tst.rejoin_param_setup_ans,
|
&tst.rejoin_param_setup_ans,
|
||||||
tst.rejoin_param_setup_req.as_ref(),
|
tst.rejoin_param_setup_req.as_ref(),
|
||||||
);
|
);
|
||||||
@ -176,7 +177,12 @@ pub mod test {
|
|||||||
assert_eq!(true, resp.unwrap().is_none());
|
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;
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
block: &lrwn::MACCommandSet,
|
block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let dev_eui = dev.dev_eui;
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Expected pending RelayConfReq mac-command"));
|
return Err(anyhow!("Expected pending RelayConfReq mac-command"));
|
||||||
}
|
}
|
||||||
@ -43,7 +45,7 @@ pub fn handle(
|
|||||||
&& ans_pl.default_ch_idx_ack
|
&& ans_pl.default_ch_idx_ack
|
||||||
&& ans_pl.cad_periodicity_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 {
|
if let Some(relay) = &mut ds.relay {
|
||||||
relay.enabled = req_pl.channel_settings_relay.start_stop == 1;
|
relay.enabled = req_pl.channel_settings_relay.start_stop == 1;
|
||||||
@ -56,7 +58,7 @@ pub fn handle(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
dev_eui = %dev.dev_eui,
|
dev_eui = %dev_eui,
|
||||||
second_ch_ack_offset_ack = ans_pl.second_ch_ack_offset_ack,
|
second_ch_ack_offset_ack = ans_pl.second_ch_ack_offset_ack,
|
||||||
second_ch_dr_ack = ans_pl.second_ch_dr_ack,
|
second_ch_dr_ack = ans_pl.second_ch_dr_ack,
|
||||||
second_ch_idx_ack = ans_pl.second_ch_idx_ack,
|
second_ch_idx_ack = ans_pl.second_ch_idx_ack,
|
||||||
@ -177,13 +179,11 @@ mod test {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for tst in &tests {
|
for tst in &tests {
|
||||||
let mut ds = tst.device_session.clone();
|
let mut dev = device::Device {
|
||||||
let resp = handle(
|
device_session: Some(tst.device_session.clone()),
|
||||||
&device::Device::default(),
|
..Default::default()
|
||||||
&mut ds,
|
};
|
||||||
&tst.relay_conf_ans,
|
let resp = handle(&mut dev, &tst.relay_conf_ans, tst.relay_conf_req.as_ref());
|
||||||
tst.relay_conf_req.as_ref(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(e) = &tst.expected_error {
|
if let Some(e) = &tst.expected_error {
|
||||||
assert_eq!(true, resp.is_err(), "{}", tst.name);
|
assert_eq!(true, resp.is_err(), "{}", tst.name);
|
||||||
@ -192,7 +192,12 @@ mod test {
|
|||||||
assert_eq!(true, resp.unwrap().is_none());
|
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 tracing::info;
|
||||||
|
|
||||||
use crate::storage::{device, device_profile};
|
use crate::storage::{device, device_profile};
|
||||||
use chirpstack_api::internal;
|
|
||||||
|
|
||||||
const SERV_LORAWAN_VERSION: lrwn::Version = lrwn::Version::LoRaWAN1_1;
|
const SERV_LORAWAN_VERSION: lrwn::Version = lrwn::Version::LoRaWAN1_1;
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
dp: &device_profile::DeviceProfile,
|
dp: &device_profile::DeviceProfile,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
block: &lrwn::MACCommandSet,
|
block: &lrwn::MACCommandSet,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let dev_eui = dev.dev_eui;
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
let block_mac = (**block)
|
let block_mac = (**block)
|
||||||
.first()
|
.first()
|
||||||
.ok_or_else(|| anyhow!("MACCommandSet is empty"))?;
|
.ok_or_else(|| anyhow!("MACCommandSet is empty"))?;
|
||||||
@ -21,7 +22,7 @@ pub fn handle(
|
|||||||
return Err(anyhow!("ResetInd expected"));
|
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);
|
dp.reset_session_to_boot_params(ds);
|
||||||
|
|
||||||
@ -41,23 +42,13 @@ pub fn handle(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use chirpstack_api::internal;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_handle() {
|
fn test_handle() {
|
||||||
let dev: device::Device = Default::default();
|
let mut dev = device::Device {
|
||||||
let dp = device_profile::DeviceProfile {
|
device_session: Some(internal::DeviceSession {
|
||||||
supports_otaa: false,
|
|
||||||
abp_rx1_delay: 1,
|
|
||||||
abp_rx1_dr_offset: 0,
|
|
||||||
abp_rx2_dr: 0,
|
|
||||||
abp_rx2_freq: 868300000,
|
|
||||||
class_b_ping_slot_dr: 2,
|
|
||||||
class_b_ping_slot_freq: 868100000,
|
|
||||||
class_b_ping_slot_nb_k: 1,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let mut ds = internal::DeviceSession {
|
|
||||||
tx_power_index: 3,
|
tx_power_index: 3,
|
||||||
min_supported_tx_power_index: 1,
|
min_supported_tx_power_index: 1,
|
||||||
max_supported_tx_power_index: 5,
|
max_supported_tx_power_index: 5,
|
||||||
@ -71,12 +62,24 @@ pub mod test {
|
|||||||
class_b_ping_slot_freq: 868100000,
|
class_b_ping_slot_freq: 868100000,
|
||||||
nb_trans: 3,
|
nb_trans: 3,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let dp = device_profile::DeviceProfile {
|
||||||
|
supports_otaa: false,
|
||||||
|
abp_rx1_delay: 1,
|
||||||
|
abp_rx1_dr_offset: 0,
|
||||||
|
abp_rx2_dr: 0,
|
||||||
|
abp_rx2_freq: 868300000,
|
||||||
|
class_b_ping_slot_dr: 2,
|
||||||
|
class_b_ping_slot_freq: 868100000,
|
||||||
|
class_b_ping_slot_nb_k: 1,
|
||||||
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let resp = handle(
|
let resp = handle(
|
||||||
&dev,
|
&mut dev,
|
||||||
&dp,
|
&dp,
|
||||||
&mut ds,
|
|
||||||
&lrwn::MACCommandSet::new(vec![lrwn::MACCommand::ResetInd(lrwn::ResetIndPayload {
|
&lrwn::MACCommandSet::new(vec![lrwn::MACCommand::ResetInd(lrwn::ResetIndPayload {
|
||||||
dev_lorawan_version: lrwn::Version::LoRaWAN1_1,
|
dev_lorawan_version: lrwn::Version::LoRaWAN1_1,
|
||||||
})]),
|
})]),
|
||||||
@ -93,7 +96,7 @@ pub mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
internal::DeviceSession {
|
&internal::DeviceSession {
|
||||||
rx1_delay: 1,
|
rx1_delay: 1,
|
||||||
rx1_dr_offset: 0,
|
rx1_dr_offset: 0,
|
||||||
rx2_dr: 0,
|
rx2_dr: 0,
|
||||||
@ -110,7 +113,7 @@ pub mod test {
|
|||||||
extra_uplink_channels: HashMap::new(),
|
extra_uplink_channels: HashMap::new(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
ds
|
dev.get_device_session().unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ use anyhow::Result;
|
|||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
use crate::storage::device;
|
use crate::storage::device;
|
||||||
use chirpstack_api::internal;
|
|
||||||
|
|
||||||
pub fn request(rx1_dr_offset: u8, rx2_freq: u32, rx2_dr: u8) -> lrwn::MACCommandSet {
|
pub fn request(rx1_dr_offset: u8, rx2_freq: u32, rx2_dr: u8) -> lrwn::MACCommandSet {
|
||||||
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::RxParamSetupReq(
|
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(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
block: &lrwn::MACCommandSet,
|
block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Expected pending RxParamSetupReq"));
|
return Err(anyhow!("Expected pending RxParamSetupReq"));
|
||||||
}
|
}
|
||||||
@ -70,6 +70,7 @@ pub fn handle(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
@ -182,12 +183,12 @@ pub mod test {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for tst in &tests {
|
for tst in &tests {
|
||||||
let mut ds = tst.device_session.clone();
|
let mut dev = device::Device {
|
||||||
let resp = handle(
|
device_session: Some(tst.device_session.clone()),
|
||||||
&device::Device {
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
};
|
||||||
&mut ds,
|
let resp = handle(
|
||||||
|
&mut dev,
|
||||||
&tst.rx_param_setup_ans,
|
&tst.rx_param_setup_ans,
|
||||||
tst.rx_param_setup_req.as_ref(),
|
tst.rx_param_setup_req.as_ref(),
|
||||||
);
|
);
|
||||||
@ -199,7 +200,12 @@ pub mod test {
|
|||||||
assert_eq!(true, resp.unwrap().is_none());
|
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 tracing::info;
|
||||||
|
|
||||||
use crate::storage::device;
|
use crate::storage::device;
|
||||||
use chirpstack_api::internal;
|
|
||||||
|
|
||||||
pub fn request(rx1_delay: u8) -> lrwn::MACCommandSet {
|
pub fn request(rx1_delay: u8) -> lrwn::MACCommandSet {
|
||||||
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::RxTimingSetupReq(
|
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::RxTimingSetupReq(
|
||||||
@ -11,11 +10,13 @@ pub fn request(rx1_delay: u8) -> lrwn::MACCommandSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
_block: &lrwn::MACCommandSet,
|
_block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let dev_eui = dev.dev_eui;
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Pending RxTimingSetupReq expected"));
|
return Err(anyhow!("Pending RxTimingSetupReq expected"));
|
||||||
}
|
}
|
||||||
@ -31,7 +32,7 @@ pub fn handle(
|
|||||||
};
|
};
|
||||||
|
|
||||||
ds.rx1_delay = req_pl.delay as u32;
|
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)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@ -39,6 +40,7 @@ pub fn handle(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
@ -100,12 +102,12 @@ pub mod test {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for tst in &tests {
|
for tst in &tests {
|
||||||
let mut ds = tst.device_session.clone();
|
let mut dev = device::Device {
|
||||||
let resp = handle(
|
device_session: Some(tst.device_session.clone()),
|
||||||
&device::Device {
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
};
|
||||||
&mut ds,
|
let resp = handle(
|
||||||
|
&mut dev,
|
||||||
&tst.rx_timing_setup_ans,
|
&tst.rx_timing_setup_ans,
|
||||||
tst.rx_timing_setup_req.as_ref(),
|
tst.rx_timing_setup_req.as_ref(),
|
||||||
);
|
);
|
||||||
@ -117,7 +119,12 @@ pub mod test {
|
|||||||
assert_eq!(true, resp.unwrap().is_none());
|
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 tracing::info;
|
||||||
|
|
||||||
use crate::storage::device;
|
use crate::storage::device;
|
||||||
use chirpstack_api::internal;
|
|
||||||
|
|
||||||
pub fn request(
|
pub fn request(
|
||||||
uplink_dwell_time_400ms: bool,
|
uplink_dwell_time_400ms: bool,
|
||||||
@ -29,11 +28,13 @@ pub fn request(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
_block: &lrwn::MACCommandSet,
|
_block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let dev_eui = dev.dev_eui;
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Expected pending TxParamSetupReq"));
|
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.downlink_dwell_time_400ms = req_pl.downlink_dwell_time == lrwn::DwellTime::Limit400ms;
|
||||||
ds.uplink_max_eirp_index = req_pl.max_eirp as u32;
|
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)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
@ -136,12 +138,12 @@ pub mod test {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for tst in &tests {
|
for tst in &tests {
|
||||||
let mut ds = tst.device_session.clone();
|
let mut dev = device::Device {
|
||||||
let resp = handle(
|
device_session: Some(tst.device_session.clone()),
|
||||||
&device::Device {
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
};
|
||||||
&mut ds,
|
let resp = handle(
|
||||||
|
&mut dev,
|
||||||
&tst.tx_param_setup_ans,
|
&tst.tx_param_setup_ans,
|
||||||
tst.tx_param_setup_req.as_ref(),
|
tst.tx_param_setup_req.as_ref(),
|
||||||
);
|
);
|
||||||
@ -153,7 +155,12 @@ pub mod test {
|
|||||||
assert_eq!(true, resp.unwrap().is_none());
|
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 tracing::info;
|
||||||
|
|
||||||
use crate::storage::device;
|
use crate::storage::device;
|
||||||
use chirpstack_api::internal;
|
|
||||||
|
|
||||||
pub fn handle(
|
pub fn handle(
|
||||||
_dev: &device::Device,
|
dev: &mut device::Device,
|
||||||
ds: &mut internal::DeviceSession,
|
|
||||||
_block: &lrwn::MACCommandSet,
|
_block: &lrwn::MACCommandSet,
|
||||||
pending: Option<&lrwn::MACCommandSet>,
|
pending: Option<&lrwn::MACCommandSet>,
|
||||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||||
|
let ds = dev.get_device_session_mut()?;
|
||||||
|
|
||||||
if pending.is_none() {
|
if pending.is_none() {
|
||||||
return Err(anyhow!("Expected pending UpdateUplinkListReq mac-command"));
|
return Err(anyhow!("Expected pending UpdateUplinkListReq mac-command"));
|
||||||
}
|
}
|
||||||
@ -43,6 +43,7 @@ pub fn handle(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use chirpstack_api::internal;
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
@ -124,12 +125,13 @@ pub mod test {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for tst in &tests {
|
for tst in &tests {
|
||||||
let mut ds = tst.device_session.clone();
|
let mut dev = device::Device {
|
||||||
let resp = handle(
|
device_session: Some(tst.device_session.clone()),
|
||||||
&device::Device {
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
};
|
||||||
&mut ds,
|
|
||||||
|
let resp = handle(
|
||||||
|
&mut dev,
|
||||||
&tst.update_uplink_list_ans,
|
&tst.update_uplink_list_ans,
|
||||||
tst.update_uplink_list_req.as_ref(),
|
tst.update_uplink_list_req.as_ref(),
|
||||||
);
|
);
|
||||||
@ -141,7 +143,12 @@ pub mod test {
|
|||||||
assert_eq!(true, resp.unwrap().is_none());
|
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")]
|
#[arg(short, long, value_name = "NAME")]
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Migrate device-sessions from Redis to PostgreSQL.
|
||||||
|
MigrateDeviceSessionsToPostgres {},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -116,6 +119,7 @@ async fn main() -> Result<()> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
Some(Commands::CreateApiKey { name }) => cmd::create_api_key::run(name).await?,
|
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?,
|
None => cmd::root::run().await?,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,12 +10,20 @@ use diesel_async::RunQueryDsl;
|
|||||||
use tracing::info;
|
use tracing::info;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use chirpstack_api::internal;
|
||||||
use lrwn::{DevAddr, EUI64};
|
use lrwn::{DevAddr, EUI64};
|
||||||
|
|
||||||
use super::schema::{application, device, device_profile, multicast_group_device, tenant};
|
use super::schema::{application, device, device_profile, multicast_group_device, tenant};
|
||||||
use super::{error::Error, fields, get_async_db_conn};
|
use super::{error::Error, fields, get_async_db_conn};
|
||||||
|
use crate::api::helpers::FromProto;
|
||||||
use crate::config;
|
use crate::config;
|
||||||
|
|
||||||
|
pub enum ValidationStatus {
|
||||||
|
Ok(u32, Device),
|
||||||
|
Retransmission(u32, Device),
|
||||||
|
Reset(u32, Device),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, AsExpression, FromSqlRow)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, AsExpression, FromSqlRow)]
|
||||||
#[diesel(sql_type = Text)]
|
#[diesel(sql_type = Text)]
|
||||||
pub enum DeviceClass {
|
pub enum DeviceClass {
|
||||||
@ -95,6 +103,24 @@ pub struct Device {
|
|||||||
pub tags: fields::KeyValue,
|
pub tags: fields::KeyValue,
|
||||||
pub variables: fields::KeyValue,
|
pub variables: fields::KeyValue,
|
||||||
pub join_eui: EUI64,
|
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 {
|
impl Device {
|
||||||
@ -104,6 +130,22 @@ impl Device {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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 {
|
impl Default for Device {
|
||||||
@ -134,6 +176,8 @@ impl Default for Device {
|
|||||||
tags: fields::KeyValue::new(HashMap::new()),
|
tags: fields::KeyValue::new(HashMap::new()),
|
||||||
variables: fields::KeyValue::new(HashMap::new()),
|
variables: fields::KeyValue::new(HashMap::new()),
|
||||||
join_eui: EUI64::default(),
|
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)
|
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> {
|
pub async fn update(d: Device) -> Result<Device, Error> {
|
||||||
d.validate()?;
|
d.validate()?;
|
||||||
|
|
||||||
@ -260,80 +528,14 @@ pub async fn update(d: Device) -> Result<Device, Error> {
|
|||||||
Ok(d)
|
Ok(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_enabled_class(dev_eui: &EUI64, mode: DeviceClass) -> Result<Device, Error> {
|
pub async fn partial_update(dev_eui: EUI64, d: &DeviceChangeset) -> Result<Device, Error> {
|
||||||
let d: Device = diesel::update(device::dsl::device.find(&dev_eui))
|
let d = diesel::update(device::dsl::device.find(&dev_eui))
|
||||||
.set(device::enabled_class.eq(&mode))
|
.set(d)
|
||||||
.get_result(&mut get_async_db_conn().await?)
|
.get_result::<Device>(&mut get_async_db_conn().await?)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Error::from_diesel(e, dev_eui.to_string()))?;
|
.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> {
|
info!(dev_eui = %dev_eui, "Device partially updated");
|
||||||
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");
|
|
||||||
Ok(d)
|
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")
|
.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)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::storage;
|
use crate::storage;
|
||||||
use crate::storage::device_queue;
|
use crate::storage::device_queue;
|
||||||
use crate::test;
|
use crate::test;
|
||||||
|
use lrwn::AES128Key;
|
||||||
|
|
||||||
struct FilterTest<'a> {
|
struct FilterTest<'a> {
|
||||||
filters: Filters,
|
filters: Filters,
|
||||||
@ -696,13 +922,37 @@ pub mod test {
|
|||||||
assert_eq!(0, res.len());
|
assert_eq!(0, res.len());
|
||||||
|
|
||||||
// device in Class-B.
|
// 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 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());
|
assert_eq!(1, res.len());
|
||||||
|
|
||||||
// device in Class-C
|
// 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();
|
let res = get_with_class_b_c_queue_items(10).await.unwrap();
|
||||||
assert_eq!(1, res.len());
|
assert_eq!(1, res.len());
|
||||||
|
|
||||||
@ -712,7 +962,15 @@ pub mod test {
|
|||||||
assert_eq!(0, res.len());
|
assert_eq!(0, res.len());
|
||||||
|
|
||||||
// device in class C / downlink is pending.
|
// 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.is_pending = true;
|
||||||
qi.timeout_after = Some(Utc::now() + Duration::seconds(10));
|
qi.timeout_after = Some(Utc::now() + Duration::seconds(10));
|
||||||
qi = device_queue::update_item(qi).await.unwrap();
|
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();
|
let res = get_with_class_b_c_queue_items(10).await.unwrap();
|
||||||
assert_eq!(1, res.len());
|
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 std::io::Cursor;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
use tracing::{error, info, trace};
|
|
||||||
|
|
||||||
use super::error::Error;
|
use super::error::Error;
|
||||||
use super::{get_async_redis_conn, redis_key};
|
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 chirpstack_api::internal;
|
||||||
use lrwn::{AES128Key, DevAddr, Payload, PhyPayload, EUI64};
|
use lrwn::EUI64;
|
||||||
|
|
||||||
pub enum ValidationStatus {
|
pub async fn get(dev_eui: &EUI64) -> Result<internal::DeviceSession, Error> {
|
||||||
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> {
|
|
||||||
let key = redis_key(format!("device:{{{}}}:ds", dev_eui));
|
let key = redis_key(format!("device:{{{}}}:ds", dev_eui));
|
||||||
|
|
||||||
let v: Vec<u8> = redis::cmd("GET")
|
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() {
|
if v.is_empty() {
|
||||||
return Err(Error::NotFound(dev_eui.to_string()));
|
return Err(Error::NotFound(dev_eui.to_string()));
|
||||||
}
|
}
|
||||||
let ds = chirpstack_api::internal::DeviceSession::decode(&mut Cursor::new(v))
|
let ds =
|
||||||
.context("Decode device-session")?;
|
internal::DeviceSession::decode(&mut Cursor::new(v)).context("Decode device-session")?;
|
||||||
Ok(ds)
|
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,
|
tags -> Jsonb,
|
||||||
variables -> Jsonb,
|
variables -> Jsonb,
|
||||||
join_eui -> Bytea,
|
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::integration::mock;
|
||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
device::{self, DeviceClass},
|
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 chirpstack_api::{gw, integration as integration_pb, internal, stream};
|
||||||
use lrwn::EUI64;
|
use lrwn::EUI64;
|
||||||
@ -27,7 +27,8 @@ pub fn f_cnt_up(dev_eui: EUI64, f_cnt: u32) -> Validator {
|
|||||||
Box::new(move || {
|
Box::new(move || {
|
||||||
let dev_eui = dev_eui.clone();
|
let dev_eui = dev_eui.clone();
|
||||||
Box::pin(async move {
|
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);
|
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 || {
|
Box::new(move || {
|
||||||
let dev_eui = dev_eui.clone();
|
let dev_eui = dev_eui.clone();
|
||||||
Box::pin(async move {
|
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);
|
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 || {
|
Box::new(move || {
|
||||||
let dev_eui = dev_eui.clone();
|
let dev_eui = dev_eui.clone();
|
||||||
Box::pin(async move {
|
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);
|
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 || {
|
Box::new(move || {
|
||||||
let dev_eui = dev_eui.clone();
|
let dev_eui = dev_eui.clone();
|
||||||
Box::pin(async move {
|
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);
|
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 || {
|
Box::new(move || {
|
||||||
let dev_eui = dev_eui.clone();
|
let dev_eui = dev_eui.clone();
|
||||||
Box::pin(async move {
|
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);
|
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 dev_eui = dev_eui.clone();
|
||||||
let channels = channels.clone();
|
let channels = channels.clone();
|
||||||
Box::pin(async move {
|
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);
|
assert_eq!(channels, ds.enabled_uplink_channel_indices);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -88,7 +94,8 @@ pub fn dr(dev_eui: EUI64, dr: u32) -> Validator {
|
|||||||
Box::new(move || {
|
Box::new(move || {
|
||||||
let dev_eui = dev_eui.clone();
|
let dev_eui = dev_eui.clone();
|
||||||
Box::pin(async move {
|
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);
|
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 || {
|
Box::new(move || {
|
||||||
let dev_eui = dev_eui.clone();
|
let dev_eui = dev_eui.clone();
|
||||||
Box::pin(async move {
|
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!(
|
assert_eq!(
|
||||||
count,
|
count,
|
||||||
ds.mac_command_error_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 dev_eui = dev_eui.clone();
|
||||||
let uh = uh.clone();
|
let uh = uh.clone();
|
||||||
Box::pin(async move {
|
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);
|
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 || {
|
Box::new(move || {
|
||||||
let ds = ds.clone();
|
let ds = ds.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let ds_get = device_session::get(&dev_eui).await.unwrap();
|
let d = device::get(&dev_eui).await.unwrap();
|
||||||
assert_eq!(ds, ds_get);
|
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 {
|
pub fn no_device_session(dev_eui: EUI64) -> Validator {
|
||||||
Box::new(move || {
|
Box::new(move || {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let res = device_session::get(&dev_eui).await;
|
let d = device::get(&dev_eui).await.unwrap();
|
||||||
assert_eq!(true, res.is_err());
|
assert!(d.device_session.is_none());
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ use crate::gateway::backend as gateway_backend;
|
|||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
application,
|
application,
|
||||||
device::{self, DeviceClass},
|
device::{self, DeviceClass},
|
||||||
device_profile, device_queue, device_session, gateway, tenant,
|
device_profile, device_queue, gateway, tenant,
|
||||||
};
|
};
|
||||||
use crate::{config, test, uplink};
|
use crate::{config, test, uplink};
|
||||||
use chirpstack_api::{common, gw, internal};
|
use chirpstack_api::{common, gw, internal};
|
||||||
@ -219,33 +219,18 @@ async fn test_sns_uplink() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.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 {
|
let dev = device::create(device::Device {
|
||||||
name: "device".into(),
|
name: "device".into(),
|
||||||
application_id: app.id.clone(),
|
application_id: app.id.clone(),
|
||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::B,
|
enabled_class: DeviceClass::B,
|
||||||
..Default::default()
|
dev_addr: Some(dev_addr),
|
||||||
})
|
device_session: Some(internal::DeviceSession {
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
device_queue::enqueue_item(device_queue::DeviceQueueItem {
|
|
||||||
dev_eui: dev.dev_eui,
|
|
||||||
f_port: 10,
|
|
||||||
data: vec![1, 2, 3, 4],
|
|
||||||
..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(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: dev_addr.to_vec(),
|
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],
|
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],
|
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||||
@ -261,8 +246,22 @@ async fn test_sns_uplink() {
|
|||||||
rx2_frequency: 869525000,
|
rx2_frequency: 869525000,
|
||||||
region_config_id: "eu868".into(),
|
region_config_id: "eu868".into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
}),
|
||||||
device_session::save(&ds).await.unwrap();
|
..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,
|
||||||
|
data: vec![1, 2, 3, 4],
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut data_phy = lrwn::PhyPayload {
|
let mut data_phy = lrwn::PhyPayload {
|
||||||
mhdr: lrwn::MHDR {
|
mhdr: lrwn::MHDR {
|
||||||
@ -346,7 +345,7 @@ async fn test_sns_uplink() {
|
|||||||
},
|
},
|
||||||
phy_payload: hex::decode("600000000a8005000a54972baa8b983cd1").unwrap(),
|
phy_payload: hex::decode("600000000a8005000a54972baa8b983cd1").unwrap(),
|
||||||
dl_meta_data: Some(backend::DLMetaData {
|
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_1: Some(868.1),
|
||||||
dl_freq_2: Some(869.525),
|
dl_freq_2: Some(869.525),
|
||||||
rx_delay_1: Some(1),
|
rx_delay_1: Some(1),
|
||||||
@ -403,7 +402,7 @@ async fn test_sns_uplink() {
|
|||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
dev_eui: ds.dev_eui.clone(),
|
dev_eui: dev.dev_eui.to_vec(),
|
||||||
nwk_s_key: Some(backend::KeyEnvelope {
|
nwk_s_key: Some(backend::KeyEnvelope {
|
||||||
kek_label: "".to_string(),
|
kek_label: "".to_string(),
|
||||||
aes_key: ds.nwk_s_enc_key.clone(),
|
aes_key: ds.nwk_s_enc_key.clone(),
|
||||||
@ -466,24 +465,18 @@ async fn test_sns_roaming_not_allowed() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.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(),
|
name: "device".into(),
|
||||||
application_id: app.id.clone(),
|
application_id: app.id.clone(),
|
||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::B,
|
enabled_class: DeviceClass::B,
|
||||||
..Default::default()
|
dev_addr: Some(dev_addr),
|
||||||
})
|
device_session: Some(internal::DeviceSession {
|
||||||
.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(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: dev_addr.to_vec(),
|
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],
|
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],
|
s_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||||
@ -499,8 +492,13 @@ async fn test_sns_roaming_not_allowed() {
|
|||||||
rx2_frequency: 869525000,
|
rx2_frequency: 869525000,
|
||||||
region_config_id: "eu868".into(),
|
region_config_id: "eu868".into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
}),
|
||||||
device_session::save(&ds).await.unwrap();
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let ds = dev.get_device_session().unwrap();
|
||||||
|
|
||||||
let mut data_phy = lrwn::PhyPayload {
|
let mut data_phy = lrwn::PhyPayload {
|
||||||
mhdr: lrwn::MHDR {
|
mhdr: lrwn::MHDR {
|
||||||
|
@ -8,16 +8,17 @@ use super::assert;
|
|||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
application,
|
application,
|
||||||
device::{self, DeviceClass},
|
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 crate::{config, gateway::backend as gateway_backend, integration, region, test, uplink};
|
||||||
use chirpstack_api::{common, gw, integration as integration_pb, internal, stream};
|
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 = ()>>>>;
|
type Function = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()>>>>;
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
|
dev_eui: EUI64,
|
||||||
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
||||||
before_func: Option<Function>,
|
before_func: Option<Function>,
|
||||||
after_func: Option<Function>,
|
after_func: Option<Function>,
|
||||||
@ -93,11 +94,28 @@ async fn test_gateway_filtering() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::B,
|
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()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let ds = dev.get_device_session().unwrap();
|
||||||
|
|
||||||
let mut rx_info_a = gw::UplinkRxInfo {
|
let mut rx_info_a = gw::UplinkRxInfo {
|
||||||
gateway_id: gw_a.gateway_id.to_string(),
|
gateway_id: gw_a.gateway_id.to_string(),
|
||||||
location: Some(Default::default()),
|
location: Some(Default::default()),
|
||||||
@ -128,26 +146,10 @@ async fn test_gateway_filtering() {
|
|||||||
};
|
};
|
||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "private gateway of same tenant".into(),
|
name: "private gateway of same tenant".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -174,6 +176,7 @@ async fn test_gateway_filtering() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "private gateway other tenant".into(),
|
name: "private gateway other tenant".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -196,7 +199,7 @@ async fn test_gateway_filtering() {
|
|||||||
}),
|
}),
|
||||||
mic: Some([48, 94, 26, 239]),
|
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(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -293,9 +297,7 @@ async fn test_region_config_id_filtering() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan102.into(),
|
mac_version: common::MacVersion::Lorawan102.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "matching config id".into(),
|
name: "matching config id".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -338,6 +341,7 @@ async fn test_region_config_id_filtering() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "non-matching configuration id".into(),
|
name: "non-matching configuration id".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -410,12 +414,13 @@ async fn test_lorawan_10_errors() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let _dev = device::create(device::Device {
|
let dev = device::create(device::Device {
|
||||||
name: "device".into(),
|
name: "device".into(),
|
||||||
application_id: app.id.clone(),
|
application_id: app.id.clone(),
|
||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -440,9 +445,7 @@ async fn test_lorawan_10_errors() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan102.into(),
|
mac_version: common::MacVersion::Lorawan102.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "invalid frame-counter (did not increment)".into(),
|
name: "invalid frame-counter (did not increment)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -491,6 +495,7 @@ async fn test_lorawan_10_errors() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "invalid frame-counter (reset)".into(),
|
name: "invalid frame-counter (reset)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -522,6 +527,7 @@ async fn test_lorawan_10_errors() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "invalid mic".into(),
|
name: "invalid mic".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -605,12 +611,13 @@ async fn test_lorawan_11_errors() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let _dev = device::create(device::Device {
|
let dev = device::create(device::Device {
|
||||||
name: "device".into(),
|
name: "device".into(),
|
||||||
application_id: app.id.clone(),
|
application_id: app.id.clone(),
|
||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -641,9 +648,7 @@ async fn test_lorawan_11_errors() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_dr, 3).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_dr, 3).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan102.into(),
|
mac_version: common::MacVersion::Lorawan102.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "invalid frequency (MIC)".into(),
|
name: "invalid frequency (MIC)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -686,6 +692,7 @@ async fn test_lorawan_11_errors() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "invalid frequency (MIC)".into(),
|
name: "invalid frequency (MIC)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_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]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
skip_fcnt_check: true,
|
skip_fcnt_check: true,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -789,9 +797,7 @@ async fn test_lorawan_10_skip_f_cnt() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan102.into(),
|
mac_version: common::MacVersion::Lorawan102.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "frame-counter is invalid but not 0".into(),
|
name: "frame-counter is invalid but not 0".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -857,6 +864,7 @@ async fn test_lorawan_10_skip_f_cnt() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "frame-counter is invalid and 0".into(),
|
name: "frame-counter is invalid and 0".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_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]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
is_disabled: true,
|
is_disabled: true,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -982,9 +991,7 @@ async fn test_lorawan_10_device_disabled() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan102.into(),
|
mac_version: common::MacVersion::Lorawan102.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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 {
|
let tests = vec![Test {
|
||||||
name: "uplink ignored".into(),
|
name: "uplink ignored".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -1081,6 +1089,7 @@ async fn test_lorawan_10_uplink() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -1111,9 +1120,7 @@ async fn test_lorawan_10_uplink() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 10).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 10).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink with payload".into(),
|
name: "unconfirmed uplink with payload".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -1184,6 +1192,7 @@ async fn test_lorawan_10_uplink() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink with payload using LR-FHSS dr".into(),
|
name: "unconfirmed uplink with payload using LR-FHSS dr".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@ -1255,6 +1264,7 @@ async fn test_lorawan_10_uplink() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink with payload + ACK".into(),
|
name: "unconfirmed uplink with payload + ACK".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -1333,6 +1343,7 @@ async fn test_lorawan_10_uplink() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink without payload (just FPort)".into(),
|
name: "unconfirmed uplink without payload (just FPort)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -1383,6 +1394,7 @@ async fn test_lorawan_10_uplink() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "confirmed uplink with payload".into(),
|
name: "confirmed uplink with payload".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -1494,6 +1506,7 @@ async fn test_lorawan_10_uplink() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "confirmed uplink without payload".into(),
|
name: "confirmed uplink without payload".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -1604,11 +1617,18 @@ async fn test_lorawan_10_uplink() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "uplink of class-c device updates scheduler_run_after".into(),
|
name: "uplink of class-c device updates scheduler_run_after".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui;
|
let dev_eui = dev.dev_eui;
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
device::set_enabled_class(&dev_eui, device::DeviceClass::C)
|
device::partial_update(
|
||||||
|
dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
|
enabled_class: Some(device::DeviceClass::C),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
})
|
})
|
||||||
@ -1616,7 +1636,13 @@ async fn test_lorawan_10_uplink() {
|
|||||||
after_func: Some(Box::new(move || {
|
after_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui;
|
let dev_eui = dev.dev_eui;
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
device::set_enabled_class(&dev_eui, device::DeviceClass::A)
|
device::partial_update(
|
||||||
|
dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
|
enabled_class: Some(device::DeviceClass::A),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
})
|
})
|
||||||
@ -1700,6 +1726,7 @@ async fn test_lorawan_10_end_to_end_enc() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.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();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||||
|
|
||||||
let ds_sess_key_id = internal::DeviceSession {
|
let ds_sess_key_id = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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 {
|
let ds_app_s_key = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "end-to-end encryption with session key id".into(),
|
name: "end-to-end encryption with session key id".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -1816,6 +1840,7 @@ async fn test_lorawan_10_end_to_end_enc() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "end-to-end encryption with AppSKey".into(),
|
name: "end-to-end encryption with AppSKey".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -1869,6 +1894,7 @@ async fn test_lorawan_10_end_to_end_enc() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "end-to-end encryption using AppSkey + encrypted downlink".into(),
|
name: "end-to-end encryption using AppSkey + encrypted downlink".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -2028,6 +2054,7 @@ async fn test_lorawan_11_uplink() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -2058,9 +2085,7 @@ async fn test_lorawan_11_uplink() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 8).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 8).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan110.into(),
|
mac_version: common::MacVersion::Lorawan110.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink with payload".into(),
|
name: "unconfirmed uplink with payload".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -2132,6 +2158,7 @@ async fn test_lorawan_11_uplink() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink with payload + ACK".into(),
|
name: "unconfirmed uplink with payload + ACK".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -2266,6 +2293,7 @@ async fn test_lorawan_10_rx_delay() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.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();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 8).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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 {
|
let tests = vec![Test {
|
||||||
name: "confirmed uplink without payload (rx_delay = 3)".into(),
|
name: "confirmed uplink without payload (rx_delay = 3)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -2479,6 +2506,7 @@ async fn test_lorawan_10_mac_commands() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.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();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 8).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink + device-status request downlink (FOpts)".into(),
|
name: "unconfirmed uplink + device-status request downlink (FOpts)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dp_id = dp.id.clone();
|
let dp_id = dp.id.clone();
|
||||||
@ -2624,6 +2651,7 @@ async fn test_lorawan_10_mac_commands() {
|
|||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink + device-status request downlink (FOpts) + downlink payload"
|
name: "unconfirmed uplink + device-status request downlink (FOpts) + downlink payload"
|
||||||
.into(),
|
.into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -2721,6 +2749,7 @@ async fn test_lorawan_10_mac_commands() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "RxTimingSetupAns is answered with an empty downlink".into(),
|
name: "RxTimingSetupAns is answered with an empty downlink".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -2848,6 +2877,7 @@ async fn test_lorawan_11_mac_commands() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -2872,9 +2902,7 @@ async fn test_lorawan_11_mac_commands() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan110.into(),
|
mac_version: common::MacVersion::Lorawan110.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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 {
|
let tests = vec![Test {
|
||||||
name: "uplink mac-command (encrypted fopts)".into(),
|
name: "uplink mac-command (encrypted fopts)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -3042,6 +3071,7 @@ async fn test_lorawan_10_device_queue() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -3066,9 +3096,7 @@ async fn test_lorawan_10_device_queue() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink + one unconfirmed downlink payload in queue".into(),
|
name: "unconfirmed uplink + one unconfirmed downlink payload in queue".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -3166,6 +3195,7 @@ async fn test_lorawan_10_device_queue() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink + two unconfirmed downlinks payload in queue".into(),
|
name: "unconfirmed uplink + two unconfirmed downlinks payload in queue".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![
|
device_queue_items: vec![
|
||||||
device_queue::DeviceQueueItem {
|
device_queue::DeviceQueueItem {
|
||||||
id: Uuid::new_v4(),
|
id: Uuid::new_v4(),
|
||||||
@ -3256,6 +3286,7 @@ async fn test_lorawan_10_device_queue() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink + one confirmed downlink payload in queue".into(),
|
name: "unconfirmed uplink + one confirmed downlink payload in queue".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -3334,6 +3365,7 @@ async fn test_lorawan_10_device_queue() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink data + downlink payload which exceeds the max payload size (for dr 0)".into(),
|
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 {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -3370,6 +3402,7 @@ async fn test_lorawan_10_device_queue() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink data + one unconfirmed downlink payload in queue (exactly max size for dr 0) + one mac command".into(),
|
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 {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -3515,6 +3548,7 @@ async fn test_lorawan_11_device_queue() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -3539,9 +3573,7 @@ async fn test_lorawan_11_device_queue() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan110.into(),
|
mac_version: common::MacVersion::Lorawan110.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink + one unconfirmed downlink payload in queue".into(),
|
name: "unconfirmed uplink + one unconfirmed downlink payload in queue".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -3640,6 +3673,7 @@ async fn test_lorawan_11_device_queue() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink + two unconfirmed downlinks payload in queue".into(),
|
name: "unconfirmed uplink + two unconfirmed downlinks payload in queue".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![
|
device_queue_items: vec![
|
||||||
device_queue::DeviceQueueItem {
|
device_queue::DeviceQueueItem {
|
||||||
id: Uuid::new_v4(),
|
id: Uuid::new_v4(),
|
||||||
@ -3730,6 +3764,7 @@ async fn test_lorawan_11_device_queue() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink + one confirmed downlink payload in queue".into(),
|
name: "unconfirmed uplink + one confirmed downlink payload in queue".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -3808,6 +3843,7 @@ async fn test_lorawan_11_device_queue() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink data + downlink payload which exceeds the max payload size (for dr 0)".into(),
|
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 {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -3844,6 +3880,7 @@ async fn test_lorawan_11_device_queue() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "unconfirmed uplink data + one unconfirmed downlink payload in queue (exactly max size for dr 0) + one mac command".into(),
|
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 {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -3992,6 +4029,7 @@ async fn test_lorawan_10_adr() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -4016,9 +4054,7 @@ async fn test_lorawan_10_adr() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "adr triggered".into(),
|
name: "adr triggered".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -4160,6 +4197,7 @@ async fn test_lorawan_10_adr() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "device has adr disabled".into(),
|
name: "device has adr disabled".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -4193,6 +4231,7 @@ async fn test_lorawan_10_adr() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "acknowledgement of pending adr request".into(),
|
name: "acknowledgement of pending adr request".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
@ -4260,6 +4299,7 @@ async fn test_lorawan_10_adr() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "negative acknowledgement of pending adr request".into(),
|
name: "negative acknowledgement of pending adr request".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
@ -4327,6 +4367,7 @@ async fn test_lorawan_10_adr() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "adr ack requested".into(),
|
name: "adr ack requested".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -4403,6 +4444,7 @@ async fn test_lorawan_10_adr() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "channel re-configuration triggered".into(),
|
name: "channel re-configuration triggered".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -4502,6 +4544,7 @@ async fn test_lorawan_10_adr() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "new channel re-configuration ack-ed".into(),
|
name: "new channel re-configuration ack-ed".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
@ -4567,6 +4610,7 @@ async fn test_lorawan_10_adr() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "new channel re-configuration not ack-ed".into(),
|
name: "new channel re-configuration not ack-ed".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
@ -4634,6 +4678,7 @@ async fn test_lorawan_10_adr() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "channel re-configuration and adr triggered".into(),
|
name: "channel re-configuration and adr triggered".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -4738,6 +4783,7 @@ async fn test_lorawan_10_adr() {
|
|||||||
// adr backoff triggered
|
// adr backoff triggered
|
||||||
Test {
|
Test {
|
||||||
name: "adr backoff triggered".into(),
|
name: "adr backoff triggered".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -4835,6 +4881,7 @@ async fn test_lorawan_10_device_status_request() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -4859,9 +4906,7 @@ async fn test_lorawan_10_device_status_request() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "must request device-status".into(),
|
name: "must request device-status".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -4963,6 +5009,7 @@ async fn test_lorawan_10_device_status_request() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "interval has not yet expired".into(),
|
name: "interval has not yet expired".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -4993,6 +5040,7 @@ async fn test_lorawan_10_device_status_request() {
|
|||||||
// reporting device-status
|
// reporting device-status
|
||||||
Test {
|
Test {
|
||||||
name: "reporting device-status".into(),
|
name: "reporting device-status".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
@ -5098,6 +5146,7 @@ async fn test_lorawan_11_receive_window_selection() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.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();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info_lr_fhss, 8).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan110.into(),
|
mac_version: common::MacVersion::Lorawan110.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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 {
|
run_test(&Test {
|
||||||
name: "unconfirmed uplink with payload (rx1)".into(),
|
name: "unconfirmed uplink with payload (rx1)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -5224,6 +5272,7 @@ async fn test_lorawan_11_receive_window_selection() {
|
|||||||
|
|
||||||
run_test(&Test {
|
run_test(&Test {
|
||||||
name: "unconfirmed uplink with payload (rx2)".into(),
|
name: "unconfirmed uplink with payload (rx2)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -5293,6 +5342,7 @@ async fn test_lorawan_11_receive_window_selection() {
|
|||||||
|
|
||||||
run_test(&Test {
|
run_test(&Test {
|
||||||
name: "unconfirmed uplink with payload (rx1 + rx2)".into(),
|
name: "unconfirmed uplink with payload (rx1 + rx2)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -5391,6 +5441,7 @@ async fn test_lorawan_11_receive_window_selection() {
|
|||||||
|
|
||||||
run_test(&Test {
|
run_test(&Test {
|
||||||
name: "unconfirmed uplink with payload (rx1, payload exceeds rx2 limit)".into(),
|
name: "unconfirmed uplink with payload (rx1, payload exceeds rx2 limit)".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -5497,7 +5548,6 @@ async fn test_lorawan_11_receive_window_selection() {
|
|||||||
|
|
||||||
async fn run_test(t: &Test) {
|
async fn run_test(t: &Test) {
|
||||||
println!("> {}", t.name);
|
println!("> {}", t.name);
|
||||||
|
|
||||||
reset_redis().await.unwrap();
|
reset_redis().await.unwrap();
|
||||||
|
|
||||||
integration::set_mock().await;
|
integration::set_mock().await;
|
||||||
@ -5506,12 +5556,16 @@ async fn run_test(t: &Test) {
|
|||||||
integration::mock::reset().await;
|
integration::mock::reset().await;
|
||||||
gateway_backend::mock::reset().await;
|
gateway_backend::mock::reset().await;
|
||||||
|
|
||||||
if let Some(ds) = &t.device_session {
|
device_queue::flush_for_dev_eui(&t.dev_eui).await.unwrap();
|
||||||
let _ = device_session::save(&ds).await.unwrap();
|
device::partial_update(
|
||||||
|
t.dev_eui,
|
||||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
&device::DeviceChangeset {
|
||||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
device_session: Some(t.device_session.clone()),
|
||||||
}
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if let Some(f) = &t.before_func {
|
if let Some(f) = &t.before_func {
|
||||||
f().await;
|
f().await;
|
||||||
|
@ -5,7 +5,7 @@ use crate::gpstime::ToGpsTime;
|
|||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
application,
|
application,
|
||||||
device::{self, DeviceClass},
|
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::{
|
use crate::{
|
||||||
config, downlink, downlink::classb, gateway::backend as gateway_backend, integration, test,
|
config, downlink, downlink::classb, gateway::backend as gateway_backend, integration, test,
|
||||||
@ -16,6 +16,7 @@ use lrwn::{DevAddr, EUI64};
|
|||||||
|
|
||||||
struct UplinkTest {
|
struct UplinkTest {
|
||||||
name: String,
|
name: String,
|
||||||
|
dev_eui: EUI64,
|
||||||
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
||||||
device_session: Option<internal::DeviceSession>,
|
device_session: Option<internal::DeviceSession>,
|
||||||
tx_info: gw::UplinkTxInfo,
|
tx_info: gw::UplinkTxInfo,
|
||||||
@ -26,6 +27,7 @@ struct UplinkTest {
|
|||||||
|
|
||||||
struct DownlinkTest {
|
struct DownlinkTest {
|
||||||
name: String,
|
name: String,
|
||||||
|
dev_eui: EUI64,
|
||||||
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
||||||
device_session: Option<internal::DeviceSession>,
|
device_session: Option<internal::DeviceSession>,
|
||||||
device_gateway_rx_info: Option<internal::DeviceGatewayRxInfo>,
|
device_gateway_rx_info: Option<internal::DeviceGatewayRxInfo>,
|
||||||
@ -80,6 +82,7 @@ async fn test_uplink() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -103,9 +106,7 @@ async fn test_uplink() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 0).unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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
|
// trigger beacon locked
|
||||||
run_uplink_test(&UplinkTest {
|
run_uplink_test(&UplinkTest {
|
||||||
name: "trigger beacon locked".into(),
|
name: "trigger beacon locked".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
device_session: Some(ds.clone()),
|
device_session: Some(ds.clone()),
|
||||||
tx_info: tx_info.clone(),
|
tx_info: tx_info.clone(),
|
||||||
@ -164,6 +166,7 @@ async fn test_uplink() {
|
|||||||
// trigger beacon unlocked
|
// trigger beacon unlocked
|
||||||
run_uplink_test(&UplinkTest {
|
run_uplink_test(&UplinkTest {
|
||||||
name: "trigger beacon locked".into(),
|
name: "trigger beacon locked".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![],
|
device_queue_items: vec![],
|
||||||
device_session: Some(ds.clone()),
|
device_session: Some(ds.clone()),
|
||||||
tx_info: tx_info.clone(),
|
tx_info: tx_info.clone(),
|
||||||
@ -244,15 +247,14 @@ async fn test_downlink_scheduler() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::B,
|
enabled_class: DeviceClass::B,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
let ds = internal::DeviceSession {
|
||||||
dev_eui: vec![2, 2, 3, 4, 5, 6, 7, 8],
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
join_eui: vec![8, 7, 6, 5, 4, 3, 2, 1],
|
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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],
|
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],
|
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 {
|
run_scheduler_test(&DownlinkTest {
|
||||||
name: "class-b downlink".into(),
|
name: "class-b downlink".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -342,6 +345,7 @@ async fn test_downlink_scheduler() {
|
|||||||
|
|
||||||
run_scheduler_test(&DownlinkTest {
|
run_scheduler_test(&DownlinkTest {
|
||||||
name: "scheduler_run_after has not yet expired".into(),
|
name: "scheduler_run_after has not yet expired".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -356,12 +360,19 @@ async fn test_downlink_scheduler() {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// remove the schedule run after
|
// remove the schedule run after
|
||||||
device::set_scheduler_run_after(&dev.dev_eui.clone(), None)
|
device::partial_update(
|
||||||
|
dev.dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
|
scheduler_run_after: Some(None),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
run_scheduler_test(&DownlinkTest {
|
run_scheduler_test(&DownlinkTest {
|
||||||
name: "class-b downlink with more data".into(),
|
name: "class-b downlink with more data".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![
|
device_queue_items: vec![
|
||||||
device_queue::DeviceQueueItem {
|
device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
@ -434,12 +445,16 @@ async fn run_uplink_test(t: &UplinkTest) {
|
|||||||
integration::mock::reset().await;
|
integration::mock::reset().await;
|
||||||
gateway_backend::mock::reset().await;
|
gateway_backend::mock::reset().await;
|
||||||
|
|
||||||
if let Some(ds) = &t.device_session {
|
device_queue::flush_for_dev_eui(&t.dev_eui).await.unwrap();
|
||||||
let _ = device_session::save(&ds).await.unwrap();
|
device::partial_update(
|
||||||
|
t.dev_eui,
|
||||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
&device::DeviceChangeset {
|
||||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
device_session: Some(t.device_session.clone()),
|
||||||
}
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
for qi in &t.device_queue_items {
|
for qi in &t.device_queue_items {
|
||||||
let _ = device_queue::enqueue_item(qi.clone()).await.unwrap();
|
let _ = device_queue::enqueue_item(qi.clone()).await.unwrap();
|
||||||
@ -471,13 +486,16 @@ async fn run_scheduler_test(t: &DownlinkTest) {
|
|||||||
|
|
||||||
integration::mock::reset().await;
|
integration::mock::reset().await;
|
||||||
gateway_backend::mock::reset().await;
|
gateway_backend::mock::reset().await;
|
||||||
|
device_queue::flush_for_dev_eui(&t.dev_eui).await.unwrap();
|
||||||
if let Some(ds) = &t.device_session {
|
device::partial_update(
|
||||||
let _ = device_session::save(&ds).await.unwrap();
|
t.dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
device_session: Some(t.device_session.clone()),
|
||||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
..Default::default()
|
||||||
}
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if let Some(rx_info) = &t.device_gateway_rx_info {
|
if let Some(rx_info) = &t.device_gateway_rx_info {
|
||||||
let _ = device_gateway::save_rx_info(rx_info).await.unwrap();
|
let _ = device_gateway::save_rx_info(rx_info).await.unwrap();
|
||||||
|
@ -4,14 +4,15 @@ use super::assert;
|
|||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
application,
|
application,
|
||||||
device::{self, DeviceClass},
|
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 crate::{downlink, gateway::backend as gateway_backend, integration, test};
|
||||||
use chirpstack_api::{common, gw, internal};
|
use chirpstack_api::{common, gw, internal};
|
||||||
use lrwn::EUI64;
|
use lrwn::{DevAddr, EUI64};
|
||||||
|
|
||||||
struct DownlinkTest {
|
struct DownlinkTest {
|
||||||
name: String,
|
name: String,
|
||||||
|
dev_eui: EUI64,
|
||||||
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
device_queue_items: Vec<device_queue::DeviceQueueItem>,
|
||||||
device_session: Option<internal::DeviceSession>,
|
device_session: Option<internal::DeviceSession>,
|
||||||
device_gateway_rx_info: Option<internal::DeviceGatewayRxInfo>,
|
device_gateway_rx_info: Option<internal::DeviceGatewayRxInfo>,
|
||||||
@ -71,6 +72,7 @@ async fn test_downlink_scheduler() {
|
|||||||
device_profile_id: dp.id.clone(),
|
device_profile_id: dp.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
enabled_class: DeviceClass::C,
|
enabled_class: DeviceClass::C,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 2, 3, 4])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -86,10 +88,8 @@ async fn test_downlink_scheduler() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let ds = internal::DeviceSession {
|
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],
|
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],
|
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],
|
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],
|
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 {
|
run_scheduler_test(&DownlinkTest {
|
||||||
name: "device has not yet sent an uplink".into(),
|
name: "device has not yet sent an uplink".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -126,12 +127,19 @@ async fn test_downlink_scheduler() {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// remove the schedule run after
|
// remove the schedule run after
|
||||||
device::set_scheduler_run_after(&dev.dev_eui.clone(), None)
|
device::partial_update(
|
||||||
|
dev.dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
|
scheduler_run_after: Some(None),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
run_scheduler_test(&DownlinkTest {
|
run_scheduler_test(&DownlinkTest {
|
||||||
name: "unconfirmed data".into(),
|
name: "unconfirmed data".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -178,6 +186,7 @@ async fn test_downlink_scheduler() {
|
|||||||
|
|
||||||
run_scheduler_test(&DownlinkTest {
|
run_scheduler_test(&DownlinkTest {
|
||||||
name: "scheduler_run_after has not yet expired".into(),
|
name: "scheduler_run_after has not yet expired".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -192,12 +201,19 @@ async fn test_downlink_scheduler() {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// remove the schedule run after
|
// remove the schedule run after
|
||||||
device::set_scheduler_run_after(&dev.dev_eui.clone(), None)
|
device::partial_update(
|
||||||
|
dev.dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
|
scheduler_run_after: Some(None),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
run_scheduler_test(&DownlinkTest {
|
run_scheduler_test(&DownlinkTest {
|
||||||
name: "unconfirmed data".into(),
|
name: "unconfirmed data".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -246,12 +262,19 @@ async fn test_downlink_scheduler() {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// remove the schedule run after
|
// remove the schedule run after
|
||||||
device::set_scheduler_run_after(&dev.dev_eui.clone(), None)
|
device::partial_update(
|
||||||
|
dev.dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
|
scheduler_run_after: Some(None),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
run_scheduler_test(&DownlinkTest {
|
run_scheduler_test(&DownlinkTest {
|
||||||
name: "unconfirmed data".into(),
|
name: "unconfirmed data".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
device_queue_items: vec![device_queue::DeviceQueueItem {
|
device_queue_items: vec![device_queue::DeviceQueueItem {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
dev_eui: dev.dev_eui.clone(),
|
dev_eui: dev.dev_eui.clone(),
|
||||||
@ -276,13 +299,16 @@ async fn run_scheduler_test(t: &DownlinkTest) {
|
|||||||
|
|
||||||
integration::mock::reset().await;
|
integration::mock::reset().await;
|
||||||
gateway_backend::mock::reset().await;
|
gateway_backend::mock::reset().await;
|
||||||
|
device_queue::flush_for_dev_eui(&t.dev_eui).await.unwrap();
|
||||||
if let Some(ds) = &t.device_session {
|
device::partial_update(
|
||||||
let _ = device_session::save(&ds).await.unwrap();
|
t.dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
device_session: Some(t.device_session.clone()),
|
||||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
..Default::default()
|
||||||
}
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if let Some(rx_info) = &t.device_gateway_rx_info {
|
if let Some(rx_info) = &t.device_gateway_rx_info {
|
||||||
let _ = device_gateway::save_rx_info(rx_info).await.unwrap();
|
let _ = device_gateway::save_rx_info(rx_info).await.unwrap();
|
||||||
|
@ -156,8 +156,6 @@ async fn test_js() {
|
|||||||
dev.dev_eui.clone(),
|
dev.dev_eui.clone(),
|
||||||
internal::DeviceSession {
|
internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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(),
|
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],
|
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],
|
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(),
|
dev.dev_eui.clone(),
|
||||||
internal::DeviceSession {
|
internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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(),
|
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],
|
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],
|
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(),
|
dev.dev_eui.clone(),
|
||||||
internal::DeviceSession {
|
internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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(),
|
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],
|
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],
|
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::{
|
use crate::storage::{
|
||||||
application,
|
application,
|
||||||
device::{self, DeviceClass},
|
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 crate::{config, gateway::backend as gateway_backend, integration, region, test, uplink};
|
||||||
use chirpstack_api::{common, gw, internal, stream};
|
use chirpstack_api::{common, gw, internal, stream};
|
||||||
@ -19,6 +19,7 @@ type Function = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()>>>>;
|
|||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
|
dev_eui: EUI64,
|
||||||
before_func: Option<Function>,
|
before_func: Option<Function>,
|
||||||
after_func: Option<Function>,
|
after_func: Option<Function>,
|
||||||
tx_info: gw::UplinkTxInfo,
|
tx_info: gw::UplinkTxInfo,
|
||||||
@ -153,6 +154,7 @@ async fn test_gateway_filtering() {
|
|||||||
let tests = vec![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "private gateway of same tenant".into(),
|
name: "private gateway of same tenant".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@ -168,8 +170,6 @@ async fn test_gateway_filtering() {
|
|||||||
dev.dev_eui.clone(),
|
dev.dev_eui.clone(),
|
||||||
internal::DeviceSession {
|
internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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(),
|
mac_version: common::MacVersion::Lorawan102.into(),
|
||||||
f_nwk_s_int_key: vec![
|
f_nwk_s_int_key: vec![
|
||||||
128, 47, 168, 41, 62, 215, 212, 79, 19, 83, 183, 201, 43, 169, 125, 200,
|
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 {
|
Test {
|
||||||
name: "private gateway other tenant".into(),
|
name: "private gateway other tenant".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@ -375,6 +376,7 @@ async fn test_lorawan_10() {
|
|||||||
let tests = vec![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "dev-nonce already used".into(),
|
name: "dev-nonce already used".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
rx_info: rx_info.clone(),
|
rx_info: rx_info.clone(),
|
||||||
@ -387,6 +389,7 @@ async fn test_lorawan_10() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "join-request accepted".into(),
|
name: "join-request accepted".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@ -407,8 +410,6 @@ async fn test_lorawan_10() {
|
|||||||
dev.dev_eui.clone(),
|
dev.dev_eui.clone(),
|
||||||
internal::DeviceSession {
|
internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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(),
|
mac_version: common::MacVersion::Lorawan102.into(),
|
||||||
f_nwk_s_int_key: vec![
|
f_nwk_s_int_key: vec![
|
||||||
128, 47, 168, 41, 62, 215, 212, 79, 19, 83, 183, 201, 43, 169, 125, 200,
|
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 {
|
Test {
|
||||||
name: "join-request accepted + skip fcnt check".into(),
|
name: "join-request accepted + skip fcnt check".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@ -602,8 +604,6 @@ async fn test_lorawan_10() {
|
|||||||
dev.dev_eui.clone(),
|
dev.dev_eui.clone(),
|
||||||
internal::DeviceSession {
|
internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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(),
|
mac_version: common::MacVersion::Lorawan102.into(),
|
||||||
f_nwk_s_int_key: vec![
|
f_nwk_s_int_key: vec![
|
||||||
128, 47, 168, 41, 62, 215, 212, 79, 19, 83, 183, 201, 43, 169, 125, 200,
|
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 {
|
Test {
|
||||||
name: "join-request accepted + cflist".into(),
|
name: "join-request accepted + cflist".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@ -649,8 +650,6 @@ async fn test_lorawan_10() {
|
|||||||
dev.dev_eui.clone(),
|
dev.dev_eui.clone(),
|
||||||
internal::DeviceSession {
|
internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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(),
|
mac_version: common::MacVersion::Lorawan102.into(),
|
||||||
f_nwk_s_int_key: vec![
|
f_nwk_s_int_key: vec![
|
||||||
128, 47, 168, 41, 62, 215, 212, 79, 19, 83, 183, 201, 43, 169, 125, 200,
|
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 {
|
Test {
|
||||||
name: "join-request accepted + class-b supported".into(),
|
name: "join-request accepted + class-b supported".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
let dp_id = dp.id.clone();
|
let dp_id = dp.id.clone();
|
||||||
@ -813,6 +813,7 @@ async fn test_lorawan_10() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "join-request accepted + class-c supported".into(),
|
name: "join-request accepted + class-c supported".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
let dp_id = dp.id.clone();
|
let dp_id = dp.id.clone();
|
||||||
@ -840,6 +841,7 @@ async fn test_lorawan_10() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "device disabled".into(),
|
name: "device disabled".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@ -997,6 +999,7 @@ async fn test_lorawan_11() {
|
|||||||
let tests = vec![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "dev-nonce already used".into(),
|
name: "dev-nonce already used".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: None,
|
before_func: None,
|
||||||
after_func: None,
|
after_func: None,
|
||||||
rx_info: rx_info.clone(),
|
rx_info: rx_info.clone(),
|
||||||
@ -1009,6 +1012,7 @@ async fn test_lorawan_11() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "join-request accepted".into(),
|
name: "join-request accepted".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@ -1025,8 +1029,6 @@ async fn test_lorawan_11() {
|
|||||||
dev.dev_eui.clone(),
|
dev.dev_eui.clone(),
|
||||||
internal::DeviceSession {
|
internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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(),
|
mac_version: common::MacVersion::Lorawan110.into(),
|
||||||
f_nwk_s_int_key: vec![
|
f_nwk_s_int_key: vec![
|
||||||
98, 222, 198, 158, 98, 155, 205, 235, 143, 171, 203, 19, 221, 9, 1, 231,
|
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 {
|
Test {
|
||||||
name: "join-request accepted + class-c supported".into(),
|
name: "join-request accepted + class-c supported".into(),
|
||||||
|
dev_eui: dev.dev_eui,
|
||||||
before_func: Some(Box::new(move || {
|
before_func: Some(Box::new(move || {
|
||||||
let dev_eui = dev.dev_eui.clone();
|
let dev_eui = dev.dev_eui.clone();
|
||||||
let dp_id = dp.id.clone();
|
let dp_id = dp.id.clone();
|
||||||
@ -1224,8 +1227,6 @@ async fn test_lorawan_11() {
|
|||||||
async fn run_test(t: &Test) {
|
async fn run_test(t: &Test) {
|
||||||
println!("> {}", t.name);
|
println!("> {}", t.name);
|
||||||
|
|
||||||
reset_redis().await.unwrap();
|
|
||||||
|
|
||||||
let mut conf: config::Configuration = (*config::get()).clone();
|
let mut conf: config::Configuration = (*config::get()).clone();
|
||||||
for f in &t.extra_uplink_channels {
|
for f in &t.extra_uplink_channels {
|
||||||
conf.regions[0]
|
conf.regions[0]
|
||||||
@ -1246,6 +1247,17 @@ async fn run_test(t: &Test) {
|
|||||||
integration::mock::reset().await;
|
integration::mock::reset().await;
|
||||||
gateway_backend::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 {
|
if let Some(f) = &t.before_func {
|
||||||
f().await;
|
f().await;
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,16 @@ use super::assert;
|
|||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
application,
|
application,
|
||||||
device::{self, DeviceClass},
|
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 crate::{gateway::backend as gateway_backend, integration, test, uplink};
|
||||||
use chirpstack_api::{common, gw, integration as integration_pb, internal};
|
use chirpstack_api::{common, gw, integration as integration_pb, internal};
|
||||||
use lrwn::{AES128Key, EUI64};
|
use lrwn::{AES128Key, DevAddr, EUI64};
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
|
dev_eui_relay: EUI64,
|
||||||
|
dev_eui_relay_ed: EUI64,
|
||||||
device_queue_items_relay_ed: Vec<device_queue::DeviceQueueItem>,
|
device_queue_items_relay_ed: Vec<device_queue::DeviceQueueItem>,
|
||||||
device_session_relay: Option<internal::DeviceSession>,
|
device_session_relay: Option<internal::DeviceSession>,
|
||||||
device_session_relay_ed: 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,
|
device_profile_id: dp_relay.id,
|
||||||
dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 1]),
|
dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 1]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([1, 1, 1, 1])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -96,6 +99,7 @@ async fn test_lorawan_10() {
|
|||||||
device_profile_id: dp_relay_ed.id,
|
device_profile_id: dp_relay_ed.id,
|
||||||
dev_eui: EUI64::from_be_bytes([2, 2, 2, 2, 2, 2, 2, 2]),
|
dev_eui: EUI64::from_be_bytes([2, 2, 2, 2, 2, 2, 2, 2]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
|
dev_addr: Some(DevAddr::from_be_bytes([2, 2, 2, 2])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -120,7 +124,6 @@ async fn test_lorawan_10() {
|
|||||||
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 5).unwrap();
|
uplink::helpers::set_uplink_modulation(&"eu868", &mut tx_info, 5).unwrap();
|
||||||
|
|
||||||
let ds_relay = internal::DeviceSession {
|
let ds_relay = internal::DeviceSession {
|
||||||
dev_eui: dev_relay.dev_eui.to_vec(),
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
dev_addr: vec![1, 1, 1, 1],
|
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],
|
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 {
|
let ds_relay_ed = internal::DeviceSession {
|
||||||
dev_eui: dev_relay_ed.dev_eui.to_vec(),
|
|
||||||
mac_version: common::MacVersion::Lorawan104.into(),
|
mac_version: common::MacVersion::Lorawan104.into(),
|
||||||
dev_addr: vec![2, 2, 2, 2],
|
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],
|
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![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
name: "relayed unconfirmed uplink".into(),
|
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_queue_items_relay_ed: vec![],
|
||||||
device_session_relay: Some(ds_relay.clone()),
|
device_session_relay: Some(ds_relay.clone()),
|
||||||
device_session_relay_ed: Some(ds_relay_ed.clone()),
|
device_session_relay_ed: Some(ds_relay_ed.clone()),
|
||||||
@ -503,6 +507,8 @@ async fn test_lorawan_10() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "relayed confirmed uplink".into(),
|
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_queue_items_relay_ed: vec![],
|
||||||
device_session_relay: Some(ds_relay.clone()),
|
device_session_relay: Some(ds_relay.clone()),
|
||||||
device_session_relay_ed: Some(ds_relay_ed.clone()),
|
device_session_relay_ed: Some(ds_relay_ed.clone()),
|
||||||
@ -627,6 +633,8 @@ async fn test_lorawan_10() {
|
|||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
name: "relayed unconfirmed uplink + adr_ack_req".into(),
|
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_queue_items_relay_ed: vec![],
|
||||||
device_session_relay: Some(ds_relay.clone()),
|
device_session_relay: Some(ds_relay.clone()),
|
||||||
device_session_relay_ed: Some(ds_relay_ed.clone()),
|
device_session_relay_ed: Some(ds_relay_ed.clone()),
|
||||||
@ -765,20 +773,30 @@ async fn run_test(t: &Test) {
|
|||||||
|
|
||||||
integration::mock::reset().await;
|
integration::mock::reset().await;
|
||||||
gateway_backend::mock::reset().await;
|
gateway_backend::mock::reset().await;
|
||||||
|
device_queue::flush_for_dev_eui(&t.dev_eui_relay)
|
||||||
if let Some(ds) = &t.device_session_relay {
|
.await
|
||||||
let _ = device_session::save(&ds).await.unwrap();
|
.unwrap();
|
||||||
|
device_queue::flush_for_dev_eui(&t.dev_eui_relay_ed)
|
||||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
.await
|
||||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
.unwrap();
|
||||||
}
|
device::partial_update(
|
||||||
|
t.dev_eui_relay,
|
||||||
if let Some(ds) = &t.device_session_relay_ed {
|
&device::DeviceChangeset {
|
||||||
let _ = device_session::save(&ds).await.unwrap();
|
device_session: Some(t.device_session_relay.clone()),
|
||||||
|
..Default::default()
|
||||||
let dev_eui = EUI64::from_slice(&ds.dev_eui).unwrap();
|
},
|
||||||
device_queue::flush_for_dev_eui(&dev_eui).await.unwrap();
|
)
|
||||||
}
|
.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 {
|
for qi in &t.device_queue_items_relay_ed {
|
||||||
let _ = device_queue::enqueue_item(qi.clone()).await.unwrap();
|
let _ = device_queue::enqueue_item(qi.clone()).await.unwrap();
|
||||||
|
@ -6,11 +6,11 @@ use super::assert;
|
|||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
application,
|
application,
|
||||||
device::{self, DeviceClass},
|
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 crate::{gateway::backend as gateway_backend, integration, test, uplink};
|
||||||
use chirpstack_api::{common, gw, internal};
|
use chirpstack_api::{common, gw, internal};
|
||||||
use lrwn::{AES128Key, EUI64};
|
use lrwn::{AES128Key, DevAddr, EUI64};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_lorawan_10() {
|
async fn test_lorawan_10() {
|
||||||
@ -96,13 +96,8 @@ async fn test_lorawan_10() {
|
|||||||
device_profile_id: dp_relay.id.clone(),
|
device_profile_id: dp_relay.id.clone(),
|
||||||
dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 2]),
|
dev_eui: EUI64::from_be_bytes([1, 1, 1, 1, 1, 1, 1, 2]),
|
||||||
enabled_class: DeviceClass::A,
|
enabled_class: DeviceClass::A,
|
||||||
..Default::default()
|
dev_addr: Some(DevAddr::from_be_bytes([4, 3, 2, 1])),
|
||||||
})
|
device_session: Some(internal::DeviceSession {
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let ds_relay = internal::DeviceSession {
|
|
||||||
dev_eui: dev_relay.dev_eui.to_vec(),
|
|
||||||
mac_version: common::MacVersion::Lorawan102.into(),
|
mac_version: common::MacVersion::Lorawan102.into(),
|
||||||
dev_addr: vec![4, 3, 2, 1],
|
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],
|
f_nwk_s_int_key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||||
@ -114,8 +109,13 @@ async fn test_lorawan_10() {
|
|||||||
rx2_frequency: 869525000,
|
rx2_frequency: 869525000,
|
||||||
region_config_id: "eu868".into(),
|
region_config_id: "eu868".into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
}),
|
||||||
device_session::save(&ds_relay).await.unwrap();
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let ds_relay = dev_relay.get_device_session().unwrap();
|
||||||
|
|
||||||
let mut rx_info = gw::UplinkRxInfo {
|
let mut rx_info = gw::UplinkRxInfo {
|
||||||
gateway_id: gw.gateway_id.to_string(),
|
gateway_id: gw.gateway_id.to_string(),
|
||||||
@ -268,8 +268,6 @@ async fn test_lorawan_10() {
|
|||||||
dev.dev_eui.clone(),
|
dev.dev_eui.clone(),
|
||||||
internal::DeviceSession {
|
internal::DeviceSession {
|
||||||
dev_addr: vec![1, 2, 3, 4],
|
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(),
|
mac_version: common::MacVersion::Lorawan102.into(),
|
||||||
f_nwk_s_int_key: vec![
|
f_nwk_s_int_key: vec![
|
||||||
146, 184, 94, 251, 180, 89, 48, 96, 236, 112, 106, 181, 94, 25, 215, 162,
|
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::{
|
use crate::storage::{
|
||||||
application,
|
application,
|
||||||
device::{self, DeviceClass},
|
device::{self, DeviceClass},
|
||||||
device_gateway, device_profile, device_queue, device_session, fields,
|
device_gateway, device_profile, device_queue, fields,
|
||||||
helpers::get_all_device_data,
|
helpers::get_all_device_data,
|
||||||
metrics, tenant,
|
metrics, tenant,
|
||||||
};
|
};
|
||||||
@ -40,7 +40,6 @@ pub struct Data {
|
|||||||
retransmission: bool,
|
retransmission: bool,
|
||||||
f_cnt_up_full: u32,
|
f_cnt_up_full: u32,
|
||||||
tenant: Option<tenant::Tenant>,
|
tenant: Option<tenant::Tenant>,
|
||||||
device_session: Option<internal::DeviceSession>,
|
|
||||||
device: Option<device::Device>,
|
device: Option<device::Device>,
|
||||||
device_profile: Option<device_profile::DeviceProfile>,
|
device_profile: Option<device_profile::DeviceProfile>,
|
||||||
application: Option<application::Application>,
|
application: Option<application::Application>,
|
||||||
@ -50,6 +49,7 @@ pub struct Data {
|
|||||||
must_send_downlink: bool,
|
must_send_downlink: bool,
|
||||||
downlink_mac_commands: Vec<lrwn::MACCommandSet>,
|
downlink_mac_commands: Vec<lrwn::MACCommandSet>,
|
||||||
device_gateway_rx_info: Option<internal::DeviceGatewayRxInfo>,
|
device_gateway_rx_info: Option<internal::DeviceGatewayRxInfo>,
|
||||||
|
device_changeset: device::DeviceChangeset,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
@ -99,7 +99,6 @@ impl Data {
|
|||||||
reset: false,
|
reset: false,
|
||||||
retransmission: false,
|
retransmission: false,
|
||||||
tenant: None,
|
tenant: None,
|
||||||
device_session: None,
|
|
||||||
device: None,
|
device: None,
|
||||||
device_profile: None,
|
device_profile: None,
|
||||||
application: None,
|
application: None,
|
||||||
@ -109,10 +108,11 @@ impl Data {
|
|||||||
must_send_downlink: false,
|
must_send_downlink: false,
|
||||||
downlink_mac_commands: Vec::new(),
|
downlink_mac_commands: Vec::new(),
|
||||||
device_gateway_rx_info: None,
|
device_gateway_rx_info: None,
|
||||||
|
device_changeset: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.handle_passive_roaming_device().await?;
|
ctx.handle_passive_roaming_device().await?;
|
||||||
ctx.get_device_session().await?;
|
ctx.get_device_for_phy_payload().await?;
|
||||||
ctx.get_device_data().await?;
|
ctx.get_device_data().await?;
|
||||||
ctx.check_roaming_allowed()?;
|
ctx.check_roaming_allowed()?;
|
||||||
|
|
||||||
@ -120,17 +120,16 @@ impl Data {
|
|||||||
let span = tracing::Span::current();
|
let span = tracing::Span::current();
|
||||||
span.record("dev_eui", ctx.device.as_ref().unwrap().dev_eui.to_string());
|
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() {
|
if !ctx._is_roaming() {
|
||||||
// In case of roaming we do not know the gateways and therefore it must not be
|
// In case of roaming we do not know the gateways and therefore it must not be
|
||||||
// filtered.
|
// filtered.
|
||||||
ctx.filter_rx_info_by_tenant().await?;
|
ctx.filter_rx_info_by_tenant().await?;
|
||||||
ctx.filter_rx_info_by_region_config_id()?;
|
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_f_opts_mac_commands()?;
|
||||||
ctx.decrypt_frm_payload()?;
|
ctx.decrypt_frm_payload()?;
|
||||||
ctx.log_uplink_frame_set().await?;
|
ctx.log_uplink_frame_set().await?;
|
||||||
@ -148,7 +147,7 @@ impl Data {
|
|||||||
ctx.detect_and_save_measurements().await?;
|
ctx.detect_and_save_measurements().await?;
|
||||||
ctx.sync_uplink_f_cnt()?;
|
ctx.sync_uplink_f_cnt()?;
|
||||||
ctx.set_region_config_id()?;
|
ctx.set_region_config_id()?;
|
||||||
ctx.save_device_session().await?;
|
ctx.update_device().await?;
|
||||||
ctx.handle_uplink_ack().await?;
|
ctx.handle_uplink_ack().await?;
|
||||||
ctx.save_metrics().await?;
|
ctx.save_metrics().await?;
|
||||||
|
|
||||||
@ -175,7 +174,6 @@ impl Data {
|
|||||||
reset: false,
|
reset: false,
|
||||||
retransmission: false,
|
retransmission: false,
|
||||||
tenant: None,
|
tenant: None,
|
||||||
device_session: None,
|
|
||||||
device: None,
|
device: None,
|
||||||
device_profile: None,
|
device_profile: None,
|
||||||
application: None,
|
application: None,
|
||||||
@ -184,11 +182,11 @@ impl Data {
|
|||||||
uplink_event: None,
|
uplink_event: None,
|
||||||
must_send_downlink: false,
|
must_send_downlink: false,
|
||||||
downlink_mac_commands: Vec::new(),
|
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.get_device_data().await?;
|
||||||
ctx.abort_on_device_is_disabled().await?;
|
|
||||||
ctx.set_device_info()?;
|
ctx.set_device_info()?;
|
||||||
ctx.set_relay_rx_info()?;
|
ctx.set_relay_rx_info()?;
|
||||||
ctx.handle_retransmission_reset().await?;
|
ctx.handle_retransmission_reset().await?;
|
||||||
@ -204,7 +202,7 @@ impl Data {
|
|||||||
ctx.detect_and_save_measurements().await?;
|
ctx.detect_and_save_measurements().await?;
|
||||||
ctx.sync_uplink_f_cnt()?;
|
ctx.sync_uplink_f_cnt()?;
|
||||||
ctx.set_region_config_id()?;
|
ctx.set_region_config_id()?;
|
||||||
ctx.save_device_session().await?;
|
ctx.update_device().await?;
|
||||||
ctx.handle_uplink_ack().await?;
|
ctx.handle_uplink_ack().await?;
|
||||||
ctx.save_metrics_relayed().await?;
|
ctx.save_metrics_relayed().await?;
|
||||||
ctx.start_downlink_data_flow_relayed().await?;
|
ctx.start_downlink_data_flow_relayed().await?;
|
||||||
@ -230,8 +228,8 @@ impl Data {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_device_session(&mut self) -> Result<(), Error> {
|
async fn get_device_for_phy_payload(&mut self) -> Result<(), Error> {
|
||||||
trace!("Getting device-session for dev_addr");
|
trace!("Getting device for PhyPayload");
|
||||||
|
|
||||||
let dev_addr = if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
let dev_addr = if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
||||||
pl.fhdr.devaddr
|
pl.fhdr.devaddr
|
||||||
@ -239,7 +237,7 @@ impl Data {
|
|||||||
return Err(Error::AnyhowError(anyhow!("No MacPayload in PhyPayload")));
|
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,
|
false,
|
||||||
&mut self.phy_payload,
|
&mut self.phy_payload,
|
||||||
self.uplink_frame_set.dr,
|
self.uplink_frame_set.dr,
|
||||||
@ -248,18 +246,18 @@ impl Data {
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(v) => match v {
|
Ok(v) => match v {
|
||||||
device_session::ValidationStatus::Ok(f_cnt, ds) => {
|
device::ValidationStatus::Ok(f_cnt, d) => {
|
||||||
self.device_session = Some(ds);
|
self.device = Some(d);
|
||||||
self.f_cnt_up_full = f_cnt;
|
self.f_cnt_up_full = f_cnt;
|
||||||
}
|
}
|
||||||
device_session::ValidationStatus::Retransmission(f_cnt, ds) => {
|
device::ValidationStatus::Retransmission(f_cnt, d) => {
|
||||||
self.retransmission = true;
|
self.retransmission = true;
|
||||||
self.device_session = Some(ds);
|
self.device = Some(d);
|
||||||
self.f_cnt_up_full = f_cnt;
|
self.f_cnt_up_full = f_cnt;
|
||||||
}
|
}
|
||||||
device_session::ValidationStatus::Reset(f_cnt, ds) => {
|
device::ValidationStatus::Reset(f_cnt, d) => {
|
||||||
self.reset = true;
|
self.reset = true;
|
||||||
self.device_session = Some(ds);
|
self.device = Some(d);
|
||||||
self.f_cnt_up_full = f_cnt;
|
self.f_cnt_up_full = f_cnt;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -289,8 +287,8 @@ impl Data {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_device_session_relayed(&mut self) -> Result<(), Error> {
|
async fn get_device_for_phy_payload_relayed(&mut self) -> Result<(), Error> {
|
||||||
trace!("Getting device-session for dev_addr (relayed)");
|
trace!("Getting device for PhyPayload (relayed)");
|
||||||
|
|
||||||
let relay_ctx = self.relay_context.as_ref().unwrap();
|
let relay_ctx = self.relay_context.as_ref().unwrap();
|
||||||
|
|
||||||
@ -307,27 +305,22 @@ impl Data {
|
|||||||
dr,
|
dr,
|
||||||
)? as u8;
|
)? as u8;
|
||||||
|
|
||||||
match device_session::get_for_phypayload_and_incr_f_cnt_up(
|
match device::get_for_phypayload_and_incr_f_cnt_up(true, &mut self.phy_payload, dr, ch)
|
||||||
true,
|
|
||||||
&mut self.phy_payload,
|
|
||||||
dr,
|
|
||||||
ch,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(v) => match v {
|
Ok(v) => match v {
|
||||||
device_session::ValidationStatus::Ok(f_cnt, ds) => {
|
device::ValidationStatus::Ok(f_cnt, d) => {
|
||||||
self.device_session = Some(ds);
|
self.device = Some(d);
|
||||||
self.f_cnt_up_full = f_cnt;
|
self.f_cnt_up_full = f_cnt;
|
||||||
}
|
}
|
||||||
device_session::ValidationStatus::Retransmission(f_cnt, ds) => {
|
device::ValidationStatus::Retransmission(f_cnt, d) => {
|
||||||
self.retransmission = true;
|
self.retransmission = true;
|
||||||
self.device_session = Some(ds);
|
self.device = Some(d);
|
||||||
self.f_cnt_up_full = f_cnt;
|
self.f_cnt_up_full = f_cnt;
|
||||||
}
|
}
|
||||||
device_session::ValidationStatus::Reset(f_cnt, ds) => {
|
device::ValidationStatus::Reset(f_cnt, d) => {
|
||||||
self.reset = true;
|
self.reset = true;
|
||||||
self.device_session = Some(ds);
|
self.device = Some(d);
|
||||||
self.f_cnt_up_full = f_cnt;
|
self.f_cnt_up_full = f_cnt;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -353,8 +346,9 @@ impl Data {
|
|||||||
|
|
||||||
async fn get_device_data(&mut self) -> Result<()> {
|
async fn get_device_data(&mut self) -> Result<()> {
|
||||||
trace!("Getting device data");
|
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 {
|
if dp.region != self.uplink_frame_set.region_common_name {
|
||||||
return Err(anyhow!("Invalid device-profile region"));
|
return Err(anyhow!("Invalid device-profile region"));
|
||||||
@ -363,7 +357,6 @@ impl Data {
|
|||||||
self.tenant = Some(t);
|
self.tenant = Some(t);
|
||||||
self.application = Some(app);
|
self.application = Some(app);
|
||||||
self.device_profile = Some(dp);
|
self.device_profile = Some(dp);
|
||||||
self.device = Some(dev);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -425,9 +418,10 @@ impl Data {
|
|||||||
|
|
||||||
fn set_device_gateway_rx_info(&mut self) -> Result<()> {
|
fn set_device_gateway_rx_info(&mut self) -> Result<()> {
|
||||||
trace!("Setting gateway rx-info for device");
|
trace!("Setting gateway rx-info for device");
|
||||||
|
let d = self.device.as_ref().unwrap();
|
||||||
|
|
||||||
self.device_gateway_rx_info = Some(internal::DeviceGatewayRxInfo {
|
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,
|
dr: self.uplink_frame_set.dr as u32,
|
||||||
items: self
|
items: self
|
||||||
.uplink_frame_set
|
.uplink_frame_set
|
||||||
@ -469,24 +463,6 @@ impl Data {
|
|||||||
Ok(())
|
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> {
|
async fn handle_retransmission_reset(&self) -> Result<(), Error> {
|
||||||
trace!("Handle retransmission and reset");
|
trace!("Handle retransmission and reset");
|
||||||
let dev = self.device.as_ref().unwrap();
|
let dev = self.device.as_ref().unwrap();
|
||||||
@ -555,7 +531,13 @@ impl Data {
|
|||||||
if dev.scheduler_run_after.is_none()
|
if dev.scheduler_run_after.is_none()
|
||||||
|| scheduler_run_after > dev.scheduler_run_after.unwrap()
|
|| scheduler_run_after > dev.scheduler_run_after.unwrap()
|
||||||
{
|
{
|
||||||
*dev = device::set_scheduler_run_after(&dev.dev_eui, Some(scheduler_run_after))
|
*dev = device::partial_update(
|
||||||
|
dev.dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
|
scheduler_run_after: Some(Some(scheduler_run_after)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -575,9 +557,15 @@ impl Data {
|
|||||||
// Restore the device-session in case of an error (no gateways available).
|
// Restore the device-session in case of an error (no gateways available).
|
||||||
// This is because during the fcnt validation, we immediately store the
|
// This is because during the fcnt validation, we immediately store the
|
||||||
// device-session with incremented fcnt to avoid race conditions.
|
// device-session with incremented fcnt to avoid race conditions.
|
||||||
device_session::save(self.device_session.as_ref().unwrap())
|
let d = self.device.as_ref().unwrap();
|
||||||
.await
|
device::partial_update(
|
||||||
.context("Save device-session")?;
|
d.dev_eui,
|
||||||
|
&device::DeviceChangeset {
|
||||||
|
device_session: Some(d.device_session.clone()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Err(v)
|
Err(v)
|
||||||
}
|
}
|
||||||
@ -597,7 +585,7 @@ impl Data {
|
|||||||
|
|
||||||
fn decrypt_f_opts_mac_commands(&mut self) -> Result<()> {
|
fn decrypt_f_opts_mac_commands(&mut self) -> Result<()> {
|
||||||
trace!("Decrypting mac-commands");
|
trace!("Decrypting mac-commands");
|
||||||
let ds = self.device_session.as_ref().unwrap();
|
let ds = self.device.as_ref().unwrap().get_device_session()?;
|
||||||
if ds.mac_version().to_string().starts_with("1.0") {
|
if ds.mac_version().to_string().starts_with("1.0") {
|
||||||
if let Err(e) = self.phy_payload.decode_f_opts_to_mac_commands() {
|
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.
|
// This avoids failing in case of a corrupted mac-command in the frm_payload.
|
||||||
@ -616,7 +604,7 @@ impl Data {
|
|||||||
|
|
||||||
fn decrypt_frm_payload(&mut self) -> Result<()> {
|
fn decrypt_frm_payload(&mut self) -> Result<()> {
|
||||||
trace!("Decrypting FRMPayload");
|
trace!("Decrypting FRMPayload");
|
||||||
let ds = self.device_session.as_ref().unwrap();
|
let ds = self.device.as_ref().unwrap().get_device_session()?;
|
||||||
let mut f_port = 0;
|
let mut f_port = 0;
|
||||||
|
|
||||||
if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
||||||
@ -656,7 +644,7 @@ impl Data {
|
|||||||
|
|
||||||
fn set_adr(&mut self) -> Result<()> {
|
fn set_adr(&mut self) -> Result<()> {
|
||||||
trace!("Set ADR flag in device-session");
|
trace!("Set ADR flag in device-session");
|
||||||
let ds = self.device_session.as_mut().unwrap();
|
let ds = self.device.as_mut().unwrap().get_device_session_mut()?;
|
||||||
if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
||||||
ds.adr = pl.fhdr.f_ctrl.adr;
|
ds.adr = pl.fhdr.f_ctrl.adr;
|
||||||
}
|
}
|
||||||
@ -666,9 +654,13 @@ impl Data {
|
|||||||
async fn set_uplink_data_rate(&mut self) -> Result<()> {
|
async fn set_uplink_data_rate(&mut self) -> Result<()> {
|
||||||
trace!("Set uplink data-rate and reset tx-power on change");
|
trace!("Set uplink data-rate and reset tx-power on change");
|
||||||
let device = self.device.as_mut().unwrap();
|
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
|
// 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
|
// 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.
|
// and the uplink history at the network-server side too.
|
||||||
@ -677,6 +669,7 @@ impl Data {
|
|||||||
ds.uplink_adr_history = Vec::new();
|
ds.uplink_adr_history = Vec::new();
|
||||||
}
|
}
|
||||||
ds.dr = self.uplink_frame_set.dr as u32;
|
ds.dr = self.uplink_frame_set.dr as u32;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,9 +677,13 @@ impl Data {
|
|||||||
trace!("Set relayed uplink data-rate and reset tx-power on change");
|
trace!("Set relayed uplink data-rate and reset tx-power on change");
|
||||||
let device = self.device.as_mut().unwrap();
|
let device = self.device.as_mut().unwrap();
|
||||||
let relay_ctx = self.relay_context.as_ref().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
|
// 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
|
// 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.
|
// and the uplink history at the network-server side too.
|
||||||
@ -717,7 +714,7 @@ impl Data {
|
|||||||
|
|
||||||
// Update if the enabled class has changed.
|
// Update if the enabled class has changed.
|
||||||
if dev.enabled_class != enabled_class {
|
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(())
|
Ok(())
|
||||||
@ -770,11 +767,12 @@ impl Data {
|
|||||||
// device did not reset these).
|
// device did not reset these).
|
||||||
fn reset_channels_on_adr_ack_req(&mut self) -> Result<()> {
|
fn reset_channels_on_adr_ack_req(&mut self) -> Result<()> {
|
||||||
trace!("Reset channels on adr ack req");
|
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 let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
||||||
if pl.fhdr.f_ctrl.adr_ack_req {
|
if pl.fhdr.f_ctrl.adr_ack_req {
|
||||||
let region_conf = region::get(&self.uplink_frame_set.region_config_id)?;
|
let region_conf = region::get(&self.uplink_frame_set.region_config_id)?;
|
||||||
let ds = self.device_session.as_mut().unwrap();
|
let ds = d.get_device_session_mut()?;
|
||||||
|
|
||||||
// We reset the device-session enabled_uplink_channel_indices and
|
// We reset the device-session enabled_uplink_channel_indices and
|
||||||
// extra_uplink_channels. On the downlink path, the mac-command handling will
|
// extra_uplink_channels. On the downlink path, the mac-command handling will
|
||||||
@ -804,8 +802,7 @@ impl Data {
|
|||||||
self.tenant.as_ref().unwrap(),
|
self.tenant.as_ref().unwrap(),
|
||||||
self.application.as_ref().unwrap(),
|
self.application.as_ref().unwrap(),
|
||||||
self.device_profile.as_ref().unwrap(),
|
self.device_profile.as_ref().unwrap(),
|
||||||
self.device.as_ref().unwrap(),
|
self.device.as_mut().unwrap(),
|
||||||
self.device_session.as_mut().unwrap(),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("Handle uplink mac-commands")?;
|
.context("Handle uplink mac-commands")?;
|
||||||
@ -822,8 +819,7 @@ impl Data {
|
|||||||
self.tenant.as_ref().unwrap(),
|
self.tenant.as_ref().unwrap(),
|
||||||
self.application.as_ref().unwrap(),
|
self.application.as_ref().unwrap(),
|
||||||
self.device_profile.as_ref().unwrap(),
|
self.device_profile.as_ref().unwrap(),
|
||||||
self.device.as_ref().unwrap(),
|
self.device.as_mut().unwrap(),
|
||||||
self.device_session.as_mut().unwrap(),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("Handle uplink mac-commands")?;
|
.context("Handle uplink mac-commands")?;
|
||||||
@ -847,7 +843,7 @@ impl Data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn append_meta_data_to_uplink_history(&mut self) -> Result<()> {
|
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().get_device_session_mut()?;
|
||||||
|
|
||||||
// ignore re-transmissions we don't know the source of the
|
// ignore re-transmissions we don't know the source of the
|
||||||
// re-transmission (it might be a replay-attack)
|
// re-transmission (it might be a replay-attack)
|
||||||
@ -892,7 +888,7 @@ impl Data {
|
|||||||
fn append_meta_data_to_uplink_history_relayed(&mut self) -> Result<()> {
|
fn append_meta_data_to_uplink_history_relayed(&mut self) -> Result<()> {
|
||||||
trace!("Apping meta-data of relayed uplink to upink history");
|
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().get_device_session_mut()?;
|
||||||
let relay_ctx = self.relay_context.as_ref().unwrap();
|
let relay_ctx = self.relay_context.as_ref().unwrap();
|
||||||
|
|
||||||
// ignore re-transmissions we don't know the source of the
|
// ignore re-transmissions we don't know the source of the
|
||||||
@ -929,7 +925,7 @@ impl Data {
|
|||||||
let app = self.application.as_ref().unwrap();
|
let app = self.application.as_ref().unwrap();
|
||||||
let dp = self.device_profile.as_ref().unwrap();
|
let dp = self.device_profile.as_ref().unwrap();
|
||||||
let dev = self.device.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 {
|
let mac = if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
|
||||||
pl
|
pl
|
||||||
} else {
|
} else {
|
||||||
@ -1092,7 +1088,8 @@ impl Data {
|
|||||||
// required.
|
// required.
|
||||||
fn sync_uplink_f_cnt(&mut self) -> Result<()> {
|
fn sync_uplink_f_cnt(&mut self) -> Result<()> {
|
||||||
trace!("Syncing uplink frame-counter");
|
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;
|
ds.f_cnt_up = self.f_cnt_up_full + 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1102,16 +1099,19 @@ impl Data {
|
|||||||
// value is not set initially.
|
// value is not set initially.
|
||||||
fn set_region_config_id(&mut self) -> Result<()> {
|
fn set_region_config_id(&mut self) -> Result<()> {
|
||||||
trace!("Setting region_config_id to device-session");
|
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();
|
ds.region_config_id = self.uplink_frame_set.region_config_id.clone();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save_device_session(&self) -> Result<()> {
|
async fn update_device(&mut self) -> Result<()> {
|
||||||
trace!("Saving device-session");
|
trace!("Updating device");
|
||||||
device_session::save(self.device_session.as_ref().unwrap())
|
|
||||||
.await
|
let d = self.device.as_mut().unwrap();
|
||||||
.context("Save device-session")?;
|
self.device_changeset.device_session = Some(d.device_session.clone());
|
||||||
|
|
||||||
|
*d = device::partial_update(d.dev_eui, &self.device_changeset).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1265,7 +1265,6 @@ impl Data {
|
|||||||
self.application.as_ref().cloned().unwrap(),
|
self.application.as_ref().cloned().unwrap(),
|
||||||
self.device_profile.as_ref().cloned().unwrap(),
|
self.device_profile.as_ref().cloned().unwrap(),
|
||||||
self.device.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,
|
pl.fhdr.f_ctrl.adr_ack_req || self.must_send_downlink,
|
||||||
self.phy_payload.mhdr.m_type == lrwn::MType::ConfirmedDataUp,
|
self.phy_payload.mhdr.m_type == lrwn::MType::ConfirmedDataUp,
|
||||||
self.downlink_mac_commands.clone(),
|
self.downlink_mac_commands.clone(),
|
||||||
@ -1291,7 +1290,6 @@ impl Data {
|
|||||||
self.application.as_ref().cloned().unwrap(),
|
self.application.as_ref().cloned().unwrap(),
|
||||||
self.device_profile.as_ref().cloned().unwrap(),
|
self.device_profile.as_ref().cloned().unwrap(),
|
||||||
self.device.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,
|
pl.fhdr.f_ctrl.adr_ack_req || self.must_send_downlink,
|
||||||
self.phy_payload.mhdr.m_type == lrwn::MType::ConfirmedDataUp,
|
self.phy_payload.mhdr.m_type == lrwn::MType::ConfirmedDataUp,
|
||||||
self.downlink_mac_commands.clone(),
|
self.downlink_mac_commands.clone(),
|
||||||
@ -1314,7 +1312,6 @@ impl Data {
|
|||||||
req: pl.clone(),
|
req: pl.clone(),
|
||||||
device: self.device.as_ref().unwrap().clone(),
|
device: self.device.as_ref().unwrap().clone(),
|
||||||
device_profile: self.device_profile.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
|
must_ack: self.phy_payload.mhdr.m_type
|
||||||
== lrwn::MType::ConfirmedDataUp,
|
== lrwn::MType::ConfirmedDataUp,
|
||||||
must_send_downlink: relay_pl.fhdr.f_ctrl.adr_ack_req,
|
must_send_downlink: relay_pl.fhdr.f_ctrl.adr_ack_req,
|
||||||
@ -1329,7 +1326,6 @@ impl Data {
|
|||||||
req: pl.clone(),
|
req: pl.clone(),
|
||||||
device: self.device.as_ref().unwrap().clone(),
|
device: self.device.as_ref().unwrap().clone(),
|
||||||
device_profile: self.device_profile.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
|
must_ack: self.phy_payload.mhdr.m_type
|
||||||
== lrwn::MType::ConfirmedDataUp,
|
== lrwn::MType::ConfirmedDataUp,
|
||||||
must_send_downlink: relay_pl.fhdr.f_ctrl.adr_ack_req,
|
must_send_downlink: relay_pl.fhdr.f_ctrl.adr_ack_req,
|
||||||
@ -1369,7 +1365,10 @@ impl Data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn _is_end_to_end_encrypted(&self) -> bool {
|
fn _is_end_to_end_encrypted(&self) -> bool {
|
||||||
let ds = self.device_session.as_ref().unwrap();
|
let ds = match self.device.as_ref().unwrap().get_device_session() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
if !ds.js_session_key_id.is_empty() {
|
if !ds.js_session_key_id.is_empty() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -6,8 +6,8 @@ use chrono::{DateTime, Local, Utc};
|
|||||||
use tracing::{error, info, span, trace, warn, Instrument, Level};
|
use tracing::{error, info, span, trace, warn, Instrument, Level};
|
||||||
|
|
||||||
use lrwn::{
|
use lrwn::{
|
||||||
keys, AES128Key, CFList, DLSettings, DevAddr, JoinAcceptPayload, JoinRequestPayload, JoinType,
|
keys, AES128Key, CFList, DLSettings, JoinAcceptPayload, JoinRequestPayload, JoinType, MType,
|
||||||
MType, Major, Payload, PhyPayload, MHDR,
|
Major, Payload, PhyPayload, MHDR,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::error::Error;
|
use super::error::Error;
|
||||||
@ -20,7 +20,6 @@ use super::{
|
|||||||
use crate::api::{backend::get_async_receiver, helpers::ToProto};
|
use crate::api::{backend::get_async_receiver, helpers::ToProto};
|
||||||
use crate::backend::{joinserver, keywrap, roaming};
|
use crate::backend::{joinserver, keywrap, roaming};
|
||||||
use crate::helpers::errors::PrintFullError;
|
use crate::helpers::errors::PrintFullError;
|
||||||
use crate::storage::device_session;
|
|
||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
application,
|
application,
|
||||||
device::{self, DeviceClass},
|
device::{self, DeviceClass},
|
||||||
@ -40,12 +39,10 @@ pub struct JoinRequest {
|
|||||||
join_request: Option<JoinRequestPayload>,
|
join_request: Option<JoinRequestPayload>,
|
||||||
join_accept: Option<PhyPayload>,
|
join_accept: Option<PhyPayload>,
|
||||||
device: Option<device::Device>,
|
device: Option<device::Device>,
|
||||||
device_session: Option<internal::DeviceSession>,
|
|
||||||
application: Option<application::Application>,
|
application: Option<application::Application>,
|
||||||
tenant: Option<tenant::Tenant>,
|
tenant: Option<tenant::Tenant>,
|
||||||
device_profile: Option<device_profile::DeviceProfile>,
|
device_profile: Option<device_profile::DeviceProfile>,
|
||||||
device_keys: Option<device_keys::DeviceKeys>,
|
device_keys: Option<device_keys::DeviceKeys>,
|
||||||
dev_addr: Option<DevAddr>,
|
|
||||||
device_info: Option<integration_pb::DeviceInfo>,
|
device_info: Option<integration_pb::DeviceInfo>,
|
||||||
relay_rx_info: Option<integration_pb::UplinkRelayRxInfo>,
|
relay_rx_info: Option<integration_pb::UplinkRelayRxInfo>,
|
||||||
f_nwk_s_int_key: Option<AES128Key>,
|
f_nwk_s_int_key: Option<AES128Key>,
|
||||||
@ -96,12 +93,10 @@ impl JoinRequest {
|
|||||||
js_client: None,
|
js_client: None,
|
||||||
join_request: None,
|
join_request: None,
|
||||||
device: None,
|
device: None,
|
||||||
device_session: None,
|
|
||||||
application: None,
|
application: None,
|
||||||
tenant: None,
|
tenant: None,
|
||||||
device_profile: None,
|
device_profile: None,
|
||||||
device_keys: None,
|
device_keys: None,
|
||||||
dev_addr: None,
|
|
||||||
join_accept: None,
|
join_accept: None,
|
||||||
device_info: None,
|
device_info: None,
|
||||||
relay_rx_info: None,
|
relay_rx_info: None,
|
||||||
@ -130,7 +125,7 @@ impl JoinRequest {
|
|||||||
ctx.abort_on_relay_only_comm()?;
|
ctx.abort_on_relay_only_comm()?;
|
||||||
ctx.log_uplink_frame_set().await?;
|
ctx.log_uplink_frame_set().await?;
|
||||||
ctx.abort_on_otaa_is_disabled()?;
|
ctx.abort_on_otaa_is_disabled()?;
|
||||||
ctx.get_random_dev_addr()?;
|
ctx.set_random_dev_addr()?;
|
||||||
if ctx.js_client.is_some() {
|
if ctx.js_client.is_some() {
|
||||||
// Using join-server
|
// Using join-server
|
||||||
ctx.get_join_accept_from_js().await?;
|
ctx.get_join_accept_from_js().await?;
|
||||||
@ -141,11 +136,10 @@ impl JoinRequest {
|
|||||||
ctx.construct_join_accept_and_set_keys()?;
|
ctx.construct_join_accept_and_set_keys()?;
|
||||||
}
|
}
|
||||||
ctx.log_uplink_meta().await?;
|
ctx.log_uplink_meta().await?;
|
||||||
ctx.create_device_session().await?;
|
ctx.set_device_session().await?;
|
||||||
ctx.flush_device_queue().await?;
|
ctx.flush_device_queue().await?;
|
||||||
ctx.set_device_mode().await?;
|
ctx.set_device_mode().await?;
|
||||||
ctx.set_dev_addr().await?;
|
ctx.update_device().await?;
|
||||||
ctx.set_join_eui().await?;
|
|
||||||
ctx.start_downlink_join_accept_flow().await?;
|
ctx.start_downlink_join_accept_flow().await?;
|
||||||
ctx.send_join_event().await?;
|
ctx.send_join_event().await?;
|
||||||
|
|
||||||
@ -159,12 +153,10 @@ impl JoinRequest {
|
|||||||
js_client: None,
|
js_client: None,
|
||||||
join_request: None,
|
join_request: None,
|
||||||
device: None,
|
device: None,
|
||||||
device_session: None,
|
|
||||||
application: None,
|
application: None,
|
||||||
tenant: None,
|
tenant: None,
|
||||||
device_profile: None,
|
device_profile: None,
|
||||||
device_keys: None,
|
device_keys: None,
|
||||||
dev_addr: None,
|
|
||||||
join_accept: None,
|
join_accept: None,
|
||||||
device_info: None,
|
device_info: None,
|
||||||
relay_rx_info: None,
|
relay_rx_info: None,
|
||||||
@ -183,7 +175,7 @@ impl JoinRequest {
|
|||||||
ctx.abort_on_device_is_disabled()?;
|
ctx.abort_on_device_is_disabled()?;
|
||||||
ctx.abort_on_otaa_is_disabled()?;
|
ctx.abort_on_otaa_is_disabled()?;
|
||||||
ctx.abort_on_relay_only_comm()?;
|
ctx.abort_on_relay_only_comm()?;
|
||||||
ctx.get_random_dev_addr()?;
|
ctx.set_random_dev_addr()?;
|
||||||
if ctx.js_client.is_some() {
|
if ctx.js_client.is_some() {
|
||||||
// Using join-server
|
// Using join-server
|
||||||
ctx.get_join_accept_from_js().await?;
|
ctx.get_join_accept_from_js().await?;
|
||||||
@ -193,11 +185,10 @@ impl JoinRequest {
|
|||||||
ctx.validate_dev_nonce_and_get_device_keys().await?;
|
ctx.validate_dev_nonce_and_get_device_keys().await?;
|
||||||
ctx.construct_join_accept_and_set_keys()?;
|
ctx.construct_join_accept_and_set_keys()?;
|
||||||
}
|
}
|
||||||
ctx.create_device_session().await?;
|
ctx.set_device_session().await?;
|
||||||
ctx.flush_device_queue().await?;
|
ctx.flush_device_queue().await?;
|
||||||
ctx.set_device_mode().await?;
|
ctx.set_device_mode().await?;
|
||||||
ctx.set_dev_addr().await?;
|
ctx.update_device().await?;
|
||||||
ctx.set_join_eui().await?;
|
|
||||||
ctx.start_downlink_join_accept_flow_relayed().await?;
|
ctx.start_downlink_join_accept_flow_relayed().await?;
|
||||||
ctx.send_join_event().await?;
|
ctx.send_join_event().await?;
|
||||||
|
|
||||||
@ -514,8 +505,10 @@ impl JoinRequest {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_random_dev_addr(&mut self) -> Result<()> {
|
fn set_random_dev_addr(&mut self) -> Result<()> {
|
||||||
self.dev_addr = Some(get_random_dev_addr());
|
trace!("Setting random DevAddr");
|
||||||
|
let d = self.device.as_mut().unwrap();
|
||||||
|
d.dev_addr = Some(get_random_dev_addr());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,7 +543,7 @@ impl JoinRequest {
|
|||||||
mac_version: dp.mac_version.to_string(),
|
mac_version: dp.mac_version.to_string(),
|
||||||
phy_payload: phy_b,
|
phy_payload: phy_b,
|
||||||
dev_eui: dev.dev_eui.to_vec(),
|
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(),
|
dl_settings: dl_settings.to_le_bytes()?.to_vec(),
|
||||||
rx_delay: region_network.rx1_delay,
|
rx_delay: region_network.rx1_delay,
|
||||||
cf_list: match region_conf.get_cf_list(dp.mac_version) {
|
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 region_conf = region::get(&self.uplink_frame_set.region_config_id)?;
|
||||||
let join_request = self.join_request.as_ref().unwrap();
|
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 dk = self.device_keys.as_mut().unwrap();
|
||||||
|
|
||||||
let join_nonce = dk.join_nonce - 1; // this was incremented on validation
|
let join_nonce = dk.join_nonce - 1; // this was incremented on validation
|
||||||
@ -643,7 +637,7 @@ impl JoinRequest {
|
|||||||
payload: Payload::JoinAccept(JoinAcceptPayload {
|
payload: Payload::JoinAccept(JoinAcceptPayload {
|
||||||
join_nonce: join_nonce as u32,
|
join_nonce: join_nonce as u32,
|
||||||
home_netid: conf.network.net_id,
|
home_netid: conf.network.net_id,
|
||||||
devaddr: self.dev_addr.unwrap(),
|
devaddr: d.dev_addr.unwrap(),
|
||||||
dl_settings: DLSettings {
|
dl_settings: DLSettings {
|
||||||
opt_neg,
|
opt_neg,
|
||||||
rx2_dr: region_network.rx2_dr,
|
rx2_dr: region_network.rx2_dr,
|
||||||
@ -768,21 +762,18 @@ impl JoinRequest {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_device_session(&mut self) -> Result<()> {
|
async fn set_device_session(&mut self) -> Result<()> {
|
||||||
trace!("Creating device-session");
|
trace!("Setting device-session");
|
||||||
|
|
||||||
let region_conf = region::get(&self.uplink_frame_set.region_config_id)?;
|
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 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 device_profile = self.device_profile.as_ref().unwrap();
|
||||||
let join_request = self.join_request.as_ref().unwrap();
|
|
||||||
|
|
||||||
let mut ds = internal::DeviceSession {
|
let mut ds = internal::DeviceSession {
|
||||||
region_config_id: self.uplink_frame_set.region_config_id.clone(),
|
region_config_id: self.uplink_frame_set.region_config_id.clone(),
|
||||||
dev_eui: device.dev_eui.to_be_bytes().to_vec(),
|
dev_addr: device.dev_addr.unwrap().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(),
|
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(),
|
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(),
|
nwk_s_enc_key: self.nwk_s_enc_key.as_ref().unwrap().to_vec(),
|
||||||
@ -847,11 +838,7 @@ impl JoinRequest {
|
|||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
device_session::save(&ds)
|
device.device_session = Some(ds);
|
||||||
.await
|
|
||||||
.context("Saving device-session failed")?;
|
|
||||||
|
|
||||||
self.device_session = Some(ds);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -870,30 +857,36 @@ impl JoinRequest {
|
|||||||
|
|
||||||
async fn set_device_mode(&mut self) -> Result<()> {
|
async fn set_device_mode(&mut self) -> Result<()> {
|
||||||
let dp = self.device_profile.as_ref().unwrap();
|
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.
|
// 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") {
|
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 {
|
} else {
|
||||||
*device = device::set_enabled_class(&device.dev_eui, DeviceClass::A).await?;
|
d.enabled_class = DeviceClass::A;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_dev_addr(&mut self) -> Result<()> {
|
async fn update_device(&mut self) -> Result<()> {
|
||||||
trace!("Setting DevAddr");
|
trace!("Updating device");
|
||||||
let dev = self.device.as_mut().unwrap();
|
|
||||||
*dev = device::set_dev_addr(dev.dev_eui, self.dev_addr.unwrap()).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();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -904,7 +897,6 @@ impl JoinRequest {
|
|||||||
&self.uplink_frame_set,
|
&self.uplink_frame_set,
|
||||||
self.tenant.as_ref().unwrap(),
|
self.tenant.as_ref().unwrap(),
|
||||||
self.device.as_ref().unwrap(),
|
self.device.as_ref().unwrap(),
|
||||||
self.device_session.as_ref().unwrap(),
|
|
||||||
self.join_accept.as_ref().unwrap(),
|
self.join_accept.as_ref().unwrap(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -918,7 +910,6 @@ impl JoinRequest {
|
|||||||
&self.uplink_frame_set,
|
&self.uplink_frame_set,
|
||||||
self.tenant.as_ref().unwrap(),
|
self.tenant.as_ref().unwrap(),
|
||||||
self.device.as_ref().unwrap(),
|
self.device.as_ref().unwrap(),
|
||||||
self.device_session.as_ref().unwrap(),
|
|
||||||
self.join_accept.as_ref().unwrap(),
|
self.join_accept.as_ref().unwrap(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -939,7 +930,7 @@ impl JoinRequest {
|
|||||||
time: Some(ts.into()),
|
time: Some(ts.into()),
|
||||||
device_info: self.device_info.clone(),
|
device_info: self.device_info.clone(),
|
||||||
relay_rx_info: self.relay_rx_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() {
|
join_server_context: if !self.js_session_key_id.is_empty() {
|
||||||
Some(common::JoinServerContext {
|
Some(common::JoinServerContext {
|
||||||
app_s_key: None,
|
app_s_key: None,
|
||||||
|
@ -10,7 +10,7 @@ use crate::backend::{joinserver, keywrap, roaming};
|
|||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
application,
|
application,
|
||||||
device::{self, DeviceClass},
|
device::{self, DeviceClass},
|
||||||
device_keys, device_profile, device_queue, device_session,
|
device_keys, device_profile, device_queue,
|
||||||
error::Error as StorageError,
|
error::Error as StorageError,
|
||||||
helpers::get_all_device_data,
|
helpers::get_all_device_data,
|
||||||
metrics, tenant,
|
metrics, tenant,
|
||||||
@ -28,7 +28,6 @@ pub struct JoinRequest {
|
|||||||
join_request: Option<lrwn::JoinRequestPayload>,
|
join_request: Option<lrwn::JoinRequestPayload>,
|
||||||
join_accept: Option<lrwn::PhyPayload>,
|
join_accept: Option<lrwn::PhyPayload>,
|
||||||
device: Option<device::Device>,
|
device: Option<device::Device>,
|
||||||
device_session: Option<internal::DeviceSession>,
|
|
||||||
js_client: Option<Arc<backend::Client>>,
|
js_client: Option<Arc<backend::Client>>,
|
||||||
application: Option<application::Application>,
|
application: Option<application::Application>,
|
||||||
tenant: Option<tenant::Tenant>,
|
tenant: Option<tenant::Tenant>,
|
||||||
@ -66,7 +65,6 @@ impl JoinRequest {
|
|||||||
join_request: None,
|
join_request: None,
|
||||||
join_accept: None,
|
join_accept: None,
|
||||||
device: None,
|
device: None,
|
||||||
device_session: None,
|
|
||||||
js_client: None,
|
js_client: None,
|
||||||
application: None,
|
application: None,
|
||||||
tenant: None,
|
tenant: None,
|
||||||
@ -99,10 +97,9 @@ impl JoinRequest {
|
|||||||
ctx.construct_join_accept_and_set_keys()?;
|
ctx.construct_join_accept_and_set_keys()?;
|
||||||
}
|
}
|
||||||
ctx.log_uplink_meta().await?;
|
ctx.log_uplink_meta().await?;
|
||||||
ctx.create_device_session().await?;
|
ctx.set_device_session().await?;
|
||||||
ctx.flush_device_queue().await?;
|
ctx.flush_device_queue().await?;
|
||||||
ctx.set_device_mode().await?;
|
ctx.update_device().await?;
|
||||||
ctx.set_join_eui().await?;
|
|
||||||
ctx.send_join_event().await?;
|
ctx.send_join_event().await?;
|
||||||
ctx.set_pr_start_ans_payload()?;
|
ctx.set_pr_start_ans_payload()?;
|
||||||
|
|
||||||
@ -562,21 +559,18 @@ impl JoinRequest {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_device_session(&mut self) -> Result<()> {
|
async fn set_device_session(&mut self) -> Result<()> {
|
||||||
trace!("Creating device-session");
|
trace!("Setting device-session");
|
||||||
|
|
||||||
let region_conf = region::get(&self.uplink_frame_set.region_config_id)?;
|
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 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 device_profile = self.device_profile.as_ref().unwrap();
|
||||||
let join_request = self.join_request.as_ref().unwrap();
|
|
||||||
|
|
||||||
let mut ds = internal::DeviceSession {
|
let mut ds = internal::DeviceSession {
|
||||||
region_config_id: self.uplink_frame_set.region_config_id.clone(),
|
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(),
|
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(),
|
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(),
|
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(),
|
nwk_s_enc_key: self.nwk_s_enc_key.as_ref().unwrap().to_vec(),
|
||||||
@ -627,11 +621,7 @@ impl JoinRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
device_session::save(&ds)
|
device.device_session = Some(ds);
|
||||||
.await
|
|
||||||
.context("Saving device-session failed")?;
|
|
||||||
|
|
||||||
self.device_session = Some(ds);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -648,25 +638,31 @@ impl JoinRequest {
|
|||||||
Ok(())
|
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 dp = self.device_profile.as_ref().unwrap();
|
||||||
let device = self.device.as_mut().unwrap();
|
let ds = self.device.as_ref().unwrap().get_device_session()?;
|
||||||
|
|
||||||
// LoRaWAN 1.1 devices send a mac-command when changing to Class-C.
|
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") {
|
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?;
|
DeviceClass::C
|
||||||
} else {
|
} else {
|
||||||
*device = device::set_enabled_class(&device.dev_eui, DeviceClass::A).await?;
|
DeviceClass::A
|
||||||
}
|
},
|
||||||
Ok(())
|
),
|
||||||
}
|
..Default::default()
|
||||||
|
},
|
||||||
async fn set_join_eui(&mut self) -> Result<()> {
|
)
|
||||||
trace!("Setting JoinEUI");
|
.await?,
|
||||||
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?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -714,7 +710,9 @@ impl JoinRequest {
|
|||||||
|
|
||||||
fn set_pr_start_ans_payload(&mut self) -> Result<()> {
|
fn set_pr_start_ans_payload(&mut self) -> Result<()> {
|
||||||
trace!("Setting PRStartAnsPayload");
|
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 region_conf = region::get(&self.uplink_frame_set.region_config_id)?;
|
||||||
|
|
||||||
let sender_id = NetID::from_slice(&self.pr_start_req.base.sender_id)?;
|
let sender_id = NetID::from_slice(&self.pr_start_req.base.sender_id)?;
|
||||||
@ -753,8 +751,8 @@ impl JoinRequest {
|
|||||||
.base
|
.base
|
||||||
.to_base_payload_result(backend::ResultCode::Success, ""),
|
.to_base_payload_result(backend::ResultCode::Success, ""),
|
||||||
phy_payload: self.join_accept.as_ref().unwrap().to_vec()?,
|
phy_payload: self.join_accept.as_ref().unwrap().to_vec()?,
|
||||||
dev_eui: ds.dev_eui.clone(),
|
dev_eui: d.dev_eui.to_vec(),
|
||||||
dev_addr: ds.dev_addr.clone(),
|
dev_addr: d.get_dev_addr()?.to_vec(),
|
||||||
lifetime: if pr_lifetime.is_zero() {
|
lifetime: if pr_lifetime.is_zero() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -764,7 +762,7 @@ impl JoinRequest {
|
|||||||
nwk_s_key,
|
nwk_s_key,
|
||||||
f_cnt_up: Some(0),
|
f_cnt_up: Some(0),
|
||||||
dl_meta_data: Some(backend::DLMetaData {
|
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_1: Some(rx1_freq as f64 / 1_000_000.0),
|
||||||
dl_freq_2: Some(rx2_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),
|
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,
|
device, device_profile, error::Error as StorageError, gateway, get_async_redis_conn, redis_key,
|
||||||
};
|
};
|
||||||
use crate::stream;
|
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::region::CommonName;
|
||||||
use lrwn::{ForwardUplinkReq, MType, PhyPayload, EUI64};
|
use lrwn::{ForwardUplinkReq, MType, PhyPayload, EUI64};
|
||||||
|
|
||||||
@ -75,7 +75,6 @@ pub struct RelayContext {
|
|||||||
pub req: ForwardUplinkReq,
|
pub req: ForwardUplinkReq,
|
||||||
pub device: device::Device,
|
pub device: device::Device,
|
||||||
pub device_profile: device_profile::DeviceProfile,
|
pub device_profile: device_profile::DeviceProfile,
|
||||||
pub device_session: internal::DeviceSession,
|
|
||||||
pub must_ack: bool,
|
pub must_ack: bool,
|
||||||
pub must_send_downlink: bool,
|
pub must_send_downlink: bool,
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ pkgs.mkShell {
|
|||||||
pkgs.perl
|
pkgs.perl
|
||||||
pkgs.cmake
|
pkgs.cmake
|
||||||
pkgs.clang
|
pkgs.clang
|
||||||
|
pkgs.postgresql # needed to build the diesel cli utility
|
||||||
];
|
];
|
||||||
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
||||||
BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.llvmPackages.libclang.lib}/lib/clang/${pkgs.llvmPackages.libclang.version}/include";
|
BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.llvmPackages.libclang.lib}/lib/clang/${pkgs.llvmPackages.libclang.version}/include";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user