mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-01-19 02:56:23 +00:00
Implement DevAddrPrefix type.
This type defines the (NetID derived) prefix that must be used when generating device addresses. This can be retrieved from the NetID, but it is also possible to define a prefix that defines a smaller address range within the NetID. See also #49.
This commit is contained in:
parent
e04f991e76
commit
6cc813aed1
@ -2,11 +2,97 @@ use std::fmt;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{Serialize, Serializer};
|
use serde::de::{self, Visitor};
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
use super::netid::NetID;
|
use super::netid::NetID;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone, Default)]
|
||||||
|
pub struct DevAddrPrefix([u8; 4], u32);
|
||||||
|
|
||||||
|
impl DevAddrPrefix {
|
||||||
|
pub fn new(prefix: [u8; 4], size: u32) -> Self {
|
||||||
|
DevAddrPrefix(prefix, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prefix(&self) -> [u8; 4] {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> u32 {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DevAddrPrefix {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}/{}", hex::encode(&self.0), self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for DevAddrPrefix {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}/{}", hex::encode(&self.0), self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for DevAddrPrefix {
|
||||||
|
type Err = Error;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let s = s.to_string();
|
||||||
|
let parts: Vec<&str> = s.split("/").collect();
|
||||||
|
if parts.len() != 2 {
|
||||||
|
return Err(Error::DevAddrPrefixFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if parts[0].len() != 8 {
|
||||||
|
return Err(Error::DevAddrPrefixFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut mask: [u8; 4] = [0; 4];
|
||||||
|
hex::decode_to_slice(&parts[0], &mut mask)?;
|
||||||
|
let size: u32 = parts[1].parse().map_err(|_| Error::DevAddrPrefixFormat)?;
|
||||||
|
|
||||||
|
Ok(DevAddrPrefix(mask, size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for DevAddrPrefix {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for DevAddrPrefix {
|
||||||
|
fn deserialize<D>(deserialize: D) -> Result<DevAddrPrefix, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserialize.deserialize_str(DevAddrPrefixVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DevAddrPrefixVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for DevAddrPrefixVisitor {
|
||||||
|
type Value = DevAddrPrefix;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("A DevAddrPrefix in the format 00000000/0 is expected")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
DevAddrPrefix::from_str(value).map_err(|e| E::custom(format!("{}", e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone, AsExpression, FromSqlRow, Default)]
|
#[derive(PartialEq, Copy, Clone, AsExpression, FromSqlRow, Default)]
|
||||||
#[diesel(sql_type = diesel::sql_types::Binary)]
|
#[diesel(sql_type = diesel::sql_types::Binary)]
|
||||||
pub struct DevAddr([u8; 4]);
|
pub struct DevAddr([u8; 4]);
|
||||||
@ -54,7 +140,7 @@ impl DevAddr {
|
|||||||
|
|
||||||
pub fn is_net_id(&self, net_id: NetID) -> bool {
|
pub fn is_net_id(&self, net_id: NetID) -> bool {
|
||||||
let mut dev_addr = *self;
|
let mut dev_addr = *self;
|
||||||
dev_addr.set_addr_prefix(&net_id);
|
dev_addr.set_dev_addr_prefix(net_id.dev_addr_prefix());
|
||||||
|
|
||||||
*self == dev_addr
|
*self == dev_addr
|
||||||
}
|
}
|
||||||
@ -83,18 +169,18 @@ impl DevAddr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_addr_prefix(&mut self, netid: &NetID) {
|
pub fn set_dev_addr_prefix(&mut self, prefix: DevAddrPrefix) {
|
||||||
match netid.netid_type() {
|
// convert devaddr to u32
|
||||||
0 => self.set_set_addr_prefix(1, 6, netid),
|
let mut devaddr = u32::from_be_bytes(self.0);
|
||||||
1 => self.set_set_addr_prefix(2, 6, netid),
|
|
||||||
2 => self.set_set_addr_prefix(3, 9, netid),
|
// clean the prefix bits
|
||||||
3 => self.set_set_addr_prefix(4, 11, netid),
|
let mask = u32::MAX; // all u32 bits to 1
|
||||||
4 => self.set_set_addr_prefix(5, 12, netid),
|
devaddr &= !(mask << (32 - prefix.size()));
|
||||||
5 => self.set_set_addr_prefix(6, 13, netid),
|
|
||||||
6 => self.set_set_addr_prefix(7, 15, netid),
|
// set the prefix
|
||||||
7 => self.set_set_addr_prefix(8, 17, netid),
|
devaddr |= u32::from_be_bytes(prefix.prefix());
|
||||||
_ => {}
|
|
||||||
}
|
self.0 = devaddr.to_be_bytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_nwkid(&self, prefix_length: u32, nwkid_bits: u32) -> Vec<u8> {
|
fn get_nwkid(&self, prefix_length: u32, nwkid_bits: u32) -> Vec<u8> {
|
||||||
@ -117,31 +203,6 @@ impl DevAddr {
|
|||||||
|
|
||||||
out[4 - blen..].to_vec()
|
out[4 - blen..].to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_set_addr_prefix(&mut self, prefix_length: u32, nwkid_bits: u32, netid: &NetID) {
|
|
||||||
// convert devaddr to u32
|
|
||||||
let mut devaddr = u32::from_be_bytes(self.0);
|
|
||||||
|
|
||||||
// clear the bits for the prefix and NwkID
|
|
||||||
let mask = u32::MAX; // all u32 bits to 1
|
|
||||||
devaddr &= !(mask << (32 - prefix_length - nwkid_bits));
|
|
||||||
|
|
||||||
// set the type prefix
|
|
||||||
let prefix: u32 = 254 << (32 - prefix_length);
|
|
||||||
let mut devaddr = devaddr | prefix;
|
|
||||||
|
|
||||||
// set the NwkID
|
|
||||||
let mut netid_bytes: [u8; 4] = [0; 4];
|
|
||||||
let netid_id = netid.id();
|
|
||||||
netid_bytes[4 - netid_id.len()..].clone_from_slice(&netid_id);
|
|
||||||
|
|
||||||
let mut nwkid = u32::from_be_bytes(netid_bytes);
|
|
||||||
nwkid <<= 32 - nwkid_bits; // truncate the MSB of the NetID ID field
|
|
||||||
nwkid >>= prefix_length; // shift base for the prefix MSB
|
|
||||||
devaddr |= nwkid;
|
|
||||||
|
|
||||||
self.0 = devaddr.to_be_bytes();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for DevAddr {
|
impl fmt::Display for DevAddr {
|
||||||
@ -237,42 +298,49 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_to_le_bytes() {
|
fn test_dev_addr_prefix() {
|
||||||
|
let p = DevAddrPrefix::from_str("01000000/8").unwrap();
|
||||||
|
assert_eq!(DevAddrPrefix::new([1, 0, 0, 0], 8), p);
|
||||||
|
assert_eq!("01000000/8", p.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dev_addr_to_le_bytes() {
|
||||||
for tst in tests() {
|
for tst in tests() {
|
||||||
assert_eq!(tst.bytes, tst.devaddr.to_le_bytes());
|
assert_eq!(tst.bytes, tst.devaddr.to_le_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_le_bytes() {
|
fn test_dev_addr_from_le_bytes() {
|
||||||
for tst in tests() {
|
for tst in tests() {
|
||||||
assert_eq!(tst.devaddr, DevAddr::from_le_bytes(tst.bytes));
|
assert_eq!(tst.devaddr, DevAddr::from_le_bytes(tst.bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_netid_type() {
|
fn test_dev_addr_netid_type() {
|
||||||
for tst in tests() {
|
for tst in tests() {
|
||||||
assert_eq!(tst.netid_type, tst.devaddr.netid_type());
|
assert_eq!(tst.netid_type, tst.devaddr.netid_type());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nwkid() {
|
fn test_dev_addr_nwkid() {
|
||||||
for tst in tests() {
|
for tst in tests() {
|
||||||
assert_eq!(tst.nwkid, tst.devaddr.nwkid());
|
assert_eq!(tst.nwkid, tst.devaddr.nwkid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string() {
|
fn test_dev_addr_string() {
|
||||||
for tst in tests() {
|
for tst in tests() {
|
||||||
assert_eq!(tst.string, tst.devaddr.to_string());
|
assert_eq!(tst.string, tst.devaddr.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_addr_prefix() {
|
fn test_dev_addr_set_addr_prefix() {
|
||||||
let tests = vec![
|
let tests = vec![
|
||||||
SetAddrPrefixTest {
|
SetAddrPrefixTest {
|
||||||
devaddr: DevAddr::from_be_bytes([0xff, 0xff, 0xff, 0xff]),
|
devaddr: DevAddr::from_be_bytes([0xff, 0xff, 0xff, 0xff]),
|
||||||
@ -288,7 +356,7 @@ mod tests {
|
|||||||
|
|
||||||
for tst in tests {
|
for tst in tests {
|
||||||
let mut devaddr = tst.devaddr.clone();
|
let mut devaddr = tst.devaddr.clone();
|
||||||
devaddr.set_addr_prefix(&tst.netid);
|
devaddr.set_dev_addr_prefix(tst.netid.dev_addr_prefix());
|
||||||
assert_eq!(tst.expected_devaddr, devaddr);
|
assert_eq!(tst.expected_devaddr, devaddr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ pub enum Error {
|
|||||||
#[error("DevAddr expects exactly 4 bytes")]
|
#[error("DevAddr expects exactly 4 bytes")]
|
||||||
DevAddrLength,
|
DevAddrLength,
|
||||||
|
|
||||||
|
#[error("DevAddrPrefix must be in the form 00000000/0")]
|
||||||
|
DevAddrPrefixFormat,
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
FromHexError(#[from] hex::FromHexError),
|
FromHexError(#[from] hex::FromHexError),
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ use anyhow::Result;
|
|||||||
use serde::de::{self, Visitor};
|
use serde::de::{self, Visitor};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
use crate::devaddr::DevAddrPrefix;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Clone, Copy, Hash, Eq)]
|
#[derive(Default, PartialEq, Clone, Copy, Hash, Eq)]
|
||||||
@ -74,6 +75,38 @@ impl NetID {
|
|||||||
|
|
||||||
out[4 - blen..].to_vec()
|
out[4 - blen..].to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dev_addr_prefix(&self) -> DevAddrPrefix {
|
||||||
|
match self.netid_type() {
|
||||||
|
0 => self.get_dev_addr_prefix(1, 6),
|
||||||
|
1 => self.get_dev_addr_prefix(2, 6),
|
||||||
|
2 => self.get_dev_addr_prefix(3, 9),
|
||||||
|
3 => self.get_dev_addr_prefix(4, 11),
|
||||||
|
4 => self.get_dev_addr_prefix(5, 12),
|
||||||
|
5 => self.get_dev_addr_prefix(6, 13),
|
||||||
|
6 => self.get_dev_addr_prefix(7, 15),
|
||||||
|
7 => self.get_dev_addr_prefix(8, 17),
|
||||||
|
_ => panic!("netid_type bug"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_dev_addr_prefix(&self, prefix_length: u32, nwkid_bits: u32) -> DevAddrPrefix {
|
||||||
|
// type prefix
|
||||||
|
let mut prefix: u32 = 254 << (32 - prefix_length);
|
||||||
|
|
||||||
|
// NwkID prefix
|
||||||
|
let mut netid_bytes: [u8; 4] = [0; 4];
|
||||||
|
let netid_id = self.id();
|
||||||
|
netid_bytes[4 - netid_id.len()..].clone_from_slice(&netid_id);
|
||||||
|
let mut nwkid = u32::from_be_bytes(netid_bytes);
|
||||||
|
nwkid <<= 32 - nwkid_bits; // truncate the MSB of the NetID ID field
|
||||||
|
nwkid >>= prefix_length; // shift base for the prefix MSB
|
||||||
|
|
||||||
|
// merge type prefix with nwkid.
|
||||||
|
prefix |= nwkid;
|
||||||
|
|
||||||
|
DevAddrPrefix::new(prefix.to_be_bytes(), prefix_length + nwkid_bits)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for NetID {
|
impl fmt::Display for NetID {
|
||||||
@ -144,6 +177,7 @@ mod tests {
|
|||||||
id: Vec<u8>,
|
id: Vec<u8>,
|
||||||
bytes: [u8; 3],
|
bytes: [u8; 3],
|
||||||
string: String,
|
string: String,
|
||||||
|
dev_addr_prefix: DevAddrPrefix,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tests() -> Vec<Test> {
|
fn tests() -> Vec<Test> {
|
||||||
@ -154,6 +188,7 @@ mod tests {
|
|||||||
id: vec![0x2d],
|
id: vec![0x2d],
|
||||||
bytes: [0x6d, 0x00, 0x00],
|
bytes: [0x6d, 0x00, 0x00],
|
||||||
string: "00006d".into(),
|
string: "00006d".into(),
|
||||||
|
dev_addr_prefix: DevAddrPrefix::new([0x5a, 0x00, 0x00, 0x00], 7),
|
||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
netid: NetID::from_be_bytes([0x20, 0x00, 0x6d]),
|
netid: NetID::from_be_bytes([0x20, 0x00, 0x6d]),
|
||||||
@ -161,6 +196,7 @@ mod tests {
|
|||||||
id: vec![0x2d],
|
id: vec![0x2d],
|
||||||
bytes: [0x6d, 0x00, 0x20],
|
bytes: [0x6d, 0x00, 0x20],
|
||||||
string: "20006d".into(),
|
string: "20006d".into(),
|
||||||
|
dev_addr_prefix: DevAddrPrefix::new([0xad, 0x00, 0x00, 0x00], 8),
|
||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
netid: NetID::from_be_bytes([0x40, 0x03, 0x6d]),
|
netid: NetID::from_be_bytes([0x40, 0x03, 0x6d]),
|
||||||
@ -168,6 +204,7 @@ mod tests {
|
|||||||
id: vec![0x01, 0x6d],
|
id: vec![0x01, 0x6d],
|
||||||
bytes: [0x6d, 0x03, 0x40],
|
bytes: [0x6d, 0x03, 0x40],
|
||||||
string: "40036d".into(),
|
string: "40036d".into(),
|
||||||
|
dev_addr_prefix: DevAddrPrefix::new([0xd6, 0xd0, 0x00, 0x00], 12),
|
||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
netid: NetID::from_be_bytes([0x76, 0xdb, 0x6d]),
|
netid: NetID::from_be_bytes([0x76, 0xdb, 0x6d]),
|
||||||
@ -175,6 +212,7 @@ mod tests {
|
|||||||
id: vec![0x16, 0xdb, 0x6d],
|
id: vec![0x16, 0xdb, 0x6d],
|
||||||
bytes: [0x6d, 0xdb, 0x76],
|
bytes: [0x6d, 0xdb, 0x76],
|
||||||
string: "76db6d".into(),
|
string: "76db6d".into(),
|
||||||
|
dev_addr_prefix: DevAddrPrefix::new([0xe6, 0xda, 0x00, 0x00], 15),
|
||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
netid: NetID::from_be_bytes([0x96, 0xdb, 0x6d]),
|
netid: NetID::from_be_bytes([0x96, 0xdb, 0x6d]),
|
||||||
@ -182,6 +220,7 @@ mod tests {
|
|||||||
id: vec![0x16, 0xdb, 0x6d],
|
id: vec![0x16, 0xdb, 0x6d],
|
||||||
bytes: [0x6d, 0xdb, 0x96],
|
bytes: [0x6d, 0xdb, 0x96],
|
||||||
string: "96db6d".into(),
|
string: "96db6d".into(),
|
||||||
|
dev_addr_prefix: DevAddrPrefix::new([0xf5, 0xb6, 0x80, 0x00], 17),
|
||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
netid: NetID::from_be_bytes([0xb6, 0xdb, 0x6d]),
|
netid: NetID::from_be_bytes([0xb6, 0xdb, 0x6d]),
|
||||||
@ -189,6 +228,7 @@ mod tests {
|
|||||||
id: vec![0x16, 0xdb, 0x6d],
|
id: vec![0x16, 0xdb, 0x6d],
|
||||||
bytes: [0x6d, 0xdb, 0xb6],
|
bytes: [0x6d, 0xdb, 0xb6],
|
||||||
string: "b6db6d".into(),
|
string: "b6db6d".into(),
|
||||||
|
dev_addr_prefix: DevAddrPrefix::new([0xfb, 0x6d, 0xa0, 0x00], 19),
|
||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
netid: NetID::from_be_bytes([0xd6, 0xdb, 0x6d]),
|
netid: NetID::from_be_bytes([0xd6, 0xdb, 0x6d]),
|
||||||
@ -196,6 +236,7 @@ mod tests {
|
|||||||
id: vec![0x16, 0xdb, 0x6d],
|
id: vec![0x16, 0xdb, 0x6d],
|
||||||
bytes: [0x6d, 0xdb, 0xd6],
|
bytes: [0x6d, 0xdb, 0xd6],
|
||||||
string: "d6db6d".into(),
|
string: "d6db6d".into(),
|
||||||
|
dev_addr_prefix: DevAddrPrefix::new([0xfd, 0x6d, 0xb4, 00], 22),
|
||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
netid: NetID::from_be_bytes([0xf6, 0xdb, 0x6d]),
|
netid: NetID::from_be_bytes([0xf6, 0xdb, 0x6d]),
|
||||||
@ -203,6 +244,7 @@ mod tests {
|
|||||||
id: vec![0x16, 0xdb, 0x6d],
|
id: vec![0x16, 0xdb, 0x6d],
|
||||||
bytes: [0x6d, 0xdb, 0xf6],
|
bytes: [0x6d, 0xdb, 0xf6],
|
||||||
string: "f6db6d".into(),
|
string: "f6db6d".into(),
|
||||||
|
dev_addr_prefix: DevAddrPrefix::new([0xfe, 0x6d, 0xb6, 0x80], 25),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -248,4 +290,11 @@ mod tests {
|
|||||||
assert_eq!(tst.netid, NetID::from_str(&tst.string).unwrap());
|
assert_eq!(tst.netid, NetID::from_str(&tst.string).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dev_addr_prefix() {
|
||||||
|
for tst in tests() {
|
||||||
|
assert_eq!(tst.dev_addr_prefix, tst.netid.dev_addr_prefix());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user