mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-06-20 00:04:00 +00:00
Initial commit.
This commit is contained in:
664
lrwn/src/payload.rs
Normal file
664
lrwn/src/payload.rs
Normal file
@ -0,0 +1,664 @@
|
||||
use anyhow::Result;
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use super::cflist::CFList;
|
||||
use super::devaddr::DevAddr;
|
||||
use super::dl_settings::DLSettings;
|
||||
use super::eui64::EUI64;
|
||||
use super::fhdr::FCtrl;
|
||||
use super::fhdr::FHDR;
|
||||
use super::maccommand::MACCommandSet;
|
||||
use super::mhdr::MType;
|
||||
use super::netid::NetID;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Payload {
|
||||
JoinRequest(JoinRequestPayload),
|
||||
JoinAccept(JoinAcceptPayload),
|
||||
MACPayload(MACPayload),
|
||||
RejoinRequestType02(RejoinRequestType02Payload),
|
||||
RejoinRequestType1(RejoinRequestType1Payload),
|
||||
Raw(Vec<u8>),
|
||||
}
|
||||
|
||||
impl Serialize for Payload {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
Payload::JoinRequest(v) => v.serialize(serializer),
|
||||
Payload::JoinAccept(v) => v.serialize(serializer),
|
||||
Payload::MACPayload(v) => v.serialize(serializer),
|
||||
Payload::RejoinRequestType02(v) => v.serialize(serializer),
|
||||
Payload::RejoinRequestType1(v) => v.serialize(serializer),
|
||||
Payload::Raw(v) => serializer.serialize_str(&hex::encode(v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, PartialEq, Debug, Clone)]
|
||||
pub enum JoinType {
|
||||
Join,
|
||||
RejoinType0,
|
||||
RejoinType1,
|
||||
RejoinType2,
|
||||
}
|
||||
|
||||
impl Payload {
|
||||
pub fn from_slice(m_type: MType, b: &[u8]) -> Result<Self> {
|
||||
Ok(match m_type {
|
||||
MType::JoinRequest => Payload::JoinRequest(JoinRequestPayload::from_slice(b)?),
|
||||
MType::JoinAccept => Payload::Raw(b.to_vec()), // the join-accept is encrypted
|
||||
MType::UnconfirmedDataUp
|
||||
| MType::ConfirmedDataUp
|
||||
| MType::UnconfirmedDataDown
|
||||
| MType::ConfirmedDataDown => Payload::MACPayload(MACPayload::from_slice(b)?),
|
||||
MType::RejoinRequest => {
|
||||
if b.is_empty() {
|
||||
return Err(anyhow!("RejoinRequest payload is empty"));
|
||||
}
|
||||
|
||||
match b[0] {
|
||||
0x00 | 0x02 => {
|
||||
Payload::RejoinRequestType02(RejoinRequestType02Payload::from_slice(b)?)
|
||||
}
|
||||
0x01 => Payload::RejoinRequestType1(RejoinRequestType1Payload::from_slice(b)?),
|
||||
_ => {
|
||||
return Err(anyhow!("invalid RejoinType"));
|
||||
}
|
||||
}
|
||||
}
|
||||
MType::Proprietary => Payload::Raw(b.to_vec()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Result<Vec<u8>> {
|
||||
match self {
|
||||
Payload::JoinRequest(v) => Ok(v.to_vec()),
|
||||
Payload::JoinAccept(v) => v.to_vec(),
|
||||
Payload::MACPayload(v) => Ok(v.to_vec()?),
|
||||
Payload::RejoinRequestType02(v) => Ok(v.to_vec()?),
|
||||
Payload::RejoinRequestType1(v) => Ok(v.to_vec()?),
|
||||
Payload::Raw(v) => Ok(v.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, PartialEq, Debug, Copy, Clone)]
|
||||
pub struct JoinRequestPayload {
|
||||
pub join_eui: EUI64,
|
||||
pub dev_eui: EUI64,
|
||||
pub dev_nonce: u16,
|
||||
}
|
||||
|
||||
impl JoinRequestPayload {
|
||||
pub fn from_slice(b: &[u8]) -> Result<Self> {
|
||||
if b.len() != 18 {
|
||||
return Err(anyhow!("join-request payload must be exactly 18 bytes"));
|
||||
}
|
||||
|
||||
let mut join_eui: [u8; 8] = [0; 8];
|
||||
let mut dev_eui: [u8; 8] = [0; 8];
|
||||
|
||||
join_eui.clone_from_slice(&b[0..8]);
|
||||
dev_eui.clone_from_slice(&b[8..16]);
|
||||
|
||||
Ok(JoinRequestPayload {
|
||||
join_eui: EUI64::from_le_bytes(join_eui),
|
||||
dev_eui: EUI64::from_le_bytes(dev_eui),
|
||||
dev_nonce: u16::from_le_bytes([b[16], b[17]]),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
let mut v = Vec::with_capacity(18);
|
||||
v.append(&mut self.join_eui.to_le_bytes().to_vec());
|
||||
v.append(&mut self.dev_eui.to_le_bytes().to_vec());
|
||||
v.append(&mut self.dev_nonce.to_le_bytes().to_vec());
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, PartialEq, Debug, Clone)]
|
||||
pub struct JoinAcceptPayload {
|
||||
pub join_nonce: u32, // the actual max value is (2^24 -1)
|
||||
pub home_netid: NetID,
|
||||
pub devaddr: DevAddr,
|
||||
pub dl_settings: DLSettings,
|
||||
pub rx_delay: u8, // 0=1s, 1=1s, ... 15=15s
|
||||
pub cflist: Option<CFList>,
|
||||
}
|
||||
|
||||
impl JoinAcceptPayload {
|
||||
pub fn from_slice(b: &[u8]) -> Result<Self> {
|
||||
if b.len() != 12 && b.len() != 28 {
|
||||
return Err(anyhow!("12 or 28 bytes are expected for JoinAcceptPayload"));
|
||||
}
|
||||
|
||||
Ok(JoinAcceptPayload {
|
||||
join_nonce: u32::from_le_bytes([b[0], b[1], b[2], 0x00]),
|
||||
home_netid: {
|
||||
let mut netid: [u8; 3] = [0; 3];
|
||||
netid.clone_from_slice(&b[3..6]);
|
||||
NetID::from_le_bytes(netid)
|
||||
},
|
||||
devaddr: {
|
||||
let mut devaddr: [u8; 4] = [0; 4];
|
||||
devaddr.clone_from_slice(&b[6..10]);
|
||||
DevAddr::from_le_bytes(devaddr)
|
||||
},
|
||||
dl_settings: {
|
||||
let mut dl_settings: [u8; 1] = [0];
|
||||
dl_settings.clone_from_slice(&b[10..11]);
|
||||
DLSettings::from_le_bytes(dl_settings)
|
||||
},
|
||||
rx_delay: b[11],
|
||||
cflist: match b.len() {
|
||||
28 => {
|
||||
let mut cflist: [u8; 16] = [0; 16];
|
||||
cflist.clone_from_slice(&b[12..]);
|
||||
Some(CFList::from_bytes(cflist)?)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Result<Vec<u8>> {
|
||||
if self.rx_delay > 15 {
|
||||
return Err(anyhow!("max value of rx_delay is 15"));
|
||||
}
|
||||
|
||||
let mut b = Vec::with_capacity(28);
|
||||
b.extend_from_slice(&self.join_nonce.to_le_bytes()[..3]);
|
||||
b.extend_from_slice(&self.home_netid.to_le_bytes());
|
||||
b.extend_from_slice(&self.devaddr.to_le_bytes());
|
||||
b.extend_from_slice(&self.dl_settings.to_le_bytes()?);
|
||||
b.push(self.rx_delay);
|
||||
|
||||
if let Some(v) = &self.cflist {
|
||||
b.extend_from_slice(&v.to_bytes()?);
|
||||
}
|
||||
|
||||
Ok(b)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum FRMPayload {
|
||||
Raw(Vec<u8>),
|
||||
MACCommandSet(MACCommandSet),
|
||||
}
|
||||
|
||||
impl Serialize for FRMPayload {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
FRMPayload::Raw(v) => serializer.serialize_str(&hex::encode(&v)),
|
||||
FRMPayload::MACCommandSet(v) => v.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FRMPayload {
|
||||
pub fn to_vec(&self) -> Result<Vec<u8>> {
|
||||
Ok(match self {
|
||||
FRMPayload::Raw(v) => v.clone(),
|
||||
FRMPayload::MACCommandSet(v) => v.to_vec()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, PartialEq, Debug, Clone, Default)]
|
||||
pub struct MACPayload {
|
||||
pub fhdr: FHDR,
|
||||
pub f_port: Option<u8>,
|
||||
pub frm_payload: Option<FRMPayload>,
|
||||
}
|
||||
|
||||
impl MACPayload {
|
||||
pub fn from_slice(b: &[u8]) -> Result<Self> {
|
||||
let b_len = b.len();
|
||||
if b_len < 7 {
|
||||
return Err(anyhow!("MACPayload requires at least 7 bytes"));
|
||||
}
|
||||
|
||||
// decode FCtrl as we need f_opts_len.
|
||||
let mut f_ctrl: [u8; 1] = [0];
|
||||
f_ctrl.clone_from_slice(&b[4..5]);
|
||||
let f_ctrl = FCtrl::from_le_bytes(f_ctrl);
|
||||
|
||||
// check that there are at least as many bytes as FOptsLen claims
|
||||
let fhdr_size = 7 + f_ctrl.f_opts_len as usize;
|
||||
if b_len < fhdr_size {
|
||||
return Err(anyhow!("not enough bytes to decode FHDR"));
|
||||
}
|
||||
|
||||
Ok(MACPayload {
|
||||
fhdr: FHDR::from_slice(&b[..fhdr_size])?,
|
||||
f_port: match b_len > fhdr_size {
|
||||
true => Some(b[fhdr_size]),
|
||||
false => None,
|
||||
},
|
||||
frm_payload: match b_len > fhdr_size + 1 {
|
||||
true => Some(FRMPayload::Raw(b[fhdr_size + 1..].to_vec())),
|
||||
false => None,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Result<Vec<u8>> {
|
||||
// validation of frm_payload
|
||||
if self.frm_payload.is_some() {
|
||||
// validate that f_port is set
|
||||
if self.f_port.is_none() {
|
||||
return Err(anyhow!("f_port not not be None when frm_payload is set"));
|
||||
}
|
||||
|
||||
// mac-commands must have f_port=0
|
||||
if let FRMPayload::MACCommandSet(_) = &self.frm_payload.as_ref().unwrap() {
|
||||
if self.f_port.unwrap() != 0 {
|
||||
return Err(anyhow!(
|
||||
"f_port must be set to 0 for mac-commands in frm_payload"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut b = Vec::new();
|
||||
|
||||
// fhdr
|
||||
b.append(&mut self.fhdr.to_vec()?);
|
||||
|
||||
// f_port
|
||||
if let Some(v) = self.f_port {
|
||||
b.push(v);
|
||||
}
|
||||
|
||||
// frm_payload
|
||||
if let Some(v) = &self.frm_payload {
|
||||
b.append(&mut v.to_vec()?);
|
||||
}
|
||||
|
||||
Ok(b)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, PartialEq, Debug, Clone)]
|
||||
pub struct RejoinRequestType02Payload {
|
||||
pub rejoin_type: JoinType,
|
||||
pub netid: NetID,
|
||||
pub dev_eui: EUI64,
|
||||
pub rj_count_0: u16,
|
||||
}
|
||||
|
||||
impl RejoinRequestType02Payload {
|
||||
pub fn from_slice(b: &[u8]) -> Result<Self> {
|
||||
if b.len() != 14 {
|
||||
return Err(anyhow!(
|
||||
"rejoin-request type 0/2 payload must be exactly 14 bytes"
|
||||
));
|
||||
}
|
||||
|
||||
Ok(RejoinRequestType02Payload {
|
||||
rejoin_type: match b[0] {
|
||||
0x00 => JoinType::RejoinType0,
|
||||
0x02 => JoinType::RejoinType2,
|
||||
_ => {
|
||||
return Err(anyhow!("invalid rejoin_type"));
|
||||
}
|
||||
},
|
||||
netid: {
|
||||
let mut netid: [u8; 3] = [0; 3];
|
||||
netid.clone_from_slice(&b[1..4]);
|
||||
NetID::from_le_bytes(netid)
|
||||
},
|
||||
dev_eui: {
|
||||
let mut deveui: [u8; 8] = [0; 8];
|
||||
deveui.clone_from_slice(&b[4..12]);
|
||||
EUI64::from_le_bytes(deveui)
|
||||
},
|
||||
rj_count_0: u16::from_le_bytes([b[12], b[13]]),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Result<Vec<u8>> {
|
||||
let mut b = Vec::with_capacity(14);
|
||||
b.push(match self.rejoin_type {
|
||||
JoinType::RejoinType0 => 0x00,
|
||||
JoinType::RejoinType2 => 0x02,
|
||||
_ => {
|
||||
return Err(anyhow!("rejoin_type must be 0 or 2"));
|
||||
}
|
||||
});
|
||||
|
||||
b.extend_from_slice(&self.netid.to_le_bytes());
|
||||
b.extend_from_slice(&self.dev_eui.to_le_bytes());
|
||||
b.extend_from_slice(&self.rj_count_0.to_le_bytes());
|
||||
|
||||
Ok(b)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, PartialEq, Debug, Clone)]
|
||||
pub struct RejoinRequestType1Payload {
|
||||
pub rejoin_type: JoinType,
|
||||
pub join_eui: EUI64,
|
||||
pub dev_eui: EUI64,
|
||||
pub rj_count_1: u16,
|
||||
}
|
||||
|
||||
impl RejoinRequestType1Payload {
|
||||
pub fn from_slice(b: &[u8]) -> Result<Self> {
|
||||
if b.len() != 19 {
|
||||
return Err(anyhow!(
|
||||
"rejoin-request type 1 payload must be exactly 19 bytes"
|
||||
));
|
||||
}
|
||||
|
||||
Ok(RejoinRequestType1Payload {
|
||||
rejoin_type: match b[0] {
|
||||
0x01 => JoinType::RejoinType1,
|
||||
_ => {
|
||||
return Err(anyhow!("invalid rejoin_type"));
|
||||
}
|
||||
},
|
||||
join_eui: {
|
||||
let mut joineui: [u8; 8] = [0; 8];
|
||||
joineui.clone_from_slice(&b[1..9]);
|
||||
EUI64::from_le_bytes(joineui)
|
||||
},
|
||||
dev_eui: {
|
||||
let mut deveui: [u8; 8] = [0; 8];
|
||||
deveui.clone_from_slice(&b[9..17]);
|
||||
EUI64::from_le_bytes(deveui)
|
||||
},
|
||||
rj_count_1: u16::from_le_bytes([b[17], b[18]]),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Result<Vec<u8>> {
|
||||
let mut b = Vec::with_capacity(19);
|
||||
b.push(match self.rejoin_type {
|
||||
JoinType::RejoinType1 => 0x01,
|
||||
_ => {
|
||||
return Err(anyhow!("rejoin_type must be 1"));
|
||||
}
|
||||
});
|
||||
|
||||
b.extend_from_slice(&self.join_eui.to_le_bytes());
|
||||
b.extend_from_slice(&self.dev_eui.to_le_bytes());
|
||||
b.extend_from_slice(&self.rj_count_1.to_le_bytes());
|
||||
|
||||
Ok(b)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::super::cflist::CFListChannels;
|
||||
use super::super::maccommand::MACCommand;
|
||||
use super::*;
|
||||
|
||||
struct PayloadTest {
|
||||
pl: Payload,
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
struct JoinAcceptTest {
|
||||
pl: JoinAcceptPayload,
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_join_request_payload() {
|
||||
let tests = vec![PayloadTest {
|
||||
pl: Payload::JoinRequest(JoinRequestPayload {
|
||||
join_eui: EUI64::from_str("0102030405060708").unwrap(),
|
||||
dev_eui: EUI64::from_str("0807060504030201").unwrap(),
|
||||
dev_nonce: 1024,
|
||||
}),
|
||||
bytes: vec![
|
||||
0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
||||
0x07, 0x08, 0x00, 0x04,
|
||||
],
|
||||
}];
|
||||
|
||||
for tst in tests {
|
||||
assert_eq!(tst.bytes, tst.pl.to_vec().unwrap());
|
||||
assert_eq!(
|
||||
tst.pl,
|
||||
Payload::from_slice(MType::JoinRequest, &tst.bytes).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_join_accept_payload() {
|
||||
// in this case the payload is converted to Raw as the join-accept must first be decrypted
|
||||
// before it can be decoded
|
||||
assert_eq!(
|
||||
Payload::Raw(vec![0x01, 0x02, 0x03]),
|
||||
Payload::from_slice(MType::JoinAccept, &vec![0x01, 0x02, 0x03]).unwrap()
|
||||
);
|
||||
|
||||
// test decoding the (decrypted) join-accept payload
|
||||
let tests = vec![
|
||||
JoinAcceptTest {
|
||||
pl: JoinAcceptPayload {
|
||||
join_nonce: 65793,
|
||||
home_netid: NetID::from_str("020202").unwrap(),
|
||||
devaddr: DevAddr::from_str("01020304").unwrap(),
|
||||
dl_settings: DLSettings {
|
||||
rx2_dr: 7,
|
||||
rx1_dr_offset: 6,
|
||||
opt_neg: false,
|
||||
},
|
||||
rx_delay: 9,
|
||||
cflist: None,
|
||||
},
|
||||
bytes: vec![
|
||||
0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x04, 0x03, 0x02, 0x01, 0x67, 0x09,
|
||||
],
|
||||
},
|
||||
JoinAcceptTest {
|
||||
pl: JoinAcceptPayload {
|
||||
join_nonce: 65793,
|
||||
home_netid: NetID::from_str("020202").unwrap(),
|
||||
devaddr: DevAddr::from_str("01020304").unwrap(),
|
||||
dl_settings: DLSettings {
|
||||
rx2_dr: 7,
|
||||
rx1_dr_offset: 6,
|
||||
opt_neg: false,
|
||||
},
|
||||
rx_delay: 9,
|
||||
cflist: Some(CFList::Channels(CFListChannels::new([
|
||||
867100000, 867300000, 867500000, 867700000, 867900000,
|
||||
]))),
|
||||
},
|
||||
bytes: vec![
|
||||
0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x04, 0x03, 0x02, 0x01, 0x67, 0x09, 0x18,
|
||||
0x4f, 0x84, 0xe8, 0x56, 0x84, 0xb8, 0x5e, 0x84, 0x88, 0x66, 0x84, 0x58, 0x6e,
|
||||
0x84, 0x00,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
for tst in tests {
|
||||
assert_eq!(tst.bytes, tst.pl.to_vec().unwrap());
|
||||
assert_eq!(tst.pl, JoinAcceptPayload::from_slice(&tst.bytes).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mac_payload() {
|
||||
let tests = vec![
|
||||
PayloadTest {
|
||||
pl: Payload::MACPayload(MACPayload {
|
||||
fhdr: FHDR {
|
||||
devaddr: DevAddr::from_str("01020304").unwrap(),
|
||||
f_ctrl: FCtrl {
|
||||
adr: true,
|
||||
..Default::default()
|
||||
},
|
||||
f_cnt: 123,
|
||||
f_opts: MACCommandSet::new(vec![]),
|
||||
},
|
||||
f_port: None,
|
||||
frm_payload: None,
|
||||
}),
|
||||
bytes: vec![0x04, 0x03, 0x02, 0x01, 0x80, 0x7b, 0x00],
|
||||
},
|
||||
PayloadTest {
|
||||
pl: Payload::MACPayload(MACPayload {
|
||||
fhdr: FHDR {
|
||||
devaddr: DevAddr::from_str("01020304").unwrap(),
|
||||
f_ctrl: FCtrl {
|
||||
adr: true,
|
||||
f_opts_len: 3, // only needed for assert_eq!
|
||||
..Default::default()
|
||||
},
|
||||
f_cnt: 123,
|
||||
f_opts: MACCommandSet::new(vec![MACCommand::Raw(vec![0x01, 0x02, 0x03])]),
|
||||
},
|
||||
f_port: None,
|
||||
frm_payload: None,
|
||||
}),
|
||||
bytes: vec![0x04, 0x03, 0x02, 0x01, 0x83, 0x7b, 0x00, 0x01, 0x02, 0x03],
|
||||
},
|
||||
PayloadTest {
|
||||
pl: Payload::MACPayload(MACPayload {
|
||||
fhdr: FHDR {
|
||||
devaddr: DevAddr::from_str("01020304").unwrap(),
|
||||
f_ctrl: FCtrl {
|
||||
adr: true,
|
||||
..Default::default()
|
||||
},
|
||||
f_cnt: 123,
|
||||
f_opts: MACCommandSet::new(vec![]),
|
||||
},
|
||||
f_port: Some(10),
|
||||
frm_payload: None,
|
||||
}),
|
||||
bytes: vec![0x04, 0x03, 0x02, 0x01, 0x80, 0x7b, 0x00, 0x0a],
|
||||
},
|
||||
PayloadTest {
|
||||
pl: Payload::MACPayload(MACPayload {
|
||||
fhdr: FHDR {
|
||||
devaddr: DevAddr::from_str("01020304").unwrap(),
|
||||
f_ctrl: FCtrl {
|
||||
adr: true,
|
||||
..Default::default()
|
||||
},
|
||||
f_cnt: 123,
|
||||
f_opts: MACCommandSet::new(vec![]),
|
||||
},
|
||||
f_port: Some(10),
|
||||
frm_payload: Some(FRMPayload::Raw(vec![0x01, 0x02, 0x03])),
|
||||
}),
|
||||
bytes: vec![
|
||||
0x04, 0x03, 0x02, 0x01, 0x80, 0x7b, 0x00, 0x0a, 0x01, 0x02, 0x03,
|
||||
],
|
||||
},
|
||||
PayloadTest {
|
||||
pl: Payload::MACPayload(MACPayload {
|
||||
fhdr: FHDR {
|
||||
devaddr: DevAddr::from_str("01020304").unwrap(),
|
||||
f_ctrl: FCtrl {
|
||||
adr: true,
|
||||
f_opts_len: 3,
|
||||
..Default::default()
|
||||
},
|
||||
f_cnt: 123,
|
||||
f_opts: MACCommandSet::new(vec![MACCommand::Raw(vec![0x03, 0x02, 0x01])]),
|
||||
},
|
||||
f_port: Some(10),
|
||||
frm_payload: Some(FRMPayload::Raw(vec![0x01, 0x02, 0x03])),
|
||||
}),
|
||||
bytes: vec![
|
||||
0x04, 0x03, 0x02, 0x01, 0x83, 0x7b, 0x00, 0x03, 0x02, 0x01, 0x0a, 0x01, 0x02,
|
||||
0x03,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
for tst in tests {
|
||||
assert_eq!(tst.bytes, tst.pl.to_vec().unwrap());
|
||||
assert_eq!(
|
||||
tst.pl,
|
||||
Payload::from_slice(MType::UnconfirmedDataUp, &tst.bytes).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rejoin_request() {
|
||||
let tests = vec![
|
||||
PayloadTest {
|
||||
pl: Payload::RejoinRequestType02(RejoinRequestType02Payload {
|
||||
rejoin_type: JoinType::RejoinType0,
|
||||
netid: NetID::from_str("010203").unwrap(),
|
||||
dev_eui: EUI64::from_str("0102030405060708").unwrap(),
|
||||
rj_count_0: 219,
|
||||
}),
|
||||
bytes: vec![
|
||||
0x00, 0x03, 0x02, 0x01, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0xdb,
|
||||
0x00,
|
||||
],
|
||||
},
|
||||
PayloadTest {
|
||||
pl: Payload::RejoinRequestType02(RejoinRequestType02Payload {
|
||||
rejoin_type: JoinType::RejoinType2,
|
||||
netid: NetID::from_str("010203").unwrap(),
|
||||
dev_eui: EUI64::from_str("0102030405060708").unwrap(),
|
||||
rj_count_0: 219,
|
||||
}),
|
||||
bytes: vec![
|
||||
0x02, 0x03, 0x02, 0x01, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0xdb,
|
||||
0x00,
|
||||
],
|
||||
},
|
||||
PayloadTest {
|
||||
pl: Payload::RejoinRequestType1(RejoinRequestType1Payload {
|
||||
rejoin_type: JoinType::RejoinType1,
|
||||
join_eui: EUI64::from_str("0102030405060708").unwrap(),
|
||||
dev_eui: EUI64::from_str("0807060504030201").unwrap(),
|
||||
rj_count_1: 219,
|
||||
}),
|
||||
bytes: vec![
|
||||
0x01, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x02, 0x03, 0x04,
|
||||
0x05, 0x06, 0x07, 0x08, 0xdb, 0x00,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
for tst in tests {
|
||||
assert_eq!(tst.bytes, tst.pl.to_vec().unwrap());
|
||||
assert_eq!(
|
||||
tst.pl,
|
||||
Payload::from_slice(MType::RejoinRequest, &tst.bytes).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_proprietary() {
|
||||
let tests = vec![PayloadTest {
|
||||
pl: Payload::Raw(vec![0x01, 0x02, 0x3]),
|
||||
bytes: vec![0x01, 0x02, 0x03],
|
||||
}];
|
||||
|
||||
for tst in tests {
|
||||
assert_eq!(tst.bytes, tst.pl.to_vec().unwrap());
|
||||
assert_eq!(
|
||||
tst.pl,
|
||||
Payload::from_slice(MType::Proprietary, &tst.bytes).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user