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