use crate::encoding::{reader::Reader, writer::Writer};
use crate::{DecodeError, EncodeError};
pub const NPDU_VERSION: u8 = 0x01;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NpduAddress {
pub network: u16,
pub mac: [u8; 18],
pub mac_len: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Npdu {
pub control: u8,
pub destination: Option<NpduAddress>,
pub source: Option<NpduAddress>,
pub hop_count: Option<u8>,
pub message_type: Option<u8>,
pub vendor_id: Option<u16>,
}
impl Npdu {
pub const fn new(control: u8) -> Self {
Self {
control,
destination: None,
source: None,
hop_count: None,
message_type: None,
vendor_id: None,
}
}
pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
let mut control = self.control;
if self.destination.is_some() {
control |= 0x20;
} else {
control &= !0x20;
}
if self.source.is_some() {
control |= 0x08;
} else {
control &= !0x08;
}
if self.message_type.is_some() {
control |= 0x80;
} else {
control &= !0x80;
}
w.write_u8(NPDU_VERSION)?;
w.write_u8(control)?;
if let Some(dest) = self.destination {
encode_addr(w, dest)?;
}
if let Some(src) = self.source {
encode_addr(w, src)?;
}
if self.destination.is_some() {
w.write_u8(self.hop_count.unwrap_or(255))?;
}
if let Some(mt) = self.message_type {
w.write_u8(mt)?;
if mt >= 0x80 {
w.write_be_u16(self.vendor_id.unwrap_or(0))?;
}
}
Ok(())
}
pub fn decode(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
let version = r.read_u8()?;
if version != NPDU_VERSION {
return Err(DecodeError::InvalidValue);
}
let control = r.read_u8()?;
let has_dest = (control & 0x20) != 0;
let has_src = (control & 0x08) != 0;
let is_network_msg = (control & 0x80) != 0;
let destination = if has_dest {
Some(decode_addr(r)?)
} else {
None
};
let source = if has_src { Some(decode_addr(r)?) } else { None };
let hop_count = if has_dest { Some(r.read_u8()?) } else { None };
let (message_type, vendor_id) = if is_network_msg {
let mt = r.read_u8()?;
let vid = if mt >= 0x80 {
Some(r.read_be_u16()?)
} else {
None
};
(Some(mt), vid)
} else {
(None, None)
};
Ok(Self {
control,
destination,
source,
hop_count,
message_type,
vendor_id,
})
}
}
fn encode_addr(w: &mut Writer<'_>, addr: NpduAddress) -> Result<(), EncodeError> {
if addr.mac_len as usize > addr.mac.len() {
return Err(EncodeError::InvalidLength);
}
w.write_be_u16(addr.network)?;
w.write_u8(addr.mac_len)?;
w.write_all(&addr.mac[..addr.mac_len as usize])
}
fn decode_addr(r: &mut Reader<'_>) -> Result<NpduAddress, DecodeError> {
let network = r.read_be_u16()?;
let mac_len = r.read_u8()?;
if mac_len as usize > 18 {
return Err(DecodeError::InvalidLength);
}
let mut mac = [0u8; 18];
let src = r.read_exact(mac_len as usize)?;
mac[..mac_len as usize].copy_from_slice(src);
Ok(NpduAddress {
network,
mac,
mac_len,
})
}
#[cfg(test)]
mod tests {
use super::{Npdu, NpduAddress};
use crate::encoding::{reader::Reader, writer::Writer};
#[test]
fn npdu_roundtrip() {
let mut p = Npdu::new(0x20);
p.destination = Some(NpduAddress {
network: 1,
mac: {
let mut m = [0u8; 18];
m[..6].copy_from_slice(&[192, 168, 1, 2, 0xBA, 0xC0]);
m
},
mac_len: 6,
});
p.hop_count = Some(255);
let mut buf = [0u8; 32];
let mut w = Writer::new(&mut buf);
p.encode(&mut w).unwrap();
let mut r = Reader::new(w.as_written());
let dec = Npdu::decode(&mut r).unwrap();
assert_eq!(dec.control, p.control);
assert_eq!(dec.destination.unwrap().network, 1);
}
#[test]
fn npdu_ipv6_mac_roundtrip() {
let mut p = Npdu::new(0x20);
let mut mac = [0u8; 18];
mac[..16].copy_from_slice(&[
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x0c, 0x29, 0xff, 0xfe, 0x5a, 0x1c, 0x3d,
]);
mac[16..].copy_from_slice(&0xBAC0u16.to_be_bytes());
p.destination = Some(NpduAddress {
network: 2,
mac,
mac_len: 18,
});
p.hop_count = Some(255);
let mut buf = [0u8; 48];
let mut w = Writer::new(&mut buf);
p.encode(&mut w).unwrap();
let mut r = Reader::new(w.as_written());
let dec = Npdu::decode(&mut r).unwrap();
let dest = dec.destination.unwrap();
assert_eq!(dest.network, 2);
assert_eq!(dest.mac_len, 18);
assert_eq!(&dest.mac[..18], &mac[..]);
}
#[test]
fn network_message_vendor_id_only_for_vendor_types() {
let mut p = Npdu::new(0x80);
p.message_type = Some(0x80);
p.vendor_id = Some(260);
let mut buf = [0u8; 16];
let mut w = Writer::new(&mut buf);
p.encode(&mut w).unwrap();
let mut r = Reader::new(w.as_written());
let dec = Npdu::decode(&mut r).unwrap();
assert_eq!(dec.message_type, Some(0x80));
assert_eq!(dec.vendor_id, Some(260));
}
}