mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-05-21 01:57:59 +00:00
Make get device-session for phypayload functions update f_cnt.
This fixes the FrmPayload decryption in case of frame-counter rollover (16lsb) as it was using the f_cnt as sent over the air (16lsb) and not the full frame-counter (32b). Before, these functions would return the device-session for the given uplink PhyPayload (if a matching device-session was found), together with the full frame-counter. However it would not modify the f_cnt of the PhyPayload to the full frame-counter making it prone to errors like the above.
This commit is contained in:
parent
07d4e89a92
commit
e3fae6260b
@ -262,7 +262,7 @@ async fn _handle_pr_start_req_data(
|
|||||||
let region_name = region::get_region_name(region_common_name)?;
|
let region_name = region::get_region_name(region_common_name)?;
|
||||||
let dr = pl.ul_meta_data.data_rate.unwrap_or_default();
|
let dr = pl.ul_meta_data.data_rate.unwrap_or_default();
|
||||||
|
|
||||||
let ufs = UplinkFrameSet {
|
let mut ufs = UplinkFrameSet {
|
||||||
uplink_set_id: Uuid::new_v4(),
|
uplink_set_id: Uuid::new_v4(),
|
||||||
dr,
|
dr,
|
||||||
ch: helpers::get_uplink_ch(®ion_name, tx_info.frequency, dr)?,
|
ch: helpers::get_uplink_ch(®ion_name, tx_info.frequency, dr)?,
|
||||||
@ -280,7 +280,7 @@ async fn _handle_pr_start_req_data(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// get device-session
|
// get device-session
|
||||||
let ds = device_session::get_for_phypayload(&ufs.phy_payload, ufs.dr, ufs.ch as u8).await?;
|
let ds = device_session::get_for_phypayload(&mut ufs.phy_payload, ufs.dr, ufs.ch as u8).await?;
|
||||||
let pr_lifetime = roaming::get_passive_roaming_lifetime(sender_id)?;
|
let pr_lifetime = roaming::get_passive_roaming_lifetime(sender_id)?;
|
||||||
let kek_label = roaming::get_passive_roaming_kek_label(sender_id)?;
|
let kek_label = roaming::get_passive_roaming_kek_label(sender_id)?;
|
||||||
|
|
||||||
|
@ -122,15 +122,13 @@ pub async fn delete(dev_eui: &EUI64) -> Result<()> {
|
|||||||
// This function will increment the uplink frame-counter and will immediately update the
|
// 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
|
// device-session in the database, to make sure that in case this function is called multiple
|
||||||
// times, at most one will be valid.
|
// 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(
|
pub async fn get_for_phypayload_and_incr_f_cnt_up(
|
||||||
phy: &PhyPayload,
|
phy: &mut PhyPayload,
|
||||||
tx_dr: u8,
|
tx_dr: u8,
|
||||||
tx_ch: u8,
|
tx_ch: u8,
|
||||||
) -> Result<ValidationStatus, Error> {
|
) -> Result<ValidationStatus, Error> {
|
||||||
// Clone the PhyPayload, as we will update the f_cnt to the full (32bit) frame-counter value
|
|
||||||
// for calculating the MIC.
|
|
||||||
let mut phy = phy.clone();
|
|
||||||
|
|
||||||
let mut _dev_addr = DevAddr::from_be_bytes([0x00, 0x00, 0x00, 0x00]);
|
let mut _dev_addr = DevAddr::from_be_bytes([0x00, 0x00, 0x00, 0x00]);
|
||||||
let mut _f_cnt_orig = 0;
|
let mut _f_cnt_orig = 0;
|
||||||
|
|
||||||
@ -150,11 +148,6 @@ pub async fn get_for_phypayload_and_incr_f_cnt_up(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for mut ds in device_sessions {
|
for mut ds in device_sessions {
|
||||||
// Restore the original f_cnt.
|
|
||||||
if let Payload::MACPayload(pl) = &mut phy.payload {
|
|
||||||
pl.fhdr.f_cnt = _f_cnt_orig;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the full 32bit frame-counter.
|
// Get the full 32bit frame-counter.
|
||||||
let full_f_cnt = get_full_f_cnt_up(ds.f_cnt_up, _f_cnt_orig);
|
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 f_nwk_s_int_key = AES128Key::from_slice(&ds.f_nwk_s_int_key)?;
|
||||||
@ -236,6 +229,11 @@ pub async fn get_for_phypayload_and_incr_f_cnt_up(
|
|||||||
return Ok(ValidationStatus::Reset(full_f_cnt, ds));
|
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)
|
Err(Error::InvalidMIC)
|
||||||
@ -244,15 +242,13 @@ pub async fn get_for_phypayload_and_incr_f_cnt_up(
|
|||||||
// Simmilar to get_for_phypayload_and_incr_f_cnt_up, but only retrieves the device-session for the
|
// 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
|
// given PhyPayload. As it does not return the ValidationStatus, it only returns the DeviceSession
|
||||||
// in case of a valid frame-counter.
|
// 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(
|
pub async fn get_for_phypayload(
|
||||||
phy: &PhyPayload,
|
phy: &mut PhyPayload,
|
||||||
tx_dr: u8,
|
tx_dr: u8,
|
||||||
tx_ch: u8,
|
tx_ch: u8,
|
||||||
) -> Result<internal::DeviceSession, Error> {
|
) -> Result<internal::DeviceSession, Error> {
|
||||||
// Clone the PhyPayload, as we will update the f_cnt to the full (32bit) frame-counter value
|
|
||||||
// for calculating the MIC.
|
|
||||||
let mut phy = phy.clone();
|
|
||||||
|
|
||||||
// Get the dev_addr and original f_cnt.
|
// Get the dev_addr and original f_cnt.
|
||||||
let (dev_addr, f_cnt_orig) = if let Payload::MACPayload(pl) = &phy.payload {
|
let (dev_addr, f_cnt_orig) = if let Payload::MACPayload(pl) = &phy.payload {
|
||||||
(pl.fhdr.devaddr, pl.fhdr.f_cnt)
|
(pl.fhdr.devaddr, pl.fhdr.f_cnt)
|
||||||
@ -268,11 +264,6 @@ pub async fn get_for_phypayload(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for ds in device_sessions {
|
for ds in device_sessions {
|
||||||
// Restore the original f_cnt.
|
|
||||||
if let Payload::MACPayload(pl) = &mut phy.payload {
|
|
||||||
pl.fhdr.f_cnt = f_cnt_orig;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the full 32bit frame-counter.
|
// Get the full 32bit frame-counter.
|
||||||
let full_f_cnt = get_full_f_cnt_up(ds.f_cnt_up, f_cnt_orig);
|
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 f_nwk_s_int_key = AES128Key::from_slice(&ds.f_nwk_s_int_key)?;
|
||||||
@ -297,6 +288,11 @@ pub async fn get_for_phypayload(
|
|||||||
if mic_ok && full_f_cnt >= ds.f_cnt_up {
|
if mic_ok && full_f_cnt >= ds.f_cnt_up {
|
||||||
return Ok(ds);
|
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)
|
Err(Error::InvalidMIC)
|
||||||
@ -511,6 +507,24 @@ pub mod test {
|
|||||||
})),
|
})),
|
||||||
..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 {
|
for ds in &device_sessions {
|
||||||
@ -628,6 +642,24 @@ pub mod test {
|
|||||||
expected_error: None,
|
expected_error: None,
|
||||||
expected_reset: false,
|
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 {
|
for tst in &tests {
|
||||||
@ -658,15 +690,29 @@ pub mod test {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let ds_res = get_for_phypayload_and_incr_f_cnt_up(&phy, 0, 0).await;
|
// 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(&mut phy, 0, 0).await;
|
||||||
if tst.expected_error.is_some() {
|
if tst.expected_error.is_some() {
|
||||||
assert_eq!(true, ds_res.is_err());
|
assert_eq!(true, ds_res.is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tst.expected_error.as_ref().unwrap(),
|
tst.expected_error.as_ref().unwrap(),
|
||||||
&ds_res.err().unwrap().to_string()
|
&ds_res.err().unwrap().to_string()
|
||||||
);
|
);
|
||||||
|
if let lrwn::Payload::MACPayload(pl) = &phy.payload {
|
||||||
|
assert_eq!(tst.f_cnt, pl.fhdr.f_cnt);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let ds = ds_res.unwrap();
|
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 {
|
if let ValidationStatus::Ok(full_f_cnt, ds) = ds {
|
||||||
assert_eq!(false, tst.expected_retransmission);
|
assert_eq!(false, tst.expected_retransmission);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -132,53 +132,58 @@ impl Data {
|
|||||||
async fn get_device_session(&mut self) -> Result<(), Error> {
|
async fn get_device_session(&mut self) -> Result<(), Error> {
|
||||||
trace!("Getting device-session for dev_addr");
|
trace!("Getting device-session for dev_addr");
|
||||||
|
|
||||||
if let lrwn::Payload::MACPayload(pl) = &self.uplink_frame_set.phy_payload.payload {
|
let dev_addr =
|
||||||
match device_session::get_for_phypayload_and_incr_f_cnt_up(
|
if let lrwn::Payload::MACPayload(pl) = &self.uplink_frame_set.phy_payload.payload {
|
||||||
&self.uplink_frame_set.phy_payload,
|
pl.fhdr.devaddr
|
||||||
self.uplink_frame_set.dr,
|
} else {
|
||||||
self.uplink_frame_set.ch as u8,
|
return Err(Error::AnyhowError(anyhow!("No MacPayload in PhyPayload")));
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(v) => match v {
|
|
||||||
device_session::ValidationStatus::Ok(f_cnt, ds) => {
|
|
||||||
self.device_session = Some(ds);
|
|
||||||
self.f_cnt_up_full = f_cnt;
|
|
||||||
}
|
|
||||||
device_session::ValidationStatus::Retransmission(f_cnt, ds) => {
|
|
||||||
self.retransmission = true;
|
|
||||||
self.device_session = Some(ds);
|
|
||||||
self.f_cnt_up_full = f_cnt;
|
|
||||||
}
|
|
||||||
device_session::ValidationStatus::Reset(f_cnt, ds) => {
|
|
||||||
self.reset = true;
|
|
||||||
self.device_session = Some(ds);
|
|
||||||
self.f_cnt_up_full = f_cnt;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => match e {
|
|
||||||
StorageError::NotFound(s) => {
|
|
||||||
warn!(dev_addr = %s, "No device-session exists for dev_addr");
|
|
||||||
return Err(Error::Abort);
|
|
||||||
}
|
|
||||||
StorageError::InvalidMIC => {
|
|
||||||
warn!(dev_addr = %pl.fhdr.devaddr, "None of the device-sessions for dev_addr resulted in valid MIC");
|
|
||||||
|
|
||||||
// Log uplink for null DevEUI.
|
|
||||||
let mut ufl: api::UplinkFrameLog = (&self.uplink_frame_set).try_into()?;
|
|
||||||
ufl.dev_eui = "0000000000000000".to_string();
|
|
||||||
framelog::log_uplink_for_device(&ufl).await?;
|
|
||||||
|
|
||||||
return Err(Error::Abort);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(Error::AnyhowError(
|
|
||||||
anyhow::Error::new(e).context("Get device-session"),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
match device_session::get_for_phypayload_and_incr_f_cnt_up(
|
||||||
|
&mut self.uplink_frame_set.phy_payload,
|
||||||
|
self.uplink_frame_set.dr,
|
||||||
|
self.uplink_frame_set.ch as u8,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(v) => match v {
|
||||||
|
device_session::ValidationStatus::Ok(f_cnt, ds) => {
|
||||||
|
self.device_session = Some(ds);
|
||||||
|
self.f_cnt_up_full = f_cnt;
|
||||||
|
}
|
||||||
|
device_session::ValidationStatus::Retransmission(f_cnt, ds) => {
|
||||||
|
self.retransmission = true;
|
||||||
|
self.device_session = Some(ds);
|
||||||
|
self.f_cnt_up_full = f_cnt;
|
||||||
|
}
|
||||||
|
device_session::ValidationStatus::Reset(f_cnt, ds) => {
|
||||||
|
self.reset = true;
|
||||||
|
self.device_session = Some(ds);
|
||||||
|
self.f_cnt_up_full = f_cnt;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => match e {
|
||||||
|
StorageError::NotFound(s) => {
|
||||||
|
warn!(dev_addr = %s, "No device-session exists for dev_addr");
|
||||||
|
return Err(Error::Abort);
|
||||||
|
}
|
||||||
|
StorageError::InvalidMIC => {
|
||||||
|
warn!(dev_addr = %dev_addr, "None of the device-sessions for dev_addr resulted in valid MIC");
|
||||||
|
|
||||||
|
// Log uplink for null DevEUI.
|
||||||
|
let mut ufl: api::UplinkFrameLog = (&self.uplink_frame_set).try_into()?;
|
||||||
|
ufl.dev_eui = "0000000000000000".to_string();
|
||||||
|
framelog::log_uplink_for_device(&ufl).await?;
|
||||||
|
|
||||||
|
return Err(Error::Abort);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::AnyhowError(
|
||||||
|
anyhow::Error::new(e).context("Get device-session"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user