/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* Author: Rayhaan Jaufeerally <rayhaan@rayhaan.ch>
*
* LLDP packet parser for the protocol defined in IEEE 802.1AB-2016.
*/
/// lldppkt is an implementation of the wire format of IEEE 802.1AB-2016
use crate::pktparser;
use byteorder::{BigEndian, WriteBytesExt};
use pktparser::{Deserialise, Serialise};
use std::convert::TryInto;
use std::fmt::Formatter;
use std::io::ErrorKind;
/// LldpPacket represents a LLDP PDU that can be read from / written to the wire.
/// LLDP PDUs are simply a concatenation of TLVs in an Ethernet frame with type 0x88cc.
/// At the end of the PDU there is an empty TLV header.
/// The first three TLVs must be: ChassisID, PortID and TTL.
#[derive(Debug, Eq, PartialEq)]
pub struct LldpPacket {
pub tlvs: Vec<LldpTlv>,
}
impl LldpPacket {
fn validate_format(&self) -> Result<(), std::io::Error> {
if self.tlvs.len() < 4 {
return Err(std::io::Error::new(
ErrorKind::InvalidData,
"At least 4 mandatory TLVs are required in an LLDP PDU.",
));
}
// Safe to unwrap four elements since we checked above.
let first: &LldpTlv = self.tlvs.first().unwrap();
let second: &LldpTlv = self.tlvs.get(1).unwrap();
let third: &LldpTlv = self.tlvs.get(2).unwrap();
let last: &LldpTlv = self.tlvs.last().unwrap();
if !matches!(first, LldpTlv::ChassisID(_)) {
return Err(std::io::Error::new(
ErrorKind::InvalidData,
format!("Expected first TLV to be ChassisID but got {}", first),
));
}
if !matches!(second, LldpTlv::PortID(_)) {
return Err(std::io::Error::new(
ErrorKind::InvalidData,
format!("Expected second TLV to be PortID but got {}", second),
));
}
if !matches!(third, LldpTlv::TTL(_)) {
return Err(std::io::Error::new(
ErrorKind::InvalidData,
format!("Expected third TLV to be TTL but got {}", third),
));
}
if !matches!(last, LldpTlv::EndOfLLDPPDU()) {
return Err(std::io::Error::new(
ErrorKind::InvalidData,
format!("Expected last TLV to be EndOfLLDPPDU but got {}", last),
));
}
Ok(())
}
}
impl Serialise for LldpPacket {
fn to_wire(&self) -> Result<Vec<u8>, std::io::Error> {
self.validate_format()?;
let mut pdu = Vec::new();
for tlv in &self.tlvs {
pdu.append(&mut tlv.to_wire()?);
}
Ok(pdu)
}
}
impl Deserialise for LldpPacket {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
let mut tlvs = Vec::new();
while buf.remaining() > 0 {
tlvs.push(LldpTlv::from_wire(buf)?);
if matches!(tlvs[tlvs.len() - 1], LldpTlv::EndOfLLDPPDU()) {
return Ok(LldpPacket { tlvs });
}
}
Err(pktparser::ParseError::InvalidArgument(
"Malformed LLDP packet: missing End of LLDP PDU TLV".to_string(),
))
}
}
impl std::fmt::Display for LldpPacket {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "LLDP [")?;
for tlv in &self.tlvs {
writeln!(f, "\t{}", tlv)?;
}
write!(f, "]")
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct LldpTlvHeader {
pub ty: u8, // 7 bits on the wire.
pub length: u16, // 9 bits on the wire
}
impl Serialise for LldpTlvHeader {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
let out: u16 = ((self.ty as u16) << 9) | (0b0000_0001_1111_1111 & self.length);
let mut vec = vec![];
WriteBytesExt::write_u16::<BigEndian>(&mut vec, out)?;
Ok(vec)
}
}
impl Deserialise for LldpTlvHeader {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
let input = buf
.get_be16()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
let r#type = ((input & 0b1111_1110_0000_0000) >> 9) as u8;
let length = input & 0b0000_0001_1111_1111;
Ok(LldpTlvHeader { ty: r#type, length })
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum LldpTlv {
/// 8.5.2
ChassisID(ChassisId),
/// 8.5.3
PortID(PortId),
/// 8.5.4
TTL(Ttl),
/// 8.5.5
PortDescription(PortDescription),
/// 8.5.6
SystemName(SystemName),
/// 8.5.7
SystemDescription(SystemDescription),
/// 8.5.8
SystemCapabilities(SystemCapabilities),
/// 8.5.9
ManagementAddress(ManagementAddress),
/// 8.6
OrganizationSpecific(OrganizationSpecific),
// A type to hold stuff we don't know, properly.
UnknownTLV(UnknownTlv),
EndOfLLDPPDU(),
}
impl Serialise for LldpTlv {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
let mut tlv_hdr = LldpTlvHeader { ty: 0, length: 0 };
let mut payload: Vec<u8>;
match self {
Self::ChassisID(tlv) => {
tlv_hdr.ty = 1;
payload = tlv.to_wire()?;
}
Self::PortID(tlv) => {
tlv_hdr.ty = 2;
payload = tlv.to_wire()?;
}
Self::TTL(tlv) => {
tlv_hdr.ty = 3;
payload = tlv.to_wire()?;
}
Self::PortDescription(tlv) => {
tlv_hdr.ty = 4;
payload = tlv.to_wire()?;
}
Self::SystemName(tlv) => {
tlv_hdr.ty = 5;
payload = tlv.to_wire()?;
}
Self::SystemDescription(tlv) => {
tlv_hdr.ty = 6;
payload = tlv.to_wire()?;
}
Self::SystemCapabilities(tlv) => {
tlv_hdr.ty = 7;
payload = tlv.to_wire()?;
}
Self::ManagementAddress(tlv) => {
tlv_hdr.ty = 8;
payload = tlv.to_wire()?;
}
Self::OrganizationSpecific(tlv) => {
tlv_hdr.ty = 127;
payload = tlv.to_wire()?;
}
Self::UnknownTLV(tlv) => {
tlv_hdr.ty = tlv.ty;
payload = tlv.payload.clone();
}
Self::EndOfLLDPPDU() => {
tlv_hdr.ty = 0;
payload = vec![];
}
};
tlv_hdr.length = payload.len() as u16;
let mut result = tlv_hdr.to_wire()?;
result.append(&mut payload);
Ok(result)
}
}
impl Deserialise for LldpTlv {
fn from_wire(buf: &mut pktparser::Buffer<'_>) -> Result<Self, pktparser::ParseError>
where
Self: Sized,
{
let ty = ((buf
.get_u8()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?)
& 0b1111_1110)
>> 1;
let len = buf
.get_u8()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
let mut payload = buf
.get_buffer(len.into())
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
match ty {
0 => Ok(Self::EndOfLLDPPDU()),
1 => Ok(LldpTlv::ChassisID(ChassisId::from_wire(&mut payload)?)),
2 => Ok(LldpTlv::PortID(PortId::from_wire(&mut payload)?)),
3 => Ok(LldpTlv::TTL(Ttl::from_wire(&mut payload)?)),
4 => Ok(LldpTlv::PortDescription(PortDescription::from_wire(
&mut payload,
)?)),
5 => Ok(LldpTlv::SystemName(SystemName::from_wire(&mut payload)?)),
6 => Ok(LldpTlv::SystemDescription(SystemDescription::from_wire(
&mut payload,
)?)),
7 => Ok(LldpTlv::SystemCapabilities(SystemCapabilities::from_wire(
&mut payload,
)?)),
8 => Ok(LldpTlv::ManagementAddress(ManagementAddress::from_wire(
&mut payload,
)?)),
127 => Ok(LldpTlv::OrganizationSpecific(
OrganizationSpecific::from_wire(&mut payload)?,
)),
_ => {
let payload_vec = payload
.get_vec(payload.remaining())
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
Ok(LldpTlv::UnknownTLV(UnknownTlv {
ty,
payload: payload_vec,
}))
}
}
}
}
impl std::fmt::Display for LldpTlv {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::ChassisID(tlv) => tlv.fmt(f),
Self::PortID(tlv) => tlv.fmt(f),
Self::TTL(tlv) => tlv.fmt(f),
Self::PortDescription(tlv) => tlv.fmt(f),
Self::SystemName(tlv) => tlv.fmt(f),
Self::SystemDescription(tlv) => tlv.fmt(f),
Self::SystemCapabilities(tlv) => tlv.fmt(f),
Self::ManagementAddress(tlv) => tlv.fmt(f),
Self::OrganizationSpecific(tlv) => tlv.fmt(f),
Self::UnknownTLV(tlv) => tlv.fmt(f),
Self::EndOfLLDPPDU() => write!(f, "End of LLDPPDU"),
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct UnknownTlv {
ty: u8,
payload: Vec<u8>,
}
impl Serialise for UnknownTlv {
fn to_wire(&self) -> Result<Vec<u8>, std::io::Error> {
let header = LldpTlvHeader {
ty: self.ty,
length: self.payload.len() as u16,
};
let mut res = header.to_wire()?;
res.append(&mut self.payload.clone());
Ok(res)
}
}
impl std::fmt::Display for UnknownTlv {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Unknown TLV type: {}, payload: {:02x?}",
self.ty, self.payload
)
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct ChassisId {
pub r#type: ChassisIdType,
pub identifier: Vec<u8>,
}
impl Serialise for ChassisId {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
let mut result: Vec<u8> = Vec::new();
result.append(&mut self.r#type.to_wire()?);
result.append(&mut self.identifier.clone());
Ok(result)
}
}
impl Deserialise for ChassisId {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
let subtype = ChassisIdType::from_wire(buf)?;
let identifier: Vec<u8> = buf
.get_vec(buf.remaining())
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
Ok(ChassisId {
r#type: subtype,
identifier,
})
}
}
impl std::fmt::Display for ChassisId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ChassisID type: {} value: {:02x?}",
self.r#type, self.identifier
)
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum ChassisIdType {
ChassisComponent,
InterfaceAlias,
PortComponent,
MacAddress,
NetworkAddress,
InterfaceName,
Local,
}
impl Deserialise for ChassisIdType {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<ChassisIdType, pktparser::ParseError> {
Ok(
match buf
.get_u8()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?
{
1 => Self::ChassisComponent,
2 => Self::InterfaceAlias,
3 => Self::PortComponent,
4 => Self::MacAddress,
5 => Self::NetworkAddress,
6 => Self::InterfaceName,
7 => Self::Local,
other => {
return Err(pktparser::ParseError::InvalidArgument(format!(
"Unknown ChassisIDType {}",
other
)));
}
},
)
}
}
impl Serialise for ChassisIdType {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
Ok(match self {
Self::ChassisComponent => vec![1],
Self::InterfaceAlias => vec![2],
Self::PortComponent => vec![3],
Self::MacAddress => vec![4],
Self::NetworkAddress => vec![5],
Self::InterfaceName => vec![6],
Self::Local => vec![7],
})
}
}
impl std::fmt::Display for ChassisIdType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self {
Self::ChassisComponent => write!(f, "ChassisComponent"),
Self::InterfaceAlias => write!(f, "InterfaceAlias"),
Self::PortComponent => write!(f, "PortComponent"),
Self::MacAddress => write!(f, "MacAddress"),
Self::NetworkAddress => write!(f, "NetworkAddress"),
Self::InterfaceName => write!(f, "InterfaceName"),
Self::Local => write!(f, "Local"),
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct PortId {
pub r#type: PortIdType,
pub identifier: Vec<u8>,
}
impl Deserialise for PortId {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
let subtype = PortIdType::from_wire(buf)?;
let identifier: Vec<u8> = buf
.get_vec(buf.remaining())
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
Ok(PortId {
r#type: subtype,
identifier,
})
}
}
impl Serialise for PortId {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
let mut result = Vec::new();
result.append(&mut self.r#type.to_wire()?);
result.append(&mut self.identifier.clone());
Ok(result)
}
}
impl std::fmt::Display for PortId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
// TODO: Actually treat the types correctly according to their value.
write!(
f,
"PortID type: {} value: {:02x?}",
self.r#type, self.identifier
)
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum PortIdType {
InterfaceAlias,
PortComponent,
MacAddress,
NetworkAddress,
InterfaceName,
AgentCircuitID,
Local,
}
impl Deserialise for PortIdType {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
Ok(
match buf
.get_u8()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?
{
1 => Self::InterfaceAlias,
2 => Self::PortComponent,
3 => Self::MacAddress,
4 => Self::NetworkAddress,
5 => Self::InterfaceName,
6 => Self::AgentCircuitID,
7 => Self::Local,
other => {
return Err(pktparser::ParseError::InvalidArgument(format!(
"Unknown PortIDType: {}",
other
)));
}
},
)
}
}
impl Serialise for PortIdType {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
Ok(match self {
Self::InterfaceAlias => vec![1],
Self::PortComponent => vec![2],
Self::MacAddress => vec![3],
Self::NetworkAddress => vec![4],
Self::InterfaceName => vec![5],
Self::AgentCircuitID => vec![6],
Self::Local => vec![7],
})
}
}
impl std::fmt::Display for PortIdType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::InterfaceAlias => write!(f, "InterfaceAlias"),
Self::PortComponent => write!(f, "PortComponent"),
Self::MacAddress => write!(f, "MacAddress"),
Self::NetworkAddress => write!(f, "NetworkAddress"),
Self::InterfaceName => write!(f, "InterfaceName"),
Self::AgentCircuitID => write!(f, "AgentCircuitID"),
Self::Local => write!(f, "Local"),
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct Ttl(u16);
impl Deserialise for Ttl {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
if buf.remaining() != 2 {
return Err(pktparser::ParseError::InvalidArgument(format!(
"TTL TLV length must be 2 but got {}",
buf.remaining(),
)));
}
let ttl = buf
.get_be16()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
Ok(Ttl(ttl))
}
}
impl Serialise for Ttl {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
let mut result = Vec::new();
WriteBytesExt::write_u16::<BigEndian>(&mut result, self.0)?;
Ok(result)
}
}
impl std::fmt::Display for Ttl {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(f, "TTL: {} seconds", self.0)
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct PortDescription {
pub description: String,
}
impl Deserialise for PortDescription {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
let description: String = String::from_utf8(
buf.get_vec(buf.remaining())
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?,
)
.map_err(|e| pktparser::ParseError::InvalidArgument(e.to_string()))?;
Ok(PortDescription { description })
}
}
impl Serialise for PortDescription {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
Ok(self.description.as_bytes().to_vec())
}
}
impl std::fmt::Display for PortDescription {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(f, "PortDescription: {}", self.description)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct SystemName(String);
impl Deserialise for SystemName {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
let system_name: String = String::from_utf8(
buf.get_vec(buf.remaining())
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?,
)
.map_err(|e| pktparser::ParseError::InvalidArgument(e.to_string()))?;
Ok(SystemName(system_name))
}
}
impl Serialise for SystemName {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
Ok(self.0.as_bytes().to_vec())
}
}
impl std::fmt::Display for SystemName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(f, "SystemName: {}", self.0)
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct SystemDescription(String);
impl Deserialise for SystemDescription {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
let system_description: String = String::from_utf8(
buf.get_vec(buf.remaining())
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?,
)
.map_err(|e| pktparser::ParseError::InvalidArgument(e.to_string()))?;
Ok(SystemDescription(system_description))
}
}
impl Serialise for SystemDescription {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
Ok(self.0.as_bytes().to_vec())
}
}
impl std::fmt::Display for SystemDescription {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(f, "SystemDescription: {}", self.0)
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct SystemCapabilities {
pub sys_cap: u16,
pub enabled_cap: u16,
}
impl Deserialise for SystemCapabilities {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
if buf.remaining() != 4 {
return Err(pktparser::ParseError::InvalidArgument(format!(
"SystemCapabilities TLV length must be 4 but got {}",
buf.remaining(),
)));
}
let sys_cap = buf
.get_be16()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
let enabled_cap = buf
.get_be16()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
Ok(SystemCapabilities {
sys_cap,
enabled_cap,
})
}
}
impl Serialise for SystemCapabilities {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
let mut result = Vec::new();
WriteBytesExt::write_u16::<BigEndian>(&mut result, self.sys_cap)?;
WriteBytesExt::write_u16::<BigEndian>(&mut result, self.enabled_cap)?;
Ok(result)
}
}
impl std::fmt::Display for SystemCapabilities {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(
f,
"SystemCapabilities system: {}, enabled: {}",
self.sys_cap, self.enabled_cap
)
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct ManagementAddress {
pub address: Vec<u8>,
/// Called management address subtype by IEEE802.1AB
pub address_family: u8,
pub numbering_subtype: u8,
pub if_number: u32,
pub oid: Vec<u8>,
}
impl Deserialise for ManagementAddress {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
let mgmt_addr_len = (buf
.get_u8()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?)
- 1; /* -1 for sizeof<mgmt_addr_af> */
let mgmt_addr_af = buf
.get_u8()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
if !(1..=32).contains(&mgmt_addr_len) {
return Err(pktparser::ParseError::InvalidArgument(format!(
"{} outside of valid range for mgmt_addr_len",
mgmt_addr_len
)));
}
let mgmt_addr = buf
.get_bytes(mgmt_addr_len.into())
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
let numbering_subtype: u8 = buf
.get_u8()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
let if_number = buf
.get_be32()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
let oid_len = buf
.get_u8()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
let oid = buf
.get_bytes(oid_len.into())
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?
.to_vec();
Ok(ManagementAddress {
address: mgmt_addr.to_vec(),
address_family: mgmt_addr_af,
numbering_subtype,
if_number,
oid,
})
}
}
impl Serialise for ManagementAddress {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
let mut payload: Vec<u8> = Vec::new();
if self.address.len() > u8::MAX.into() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!(
"management address too long: got {} but size is u8",
self.address.len()
),
));
}
payload.push(self.address.len() as u8);
payload.push(self.address_family);
payload.append(&mut self.address.clone());
payload.push(self.numbering_subtype);
WriteBytesExt::write_u32::<BigEndian>(&mut payload, self.if_number)?;
if self.oid.len() > u8::MAX.into() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("oid too long: got {} but size is u8", self.oid.len()),
));
}
payload.push(self.oid.len() as u8);
payload.append(&mut self.oid.clone());
Ok(payload)
}
}
impl std::fmt::Display for ManagementAddress {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(
f,
"ManagementAddress address: {:02x?}, af: {}, numbering_subtype: {}, if_number: {}, oid: {:?}",
self.address, self.address_family, self.numbering_subtype, self.if_number, self.oid
)
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct OrganizationSpecific {
pub oui: [u8; 3],
pub subtype: u8,
pub value: Vec<u8>,
}
impl Deserialise for OrganizationSpecific {
fn from_wire(
buf: &mut pktparser::Buffer<'_>,
) -> std::result::Result<Self, pktparser::ParseError> {
let oui = buf
.get_bytes(3)
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?
.to_vec();
let subtype = buf
.get_u8()
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
let value = buf
.get_bytes(buf.remaining())
.ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
Ok(OrganizationSpecific {
oui: oui
.try_into()
.expect("Error in converting Vec of len 3 to slice of len 3"),
subtype,
value: value.to_vec(),
})
}
}
impl Serialise for OrganizationSpecific {
fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
let mut payload = Vec::new();
payload.append(&mut self.oui.to_vec());
payload.push(self.subtype);
payload.append(&mut self.value.clone());
Ok(payload)
}
}
impl std::fmt::Display for OrganizationSpecific {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(
f,
"OrganizationSpecific oui: {:02x?} subtype: {}, value: {:02x?}",
self.oui, self.subtype, self.value
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_chassis_tlv() {
let bytes = vec![0x02, 0x07, 0x04, 0x8c, 0x1f, 0x64, 0xac, 0xe0, 0x00];
let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
assert_eq!(
"ChassisID type: MacAddress value: [8c, 1f, 64, ac, e0, 00]",
parsed.to_string()
);
let reserialized = parsed.to_wire().unwrap();
assert_eq!(reserialized, bytes);
}
#[test]
fn parse_portid_tlv() {
let bytes = vec![
0x04, 0x0d, 0x01, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x74, 0x6f, 0x20, 0x53,
0x31,
];
let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
assert_eq!(
"PortID type: InterfaceAlias value: [55, 70, 6c, 69, 6e, 6b, 20, 74, 6f, 20, 53, 31]",
parsed.to_string()
);
let reserialized = parsed.to_wire().unwrap();
assert_eq!(reserialized, bytes);
}
#[test]
fn parse_ttl_tlv() {
let bytes = vec![0x06, 0x02, 0x00, 0x78];
let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
assert_eq!("TTL: 120 seconds", parsed.to_string());
let reserialized = parsed.to_wire().unwrap();
assert_eq!(reserialized, bytes);
}
#[test]
fn parse_system_name_tlv() {
let bytes = vec![
0x0a, 0x0c, 0x53, 0x32, 0x2e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x2e, 0x63, 0x6f, 0x6d,
];
let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
assert_eq!("SystemName: S2.cisco.com", parsed.to_string());
let reserialized = parsed.to_wire().unwrap();
assert_eq!(reserialized, bytes);
}
#[test]
fn parse_system_description_tlv() {
let bytes = vec![
0x0c, 0xbe, 0x43, 0x69, 0x73, 0x63, 0x6f, 0x20, 0x49, 0x4f, 0x53, 0x20, 0x53, 0x6f,
0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2c, 0x20, 0x43, 0x33, 0x35, 0x36, 0x30, 0x20,
0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x28, 0x43, 0x33, 0x35, 0x36,
0x30, 0x2d, 0x41, 0x44, 0x56, 0x49, 0x50, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45,
0x53, 0x4b, 0x39, 0x2d, 0x4d, 0x29, 0x2c, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x20, 0x31, 0x32, 0x2e, 0x32, 0x28, 0x34, 0x34, 0x29, 0x53, 0x45, 0x2c, 0x20,
0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x20, 0x53, 0x4f, 0x46, 0x54, 0x57, 0x41,
0x52, 0x45, 0x20, 0x28, 0x66, 0x63, 0x31, 0x29, 0x0a, 0x43, 0x6f, 0x70, 0x79, 0x72,
0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x38, 0x36, 0x2d,
0x32, 0x30, 0x30, 0x38, 0x20, 0x62, 0x79, 0x20, 0x43, 0x69, 0x73, 0x63, 0x6f, 0x20,
0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x0a,
0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x53, 0x61, 0x74, 0x20, 0x30,
0x35, 0x2d, 0x4a, 0x61, 0x6e, 0x2d, 0x30, 0x38, 0x20, 0x30, 0x30, 0x3a, 0x31, 0x35,
0x20, 0x62, 0x79, 0x20, 0x77, 0x65, 0x69, 0x6c, 0x69, 0x75,
];
let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
assert_eq!(
"SystemDescription: Cisco IOS Software, C3560 Software (C3560-ADVIPSERVICESK9-M), Version 12.2(44)SE, RELEASE SOFTWARE (fc1)\nCopyright (c) 1986-2008 by Cisco Systems, Inc.\nCompiled Sat 05-Jan-08 00:15 by weiliu",
parsed.to_string()
);
let reserialized = parsed.to_wire().unwrap();
assert_eq!(reserialized, bytes);
}
#[test]
fn parse_port_description_tlv() {
let bytes = vec![
0x08, 0x13, 0x47, 0x69, 0x67, 0x61, 0x62, 0x69, 0x74, 0x45, 0x74, 0x68, 0x65, 0x72,
0x6e, 0x65, 0x74, 0x30, 0x2f, 0x31, 0x33,
];
let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
assert_eq!("PortDescription: GigabitEthernet0/13", parsed.to_string());
let reserialized = parsed.to_wire().unwrap();
assert_eq!(reserialized, bytes);
}
#[test]
fn parse_capabilities_tlv() {
let bytes = vec![0x0e, 0x04, 0x00, 0x14, 0x00, 0x04];
let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
assert_eq!(
"SystemCapabilities system: 20, enabled: 4",
parsed.to_string()
);
let reserialized = parsed.to_wire().unwrap();
assert_eq!(reserialized, bytes);
}
#[test]
fn parse_organization_specific() {
let bytes = vec![
0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, 0x03, 0xc0, 0x36, 0x00, 0x10,
];
let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
assert_eq!(
"OrganizationSpecific oui: [00, 12, 0f] subtype: 1, value: [03, c0, 36, 00, 10]",
parsed.to_string()
);
let reserialized = parsed.to_wire().unwrap();
assert_eq!(reserialized, bytes);
}
#[test]
fn parse_unknown_tlv() {
let bytes = vec![0xaa, 0x01, 0x42];
let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
assert_eq!("Unknown TLV type: 85, payload: [42]", parsed.to_string());
let reserialized = parsed.to_wire().unwrap();
assert_eq!(reserialized, bytes);
}
#[test]
fn parse_lldp_packet() {
let bytes = vec![
0x02, 0x07, 0x04, 0x00, 0x19, 0x2f, 0xa7, 0xb2, 0x8d, 0x04, 0x0d, 0x01, 0x55, 0x70,
0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x74, 0x6f, 0x20, 0x53, 0x31, 0x06, 0x02, 0x00, 0x78,
0x0a, 0x0c, 0x53, 0x32, 0x2e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x2e, 0x63, 0x6f, 0x6d,
0x0c, 0xbe, 0x43, 0x69, 0x73, 0x63, 0x6f, 0x20, 0x49, 0x4f, 0x53, 0x20, 0x53, 0x6f,
0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2c, 0x20, 0x43, 0x33, 0x35, 0x36, 0x30, 0x20,
0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x28, 0x43, 0x33, 0x35, 0x36,
0x30, 0x2d, 0x41, 0x44, 0x56, 0x49, 0x50, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45,
0x53, 0x4b, 0x39, 0x2d, 0x4d, 0x29, 0x2c, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x20, 0x31, 0x32, 0x2e, 0x32, 0x28, 0x34, 0x34, 0x29, 0x53, 0x45, 0x2c, 0x20,
0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x20, 0x53, 0x4f, 0x46, 0x54, 0x57, 0x41,
0x52, 0x45, 0x20, 0x28, 0x66, 0x63, 0x31, 0x29, 0x0a, 0x43, 0x6f, 0x70, 0x79, 0x72,
0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x38, 0x36, 0x2d,
0x32, 0x30, 0x30, 0x38, 0x20, 0x62, 0x79, 0x20, 0x43, 0x69, 0x73, 0x63, 0x6f, 0x20,
0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x0a,
0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x53, 0x61, 0x74, 0x20, 0x30,
0x35, 0x2d, 0x4a, 0x61, 0x6e, 0x2d, 0x30, 0x38, 0x20, 0x30, 0x30, 0x3a, 0x31, 0x35,
0x20, 0x62, 0x79, 0x20, 0x77, 0x65, 0x69, 0x6c, 0x69, 0x75, 0x08, 0x13, 0x47, 0x69,
0x67, 0x61, 0x62, 0x69, 0x74, 0x45, 0x74, 0x68, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x30,
0x2f, 0x31, 0x33, 0x0e, 0x04, 0x00, 0x14, 0x00, 0x04, 0xfe, 0x06, 0x00, 0x80, 0xc2,
0x01, 0x00, 0x01, 0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, 0x03, 0xc0, 0x36, 0x00, 0x10,
0x00, 0x00,
];
let parsed = LldpPacket::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
assert_eq!(
"LLDP [\n\tChassisID type: MacAddress value: [00, 19, 2f, a7, b2, 8d]\n\tPortID type: InterfaceAlias value: [55, 70, 6c, 69, 6e, 6b, 20, 74, 6f, 20, 53, 31]\n\tTTL: 120 seconds\n\tSystemName: S2.cisco.com\n\tSystemDescription: Cisco IOS Software, C3560 Software (C3560-ADVIPSERVICESK9-M), Version 12.2(44)SE, RELEASE SOFTWARE (fc1)\nCopyright (c) 1986-2008 by Cisco Systems, Inc.\nCompiled Sat 05-Jan-08 00:15 by weiliu\n\tPortDescription: GigabitEthernet0/13\n\tSystemCapabilities system: 20, enabled: 4\n\tOrganizationSpecific oui: [00, 80, c2] subtype: 1, value: [00, 01]\n\tOrganizationSpecific oui: [00, 12, 0f] subtype: 1, value: [03, c0, 36, 00, 10]\n\tEnd of LLDPPDU\n]",
parsed.to_string()
);
let reserialized = parsed.to_wire().unwrap();
assert_eq!(reserialized, bytes);
}
#[test]
fn parse2() {
let bytes = [
1, 128, 194, 0, 0, 14, /* src mac */
32, 12, 200, 58, 251, 201, /* dst mac */
136, 204, /* ethertype */
2, 7, 4, 32, 12, 200, 58, 251, 199, /* type 2 */
4, 3, 7, 103, 50, /* type 4 */
6, 2, 0, 120, /* type 6 */
8, 0, /* type 8 */
10, 7, 115, 119, 105, 116, 99, 104, 49, /* type 10 */
12, 28, 78, 101, 116, 103, 101, 97, 114, 32, 71, 105, 103, 97, 98, 105, 116, 32, 83,
109, 97, 114, 116, 32, 83, 119, 105, 116, 99, 104, /* type 12 */
14, 4, 0, 4, 0, 4, /* type 14 */
16, 20, 5, 1, 192, 168, 0, 238, 2, 0, 0, 0, 13, 8, 98, 114, 111, 97, 100, 99, 111,
109, /* type 16 */
0, 0, /* eof */
];
let _parsed = LldpPacket::from_wire(&mut pktparser::Buffer::new(&bytes[14..])).unwrap();
}
}