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.
This commit is contained in:
Orne Brocaar 2023-11-23 13:10:10 +00:00
parent 3538145e3d
commit 8e6079ec9c
4 changed files with 256 additions and 3 deletions

View File

@ -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 {

View File

@ -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 {

View File

@ -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(),

View File

@ -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)]