From 8e6079ec9cbe65eb1cc404bd459aab7e393ac44c Mon Sep 17 00:00:00 2001 From: Orne Brocaar Date: Thu, 23 Nov 2023 13:10:10 +0000 Subject: [PATCH] Fix sending empty downlink to Relay (adr_ack_req=true). In case a Relay would send an uplink with adr_ack_req=true, this would not result in an empty downlink in case there is no downlink to relay back to the relay end-device. --- chirpstack/src/downlink/data.rs | 56 +++++++ chirpstack/src/test/relay_class_a_test.rs | 196 +++++++++++++++++++++- chirpstack/src/uplink/data.rs | 6 +- chirpstack/src/uplink/mod.rs | 1 + 4 files changed, 256 insertions(+), 3 deletions(-) diff --git a/chirpstack/src/downlink/data.rs b/chirpstack/src/downlink/data.rs index 8bb55513..cdd30bbf 100644 --- a/chirpstack/src/downlink/data.rs +++ b/chirpstack/src/downlink/data.rs @@ -287,6 +287,10 @@ impl Data { ctx.save_downlink_frame_relayed().await?; ctx.save_device_session().await?; ctx.send_downlink_frame().await?; + } else if ctx._must_respond_to_relay() { + ctx.set_phy_payloads_relay()?; + ctx.save_downlink_frame_relayed().await?; + ctx.send_downlink_frame().await?; } Ok(()) @@ -635,6 +639,11 @@ impl Data { true } + fn _must_respond_to_relay(&self) -> bool { + let relay_ctx = self.relay_context.as_ref().unwrap(); + relay_ctx.must_ack || relay_ctx.must_send_downlink + } + fn _is_class_a(&self) -> bool { self.device.enabled_class == DeviceClass::A } @@ -862,6 +871,53 @@ impl Data { Ok(()) } + fn set_phy_payloads_relay(&mut self) -> Result<()> { + trace!("Set relay PhyPayloads"); + + let relay_ctx = self.relay_context.as_ref().unwrap(); + + for item in self.downlink_frame_items.iter_mut() { + let mut relay_phy = lrwn::PhyPayload { + mhdr: lrwn::MHDR { + m_type: lrwn::MType::UnconfirmedDataDown, + major: lrwn::Major::LoRaWANR1, + }, + payload: lrwn::Payload::MACPayload(lrwn::MACPayload { + fhdr: lrwn::FHDR { + devaddr: lrwn::DevAddr::from_slice(&relay_ctx.device_session.dev_addr)?, + f_cnt: relay_ctx.device_session.get_a_f_cnt_down(), + f_ctrl: lrwn::FCtrl { + adr: !self.network_conf.adr_disabled, + ack: relay_ctx.must_ack, + ..Default::default() + }, + f_opts: lrwn::MACCommandSet::new(vec![]), + }, + f_port: None, + frm_payload: None, + }), + mic: None, + }; + + // 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)?, + )?; + + let relay_phy_b = relay_phy.to_vec()?; + item.downlink_frame_item.phy_payload = relay_phy_b; + self.downlink_frame + .items + .push(item.downlink_frame_item.clone()); + } + + Ok(()) + } + async fn update_device_queue_item(&mut self) -> Result<()> { trace!("Updating device queue-item"); if let Some(qi) = &mut self.device_queue_item { diff --git a/chirpstack/src/test/relay_class_a_test.rs b/chirpstack/src/test/relay_class_a_test.rs index b5706ce3..27d5590d 100644 --- a/chirpstack/src/test/relay_class_a_test.rs +++ b/chirpstack/src/test/relay_class_a_test.rs @@ -243,7 +243,7 @@ async fn test_lorawan_10() { wor_channel: 0, }, frequency: 868100000, - payload: Box::new(phy_relay_ed_unconfirmed_up), + payload: Box::new(phy_relay_ed_unconfirmed_up.clone()), })), }), mic: None, @@ -262,6 +262,49 @@ async fn test_lorawan_10() { ) .unwrap(); + let mut phy_relay_unconfirmed_up_adr_ack_req = lrwn::PhyPayload { + mhdr: lrwn::MHDR { + m_type: lrwn::MType::UnconfirmedDataUp, + major: lrwn::Major::LoRaWANR1, + }, + payload: lrwn::Payload::MACPayload(lrwn::MACPayload { + fhdr: lrwn::FHDR { + devaddr: lrwn::DevAddr::from_slice(&ds_relay.dev_addr).unwrap(), + f_cnt: 8, + f_ctrl: lrwn::FCtrl { + adr_ack_req: true, + ..Default::default() + }, + ..Default::default() + }, + f_port: Some(lrwn::LA_FPORT_RELAY), + frm_payload: Some(lrwn::FRMPayload::ForwardUplinkReq(lrwn::ForwardUplinkReq { + metadata: lrwn::UplinkMetadata { + dr: 5, + snr: 10, + rssi: -100, + wor_channel: 0, + }, + frequency: 868100000, + payload: Box::new(phy_relay_ed_unconfirmed_up), + })), + }), + mic: None, + }; + phy_relay_unconfirmed_up_adr_ack_req + .encrypt_frm_payload(&AES128Key::from_slice(&ds_relay.nwk_s_enc_key).unwrap()) + .unwrap(); + phy_relay_unconfirmed_up_adr_ack_req + .set_uplink_data_mic( + lrwn::MACVersion::LoRaWAN1_0, + 0, + 0, + 0, + &AES128Key::from_slice(&ds_relay.f_nwk_s_int_key).unwrap(), + &AES128Key::from_slice(&ds_relay.s_nwk_s_int_key).unwrap(), + ) + .unwrap(); + let mut phy_relay_confirmed_up = lrwn::PhyPayload { mhdr: lrwn::MHDR { m_type: lrwn::MType::UnconfirmedDataUp, @@ -365,6 +408,34 @@ async fn test_lorawan_10() { ) .unwrap(); + let mut phy_relay_unconfirmed_down_empty = lrwn::PhyPayload { + mhdr: lrwn::MHDR { + m_type: lrwn::MType::UnconfirmedDataDown, + major: lrwn::Major::LoRaWANR1, + }, + payload: lrwn::Payload::MACPayload(lrwn::MACPayload { + fhdr: lrwn::FHDR { + devaddr: lrwn::DevAddr::from_slice(&ds_relay.dev_addr).unwrap(), + f_cnt: 5, + f_ctrl: lrwn::FCtrl { + adr: true, + ..Default::default() + }, + ..Default::default() + }, + f_port: None, + frm_payload: None, + }), + mic: None, + }; + phy_relay_unconfirmed_down_empty + .set_downlink_data_mic( + lrwn::MACVersion::LoRaWAN1_0, + 0, + &AES128Key::from_slice(&ds_relay.s_nwk_s_int_key).unwrap(), + ) + .unwrap(); + let tests = vec![ Test { name: "relayed unconfirmed uplink".into(), @@ -554,6 +625,129 @@ async fn test_lorawan_10() { }), ], }, + Test { + name: "relayed unconfirmed uplink + adr_ack_req".into(), + device_queue_items_relay_ed: vec![], + device_session_relay: Some(ds_relay.clone()), + device_session_relay_ed: Some(ds_relay_ed.clone()), + tx_info: tx_info.clone(), + rx_info: rx_info.clone(), + phy_payload: phy_relay_unconfirmed_up_adr_ack_req, + assert: vec![ + assert::f_cnt_up(dev_relay.dev_eui, 9), + assert::f_cnt_up(dev_relay_ed.dev_eui, 89), + assert::uplink_event(integration_pb::UplinkEvent { + device_info: Some(integration_pb::DeviceInfo { + tenant_id: t.id.to_string(), + tenant_name: t.name.clone(), + application_id: app.id.to_string(), + application_name: app.name.clone(), + device_profile_id: dp_relay.id.to_string(), + device_profile_name: dp_relay.name.clone(), + device_name: dev_relay.name.clone(), + dev_eui: dev_relay.dev_eui.to_string(), + ..Default::default() + }), + dev_addr: "01010101".to_string(), + dr: 5, + f_cnt: 8, + f_port: 226, + data: vec![], + rx_info: vec![rx_info.clone()], + tx_info: Some(tx_info.clone()), + ..Default::default() + }), + assert::uplink_event(integration_pb::UplinkEvent { + device_info: Some(integration_pb::DeviceInfo { + tenant_id: t.id.to_string(), + tenant_name: t.name.clone(), + application_id: app.id.to_string(), + application_name: app.name.clone(), + device_profile_id: dp_relay_ed.id.to_string(), + device_profile_name: dp_relay_ed.name.clone(), + device_name: dev_relay_ed.name.clone(), + dev_eui: dev_relay_ed.dev_eui.to_string(), + ..Default::default() + }), + dev_addr: "02020202".to_string(), + dr: 5, + f_cnt: 88, + f_port: 1, + data: vec![1, 2, 3, 4], + rx_info: vec![rx_info.clone()], + tx_info: Some(tx_info.clone()), + relay_rx_info: Some(integration_pb::UplinkRelayRxInfo { + dev_eui: "0101010101010101".into(), + frequency: 868100000, + dr: 5, + snr: 10, + rssi: -100, + wor_channel: 0, + }), + ..Default::default() + }), + assert::downlink_frame(gw::DownlinkFrame { + gateway_id: gw.gateway_id.to_string(), + items: vec![ + gw::DownlinkFrameItem { + phy_payload: phy_relay_unconfirmed_down_empty.to_vec().unwrap(), + tx_info: Some(gw::DownlinkTxInfo { + frequency: 868100000, + power: 16, + modulation: Some(gw::Modulation { + parameters: Some(gw::modulation::Parameters::Lora( + gw::LoraModulationInfo { + bandwidth: 125000, + spreading_factor: 7, + code_rate: gw::CodeRate::Cr45.into(), + polarization_inversion: true, + ..Default::default() + }, + )), + }), + timing: Some(gw::Timing { + parameters: Some(gw::timing::Parameters::Delay( + gw::DelayTimingInfo { + delay: Some(Duration::from_secs(1).into()), + }, + )), + }), + ..Default::default() + }), + ..Default::default() + }, + gw::DownlinkFrameItem { + phy_payload: phy_relay_unconfirmed_down_empty.to_vec().unwrap(), + tx_info: Some(gw::DownlinkTxInfo { + frequency: 869525000, + power: 29, + modulation: Some(gw::Modulation { + parameters: Some(gw::modulation::Parameters::Lora( + gw::LoraModulationInfo { + bandwidth: 125000, + spreading_factor: 12, + code_rate: gw::CodeRate::Cr45.into(), + polarization_inversion: true, + ..Default::default() + }, + )), + }), + timing: Some(gw::Timing { + parameters: Some(gw::timing::Parameters::Delay( + gw::DelayTimingInfo { + delay: Some(Duration::from_secs(2).into()), + }, + )), + }), + ..Default::default() + }), + ..Default::default() + }, + ], + ..Default::default() + }), + ], + }, ]; for test in &tests { diff --git a/chirpstack/src/uplink/data.rs b/chirpstack/src/uplink/data.rs index 053c4d0a..d895a463 100644 --- a/chirpstack/src/uplink/data.rs +++ b/chirpstack/src/uplink/data.rs @@ -1305,8 +1305,8 @@ impl Data { async fn handle_forward_uplink_req(&self) -> Result<()> { trace!("Handling ForwardUplinkReq"); - if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload { - if let Some(lrwn::FRMPayload::ForwardUplinkReq(pl)) = &pl.frm_payload { + if let lrwn::Payload::MACPayload(relay_pl) = &self.phy_payload.payload { + if let Some(lrwn::FRMPayload::ForwardUplinkReq(pl)) = &relay_pl.frm_payload { match pl.payload.mhdr.m_type { lrwn::MType::JoinRequest => { super::join::JoinRequest::handle_relayed( @@ -1317,6 +1317,7 @@ impl Data { 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, }, self.uplink_frame_set.clone(), ) @@ -1331,6 +1332,7 @@ impl Data { 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, }, self.device_gateway_rx_info.as_ref().unwrap().clone(), self.uplink_frame_set.clone(), diff --git a/chirpstack/src/uplink/mod.rs b/chirpstack/src/uplink/mod.rs index a3c8728d..e1958985 100644 --- a/chirpstack/src/uplink/mod.rs +++ b/chirpstack/src/uplink/mod.rs @@ -78,6 +78,7 @@ pub struct RelayContext { pub device_profile: device_profile::DeviceProfile, pub device_session: internal::DeviceSession, pub must_ack: bool, + pub must_send_downlink: bool, } #[derive(Clone)]