Skip to main content

erbium/lldp/
lldppkt.rs

1/*
2 *  Licensed under the Apache License, Version 2.0 (the "License");
3 *  you may not use this file except in compliance with the License.
4 *  You may obtain a copy of the License at
5 *
6 *      http://www.apache.org/licenses/LICENSE-2.0
7 *
8 *  Unless required by applicable law or agreed to in writing, software
9 *  distributed under the License is distributed on an "AS IS" BASIS,
10 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 *  See the License for the specific language governing permissions and
12 *  limitations under the License.
13 *
14 *  SPDX-License-Identifier: Apache-2.0
15 *  Author: Rayhaan Jaufeerally <rayhaan@rayhaan.ch>
16 *
17 *  LLDP packet parser for the protocol defined in IEEE 802.1AB-2016.
18 */
19
20/// lldppkt is an implementation of the wire format of IEEE 802.1AB-2016
21use crate::pktparser;
22use byteorder::{BigEndian, WriteBytesExt};
23use pktparser::{Deserialise, Serialise};
24
25use std::convert::TryInto;
26use std::fmt::Formatter;
27use std::io::ErrorKind;
28
29/// LldpPacket represents a LLDP PDU that can be read from / written to the wire.
30/// LLDP PDUs are simply a concatenation of TLVs in an Ethernet frame with type 0x88cc.
31/// At the end of the PDU there is an empty TLV header.
32/// The first three TLVs must be: ChassisID, PortID and TTL.
33#[derive(Debug, Eq, PartialEq)]
34pub struct LldpPacket {
35    pub tlvs: Vec<LldpTlv>,
36}
37
38impl LldpPacket {
39    fn validate_format(&self) -> Result<(), std::io::Error> {
40        if self.tlvs.len() < 4 {
41            return Err(std::io::Error::new(
42                ErrorKind::InvalidData,
43                "At least 4 mandatory TLVs are required in an LLDP PDU.",
44            ));
45        }
46
47        // Safe to unwrap four elements since we checked above.
48        let first: &LldpTlv = self.tlvs.first().unwrap();
49        let second: &LldpTlv = self.tlvs.get(1).unwrap();
50        let third: &LldpTlv = self.tlvs.get(2).unwrap();
51        let last: &LldpTlv = self.tlvs.last().unwrap();
52
53        if !matches!(first, LldpTlv::ChassisID(_)) {
54            return Err(std::io::Error::new(
55                ErrorKind::InvalidData,
56                format!("Expected first TLV to be ChassisID but got {}", first),
57            ));
58        }
59        if !matches!(second, LldpTlv::PortID(_)) {
60            return Err(std::io::Error::new(
61                ErrorKind::InvalidData,
62                format!("Expected second TLV to be PortID but got {}", second),
63            ));
64        }
65        if !matches!(third, LldpTlv::TTL(_)) {
66            return Err(std::io::Error::new(
67                ErrorKind::InvalidData,
68                format!("Expected third TLV to be TTL but got {}", third),
69            ));
70        }
71        if !matches!(last, LldpTlv::EndOfLLDPPDU()) {
72            return Err(std::io::Error::new(
73                ErrorKind::InvalidData,
74                format!("Expected last TLV to be EndOfLLDPPDU but got {}", last),
75            ));
76        }
77
78        Ok(())
79    }
80}
81
82impl Serialise for LldpPacket {
83    fn to_wire(&self) -> Result<Vec<u8>, std::io::Error> {
84        self.validate_format()?;
85        let mut pdu = Vec::new();
86
87        for tlv in &self.tlvs {
88            pdu.append(&mut tlv.to_wire()?);
89        }
90
91        Ok(pdu)
92    }
93}
94
95impl Deserialise for LldpPacket {
96    fn from_wire(
97        buf: &mut pktparser::Buffer<'_>,
98    ) -> std::result::Result<Self, pktparser::ParseError> {
99        let mut tlvs = Vec::new();
100        while buf.remaining() > 0 {
101            tlvs.push(LldpTlv::from_wire(buf)?);
102            if matches!(tlvs[tlvs.len() - 1], LldpTlv::EndOfLLDPPDU()) {
103                return Ok(LldpPacket { tlvs });
104            }
105        }
106        Err(pktparser::ParseError::InvalidArgument(
107            "Malformed LLDP packet: missing End of LLDP PDU TLV".to_string(),
108        ))
109    }
110}
111
112impl std::fmt::Display for LldpPacket {
113    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
114        writeln!(f, "LLDP [")?;
115        for tlv in &self.tlvs {
116            writeln!(f, "\t{}", tlv)?;
117        }
118        write!(f, "]")
119    }
120}
121
122#[derive(Debug, Eq, PartialEq)]
123pub struct LldpTlvHeader {
124    pub ty: u8,      // 7 bits on the wire.
125    pub length: u16, // 9 bits on the wire
126}
127
128impl Serialise for LldpTlvHeader {
129    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
130        let out: u16 = ((self.ty as u16) << 9) | (0b0000_0001_1111_1111 & self.length);
131        let mut vec = vec![];
132        WriteBytesExt::write_u16::<BigEndian>(&mut vec, out)?;
133        Ok(vec)
134    }
135}
136
137impl Deserialise for LldpTlvHeader {
138    fn from_wire(
139        buf: &mut pktparser::Buffer<'_>,
140    ) -> std::result::Result<Self, pktparser::ParseError> {
141        let input = buf
142            .get_be16()
143            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
144        let r#type = ((input & 0b1111_1110_0000_0000) >> 9) as u8;
145        let length = input & 0b0000_0001_1111_1111;
146
147        Ok(LldpTlvHeader { ty: r#type, length })
148    }
149}
150
151#[derive(Debug, Eq, PartialEq)]
152pub enum LldpTlv {
153    /// 8.5.2
154    ChassisID(ChassisId),
155    /// 8.5.3
156    PortID(PortId),
157    /// 8.5.4
158    TTL(Ttl),
159    /// 8.5.5
160    PortDescription(PortDescription),
161    /// 8.5.6
162    SystemName(SystemName),
163    /// 8.5.7
164    SystemDescription(SystemDescription),
165    /// 8.5.8
166    SystemCapabilities(SystemCapabilities),
167    /// 8.5.9
168    ManagementAddress(ManagementAddress),
169    /// 8.6
170    OrganizationSpecific(OrganizationSpecific),
171
172    // A type to hold stuff we don't know, properly.
173    UnknownTLV(UnknownTlv),
174
175    EndOfLLDPPDU(),
176}
177
178impl Serialise for LldpTlv {
179    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
180        let mut tlv_hdr = LldpTlvHeader { ty: 0, length: 0 };
181        let mut payload: Vec<u8>;
182        match self {
183            Self::ChassisID(tlv) => {
184                tlv_hdr.ty = 1;
185                payload = tlv.to_wire()?;
186            }
187            Self::PortID(tlv) => {
188                tlv_hdr.ty = 2;
189                payload = tlv.to_wire()?;
190            }
191            Self::TTL(tlv) => {
192                tlv_hdr.ty = 3;
193                payload = tlv.to_wire()?;
194            }
195            Self::PortDescription(tlv) => {
196                tlv_hdr.ty = 4;
197                payload = tlv.to_wire()?;
198            }
199            Self::SystemName(tlv) => {
200                tlv_hdr.ty = 5;
201                payload = tlv.to_wire()?;
202            }
203            Self::SystemDescription(tlv) => {
204                tlv_hdr.ty = 6;
205                payload = tlv.to_wire()?;
206            }
207            Self::SystemCapabilities(tlv) => {
208                tlv_hdr.ty = 7;
209                payload = tlv.to_wire()?;
210            }
211            Self::ManagementAddress(tlv) => {
212                tlv_hdr.ty = 8;
213                payload = tlv.to_wire()?;
214            }
215            Self::OrganizationSpecific(tlv) => {
216                tlv_hdr.ty = 127;
217                payload = tlv.to_wire()?;
218            }
219            Self::UnknownTLV(tlv) => {
220                tlv_hdr.ty = tlv.ty;
221                payload = tlv.payload.clone();
222            }
223            Self::EndOfLLDPPDU() => {
224                tlv_hdr.ty = 0;
225                payload = vec![];
226            }
227        };
228        tlv_hdr.length = payload.len() as u16;
229        let mut result = tlv_hdr.to_wire()?;
230        result.append(&mut payload);
231        Ok(result)
232    }
233}
234
235impl Deserialise for LldpTlv {
236    fn from_wire(buf: &mut pktparser::Buffer<'_>) -> Result<Self, pktparser::ParseError>
237    where
238        Self: Sized,
239    {
240        let ty = ((buf
241            .get_u8()
242            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?)
243            & 0b1111_1110)
244            >> 1;
245        let len = buf
246            .get_u8()
247            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
248        let mut payload = buf
249            .get_buffer(len.into())
250            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
251
252        match ty {
253            0 => Ok(Self::EndOfLLDPPDU()),
254            1 => Ok(LldpTlv::ChassisID(ChassisId::from_wire(&mut payload)?)),
255            2 => Ok(LldpTlv::PortID(PortId::from_wire(&mut payload)?)),
256            3 => Ok(LldpTlv::TTL(Ttl::from_wire(&mut payload)?)),
257            4 => Ok(LldpTlv::PortDescription(PortDescription::from_wire(
258                &mut payload,
259            )?)),
260            5 => Ok(LldpTlv::SystemName(SystemName::from_wire(&mut payload)?)),
261            6 => Ok(LldpTlv::SystemDescription(SystemDescription::from_wire(
262                &mut payload,
263            )?)),
264            7 => Ok(LldpTlv::SystemCapabilities(SystemCapabilities::from_wire(
265                &mut payload,
266            )?)),
267            8 => Ok(LldpTlv::ManagementAddress(ManagementAddress::from_wire(
268                &mut payload,
269            )?)),
270            127 => Ok(LldpTlv::OrganizationSpecific(
271                OrganizationSpecific::from_wire(&mut payload)?,
272            )),
273            _ => {
274                let payload_vec = payload
275                    .get_vec(payload.remaining())
276                    .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
277                Ok(LldpTlv::UnknownTLV(UnknownTlv {
278                    ty,
279                    payload: payload_vec,
280                }))
281            }
282        }
283    }
284}
285
286impl std::fmt::Display for LldpTlv {
287    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
288        match self {
289            Self::ChassisID(tlv) => tlv.fmt(f),
290            Self::PortID(tlv) => tlv.fmt(f),
291            Self::TTL(tlv) => tlv.fmt(f),
292            Self::PortDescription(tlv) => tlv.fmt(f),
293            Self::SystemName(tlv) => tlv.fmt(f),
294            Self::SystemDescription(tlv) => tlv.fmt(f),
295            Self::SystemCapabilities(tlv) => tlv.fmt(f),
296            Self::ManagementAddress(tlv) => tlv.fmt(f),
297            Self::OrganizationSpecific(tlv) => tlv.fmt(f),
298            Self::UnknownTLV(tlv) => tlv.fmt(f),
299            Self::EndOfLLDPPDU() => write!(f, "End of LLDPPDU"),
300        }
301    }
302}
303
304#[derive(Debug, Eq, PartialEq)]
305pub struct UnknownTlv {
306    ty: u8,
307    payload: Vec<u8>,
308}
309
310impl Serialise for UnknownTlv {
311    fn to_wire(&self) -> Result<Vec<u8>, std::io::Error> {
312        let header = LldpTlvHeader {
313            ty: self.ty,
314            length: self.payload.len() as u16,
315        };
316
317        let mut res = header.to_wire()?;
318        res.append(&mut self.payload.clone());
319        Ok(res)
320    }
321}
322
323impl std::fmt::Display for UnknownTlv {
324    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
325        write!(
326            f,
327            "Unknown TLV type: {}, payload: {:02x?}",
328            self.ty, self.payload
329        )
330    }
331}
332
333#[derive(Debug, Eq, PartialEq)]
334pub struct ChassisId {
335    pub r#type: ChassisIdType,
336    pub identifier: Vec<u8>,
337}
338
339impl Serialise for ChassisId {
340    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
341        let mut result: Vec<u8> = Vec::new();
342        result.append(&mut self.r#type.to_wire()?);
343        result.append(&mut self.identifier.clone());
344        Ok(result)
345    }
346}
347
348impl Deserialise for ChassisId {
349    fn from_wire(
350        buf: &mut pktparser::Buffer<'_>,
351    ) -> std::result::Result<Self, pktparser::ParseError> {
352        let subtype = ChassisIdType::from_wire(buf)?;
353
354        let identifier: Vec<u8> = buf
355            .get_vec(buf.remaining())
356            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
357
358        Ok(ChassisId {
359            r#type: subtype,
360            identifier,
361        })
362    }
363}
364
365impl std::fmt::Display for ChassisId {
366    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
367        write!(
368            f,
369            "ChassisID type: {} value: {:02x?}",
370            self.r#type, self.identifier
371        )
372    }
373}
374
375#[derive(Debug, Eq, PartialEq)]
376pub enum ChassisIdType {
377    ChassisComponent,
378    InterfaceAlias,
379    PortComponent,
380    MacAddress,
381    NetworkAddress,
382    InterfaceName,
383    Local,
384}
385
386impl Deserialise for ChassisIdType {
387    fn from_wire(
388        buf: &mut pktparser::Buffer<'_>,
389    ) -> std::result::Result<ChassisIdType, pktparser::ParseError> {
390        Ok(
391            match buf
392                .get_u8()
393                .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?
394            {
395                1 => Self::ChassisComponent,
396                2 => Self::InterfaceAlias,
397                3 => Self::PortComponent,
398                4 => Self::MacAddress,
399                5 => Self::NetworkAddress,
400                6 => Self::InterfaceName,
401                7 => Self::Local,
402                other => {
403                    return Err(pktparser::ParseError::InvalidArgument(format!(
404                        "Unknown ChassisIDType {}",
405                        other
406                    )));
407                }
408            },
409        )
410    }
411}
412
413impl Serialise for ChassisIdType {
414    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
415        Ok(match self {
416            Self::ChassisComponent => vec![1],
417            Self::InterfaceAlias => vec![2],
418            Self::PortComponent => vec![3],
419            Self::MacAddress => vec![4],
420            Self::NetworkAddress => vec![5],
421            Self::InterfaceName => vec![6],
422            Self::Local => vec![7],
423        })
424    }
425}
426
427impl std::fmt::Display for ChassisIdType {
428    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
429        match self {
430            Self::ChassisComponent => write!(f, "ChassisComponent"),
431            Self::InterfaceAlias => write!(f, "InterfaceAlias"),
432            Self::PortComponent => write!(f, "PortComponent"),
433            Self::MacAddress => write!(f, "MacAddress"),
434            Self::NetworkAddress => write!(f, "NetworkAddress"),
435            Self::InterfaceName => write!(f, "InterfaceName"),
436            Self::Local => write!(f, "Local"),
437        }
438    }
439}
440
441#[derive(Debug, Eq, PartialEq)]
442pub struct PortId {
443    pub r#type: PortIdType,
444    pub identifier: Vec<u8>,
445}
446
447impl Deserialise for PortId {
448    fn from_wire(
449        buf: &mut pktparser::Buffer<'_>,
450    ) -> std::result::Result<Self, pktparser::ParseError> {
451        let subtype = PortIdType::from_wire(buf)?;
452
453        let identifier: Vec<u8> = buf
454            .get_vec(buf.remaining())
455            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
456
457        Ok(PortId {
458            r#type: subtype,
459            identifier,
460        })
461    }
462}
463
464impl Serialise for PortId {
465    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
466        let mut result = Vec::new();
467        result.append(&mut self.r#type.to_wire()?);
468        result.append(&mut self.identifier.clone());
469        Ok(result)
470    }
471}
472
473impl std::fmt::Display for PortId {
474    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
475        // TODO: Actually treat the types correctly according to their value.
476        write!(
477            f,
478            "PortID type: {} value: {:02x?}",
479            self.r#type, self.identifier
480        )
481    }
482}
483
484#[derive(Debug, PartialEq, Eq)]
485pub enum PortIdType {
486    InterfaceAlias,
487    PortComponent,
488    MacAddress,
489    NetworkAddress,
490    InterfaceName,
491    AgentCircuitID,
492    Local,
493}
494
495impl Deserialise for PortIdType {
496    fn from_wire(
497        buf: &mut pktparser::Buffer<'_>,
498    ) -> std::result::Result<Self, pktparser::ParseError> {
499        Ok(
500            match buf
501                .get_u8()
502                .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?
503            {
504                1 => Self::InterfaceAlias,
505                2 => Self::PortComponent,
506                3 => Self::MacAddress,
507                4 => Self::NetworkAddress,
508                5 => Self::InterfaceName,
509                6 => Self::AgentCircuitID,
510                7 => Self::Local,
511                other => {
512                    return Err(pktparser::ParseError::InvalidArgument(format!(
513                        "Unknown PortIDType: {}",
514                        other
515                    )));
516                }
517            },
518        )
519    }
520}
521
522impl Serialise for PortIdType {
523    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
524        Ok(match self {
525            Self::InterfaceAlias => vec![1],
526            Self::PortComponent => vec![2],
527            Self::MacAddress => vec![3],
528            Self::NetworkAddress => vec![4],
529            Self::InterfaceName => vec![5],
530            Self::AgentCircuitID => vec![6],
531            Self::Local => vec![7],
532        })
533    }
534}
535
536impl std::fmt::Display for PortIdType {
537    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
538        match self {
539            Self::InterfaceAlias => write!(f, "InterfaceAlias"),
540            Self::PortComponent => write!(f, "PortComponent"),
541            Self::MacAddress => write!(f, "MacAddress"),
542            Self::NetworkAddress => write!(f, "NetworkAddress"),
543            Self::InterfaceName => write!(f, "InterfaceName"),
544            Self::AgentCircuitID => write!(f, "AgentCircuitID"),
545            Self::Local => write!(f, "Local"),
546        }
547    }
548}
549
550#[derive(Debug, Eq, PartialEq)]
551pub struct Ttl(u16);
552
553impl Deserialise for Ttl {
554    fn from_wire(
555        buf: &mut pktparser::Buffer<'_>,
556    ) -> std::result::Result<Self, pktparser::ParseError> {
557        if buf.remaining() != 2 {
558            return Err(pktparser::ParseError::InvalidArgument(format!(
559                "TTL TLV length must be 2 but got {}",
560                buf.remaining(),
561            )));
562        }
563
564        let ttl = buf
565            .get_be16()
566            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
567        Ok(Ttl(ttl))
568    }
569}
570
571impl Serialise for Ttl {
572    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
573        let mut result = Vec::new();
574        WriteBytesExt::write_u16::<BigEndian>(&mut result, self.0)?;
575        Ok(result)
576    }
577}
578
579impl std::fmt::Display for Ttl {
580    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
581        write!(f, "TTL: {} seconds", self.0)
582    }
583}
584
585#[derive(Debug, Eq, PartialEq)]
586pub struct PortDescription {
587    pub description: String,
588}
589
590impl Deserialise for PortDescription {
591    fn from_wire(
592        buf: &mut pktparser::Buffer<'_>,
593    ) -> std::result::Result<Self, pktparser::ParseError> {
594        let description: String = String::from_utf8(
595            buf.get_vec(buf.remaining())
596                .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?,
597        )
598        .map_err(|e| pktparser::ParseError::InvalidArgument(e.to_string()))?;
599
600        Ok(PortDescription { description })
601    }
602}
603
604impl Serialise for PortDescription {
605    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
606        Ok(self.description.as_bytes().to_vec())
607    }
608}
609
610impl std::fmt::Display for PortDescription {
611    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
612        write!(f, "PortDescription: {}", self.description)
613    }
614}
615
616#[derive(Debug, PartialEq, Eq)]
617pub struct SystemName(String);
618
619impl Deserialise for SystemName {
620    fn from_wire(
621        buf: &mut pktparser::Buffer<'_>,
622    ) -> std::result::Result<Self, pktparser::ParseError> {
623        let system_name: String = String::from_utf8(
624            buf.get_vec(buf.remaining())
625                .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?,
626        )
627        .map_err(|e| pktparser::ParseError::InvalidArgument(e.to_string()))?;
628
629        Ok(SystemName(system_name))
630    }
631}
632
633impl Serialise for SystemName {
634    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
635        Ok(self.0.as_bytes().to_vec())
636    }
637}
638
639impl std::fmt::Display for SystemName {
640    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
641        write!(f, "SystemName: {}", self.0)
642    }
643}
644
645#[derive(Debug, Eq, PartialEq)]
646pub struct SystemDescription(String);
647
648impl Deserialise for SystemDescription {
649    fn from_wire(
650        buf: &mut pktparser::Buffer<'_>,
651    ) -> std::result::Result<Self, pktparser::ParseError> {
652        let system_description: String = String::from_utf8(
653            buf.get_vec(buf.remaining())
654                .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?,
655        )
656        .map_err(|e| pktparser::ParseError::InvalidArgument(e.to_string()))?;
657
658        Ok(SystemDescription(system_description))
659    }
660}
661
662impl Serialise for SystemDescription {
663    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
664        Ok(self.0.as_bytes().to_vec())
665    }
666}
667
668impl std::fmt::Display for SystemDescription {
669    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
670        write!(f, "SystemDescription: {}", self.0)
671    }
672}
673
674#[derive(Debug, Eq, PartialEq)]
675pub struct SystemCapabilities {
676    pub sys_cap: u16,
677    pub enabled_cap: u16,
678}
679
680impl Deserialise for SystemCapabilities {
681    fn from_wire(
682        buf: &mut pktparser::Buffer<'_>,
683    ) -> std::result::Result<Self, pktparser::ParseError> {
684        if buf.remaining() != 4 {
685            return Err(pktparser::ParseError::InvalidArgument(format!(
686                "SystemCapabilities TLV length must be 4 but got {}",
687                buf.remaining(),
688            )));
689        }
690
691        let sys_cap = buf
692            .get_be16()
693            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
694        let enabled_cap = buf
695            .get_be16()
696            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
697
698        Ok(SystemCapabilities {
699            sys_cap,
700            enabled_cap,
701        })
702    }
703}
704
705impl Serialise for SystemCapabilities {
706    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
707        let mut result = Vec::new();
708        WriteBytesExt::write_u16::<BigEndian>(&mut result, self.sys_cap)?;
709        WriteBytesExt::write_u16::<BigEndian>(&mut result, self.enabled_cap)?;
710        Ok(result)
711    }
712}
713
714impl std::fmt::Display for SystemCapabilities {
715    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
716        write!(
717            f,
718            "SystemCapabilities system: {}, enabled: {}",
719            self.sys_cap, self.enabled_cap
720        )
721    }
722}
723
724#[derive(Debug, Eq, PartialEq)]
725pub struct ManagementAddress {
726    pub address: Vec<u8>,
727    /// Called management address subtype by IEEE802.1AB
728    pub address_family: u8,
729    pub numbering_subtype: u8,
730    pub if_number: u32,
731    pub oid: Vec<u8>,
732}
733
734impl Deserialise for ManagementAddress {
735    fn from_wire(
736        buf: &mut pktparser::Buffer<'_>,
737    ) -> std::result::Result<Self, pktparser::ParseError> {
738        let mgmt_addr_len = (buf
739            .get_u8()
740            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?)
741            - 1; /* -1 for sizeof<mgmt_addr_af> */
742        let mgmt_addr_af = buf
743            .get_u8()
744            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
745        if !(1..=32).contains(&mgmt_addr_len) {
746            return Err(pktparser::ParseError::InvalidArgument(format!(
747                "{} outside of valid range for mgmt_addr_len",
748                mgmt_addr_len
749            )));
750        }
751        let mgmt_addr = buf
752            .get_bytes(mgmt_addr_len.into())
753            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
754
755        let numbering_subtype: u8 = buf
756            .get_u8()
757            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
758        let if_number = buf
759            .get_be32()
760            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
761
762        let oid_len = buf
763            .get_u8()
764            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
765        let oid = buf
766            .get_bytes(oid_len.into())
767            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?
768            .to_vec();
769
770        Ok(ManagementAddress {
771            address: mgmt_addr.to_vec(),
772            address_family: mgmt_addr_af,
773            numbering_subtype,
774            if_number,
775            oid,
776        })
777    }
778}
779
780impl Serialise for ManagementAddress {
781    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
782        let mut payload: Vec<u8> = Vec::new();
783        if self.address.len() > u8::MAX.into() {
784            return Err(std::io::Error::new(
785                std::io::ErrorKind::InvalidData,
786                format!(
787                    "management address too long: got {} but size is u8",
788                    self.address.len()
789                ),
790            ));
791        }
792        payload.push(self.address.len() as u8);
793        payload.push(self.address_family);
794        payload.append(&mut self.address.clone());
795        payload.push(self.numbering_subtype);
796        WriteBytesExt::write_u32::<BigEndian>(&mut payload, self.if_number)?;
797        if self.oid.len() > u8::MAX.into() {
798            return Err(std::io::Error::new(
799                std::io::ErrorKind::InvalidData,
800                format!("oid too long: got {} but size is u8", self.oid.len()),
801            ));
802        }
803        payload.push(self.oid.len() as u8);
804        payload.append(&mut self.oid.clone());
805
806        Ok(payload)
807    }
808}
809
810impl std::fmt::Display for ManagementAddress {
811    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
812        write!(
813            f,
814            "ManagementAddress address: {:02x?}, af: {}, numbering_subtype: {}, if_number: {}, oid: {:?}",
815            self.address, self.address_family, self.numbering_subtype, self.if_number, self.oid
816        )
817    }
818}
819
820#[derive(Debug, Eq, PartialEq)]
821pub struct OrganizationSpecific {
822    pub oui: [u8; 3],
823    pub subtype: u8,
824    pub value: Vec<u8>,
825}
826
827impl Deserialise for OrganizationSpecific {
828    fn from_wire(
829        buf: &mut pktparser::Buffer<'_>,
830    ) -> std::result::Result<Self, pktparser::ParseError> {
831        let oui = buf
832            .get_bytes(3)
833            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?
834            .to_vec();
835        let subtype = buf
836            .get_u8()
837            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
838        let value = buf
839            .get_bytes(buf.remaining())
840            .ok_or(pktparser::ParseError::UnexpectedEndOfInput)?;
841
842        Ok(OrganizationSpecific {
843            oui: oui
844                .try_into()
845                .expect("Error in converting Vec of len 3 to slice of len 3"),
846            subtype,
847            value: value.to_vec(),
848        })
849    }
850}
851
852impl Serialise for OrganizationSpecific {
853    fn to_wire(&self) -> std::result::Result<Vec<u8>, std::io::Error> {
854        let mut payload = Vec::new();
855        payload.append(&mut self.oui.to_vec());
856        payload.push(self.subtype);
857        payload.append(&mut self.value.clone());
858        Ok(payload)
859    }
860}
861
862impl std::fmt::Display for OrganizationSpecific {
863    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
864        write!(
865            f,
866            "OrganizationSpecific oui: {:02x?} subtype: {}, value: {:02x?}",
867            self.oui, self.subtype, self.value
868        )
869    }
870}
871
872#[cfg(test)]
873mod tests {
874    use super::*;
875
876    #[test]
877    fn parse_chassis_tlv() {
878        let bytes = vec![0x02, 0x07, 0x04, 0x8c, 0x1f, 0x64, 0xac, 0xe0, 0x00];
879        let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
880        assert_eq!(
881            "ChassisID type: MacAddress value: [8c, 1f, 64, ac, e0, 00]",
882            parsed.to_string()
883        );
884        let reserialized = parsed.to_wire().unwrap();
885        assert_eq!(reserialized, bytes);
886    }
887
888    #[test]
889    fn parse_portid_tlv() {
890        let bytes = vec![
891            0x04, 0x0d, 0x01, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x74, 0x6f, 0x20, 0x53,
892            0x31,
893        ];
894        let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
895        assert_eq!(
896            "PortID type: InterfaceAlias value: [55, 70, 6c, 69, 6e, 6b, 20, 74, 6f, 20, 53, 31]",
897            parsed.to_string()
898        );
899        let reserialized = parsed.to_wire().unwrap();
900        assert_eq!(reserialized, bytes);
901    }
902
903    #[test]
904    fn parse_ttl_tlv() {
905        let bytes = vec![0x06, 0x02, 0x00, 0x78];
906        let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
907        assert_eq!("TTL: 120 seconds", parsed.to_string());
908        let reserialized = parsed.to_wire().unwrap();
909        assert_eq!(reserialized, bytes);
910    }
911
912    #[test]
913    fn parse_system_name_tlv() {
914        let bytes = vec![
915            0x0a, 0x0c, 0x53, 0x32, 0x2e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x2e, 0x63, 0x6f, 0x6d,
916        ];
917        let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
918        assert_eq!("SystemName: S2.cisco.com", parsed.to_string());
919        let reserialized = parsed.to_wire().unwrap();
920        assert_eq!(reserialized, bytes);
921    }
922
923    #[test]
924    fn parse_system_description_tlv() {
925        let bytes = vec![
926            0x0c, 0xbe, 0x43, 0x69, 0x73, 0x63, 0x6f, 0x20, 0x49, 0x4f, 0x53, 0x20, 0x53, 0x6f,
927            0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2c, 0x20, 0x43, 0x33, 0x35, 0x36, 0x30, 0x20,
928            0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x28, 0x43, 0x33, 0x35, 0x36,
929            0x30, 0x2d, 0x41, 0x44, 0x56, 0x49, 0x50, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45,
930            0x53, 0x4b, 0x39, 0x2d, 0x4d, 0x29, 0x2c, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
931            0x6e, 0x20, 0x31, 0x32, 0x2e, 0x32, 0x28, 0x34, 0x34, 0x29, 0x53, 0x45, 0x2c, 0x20,
932            0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x20, 0x53, 0x4f, 0x46, 0x54, 0x57, 0x41,
933            0x52, 0x45, 0x20, 0x28, 0x66, 0x63, 0x31, 0x29, 0x0a, 0x43, 0x6f, 0x70, 0x79, 0x72,
934            0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x38, 0x36, 0x2d,
935            0x32, 0x30, 0x30, 0x38, 0x20, 0x62, 0x79, 0x20, 0x43, 0x69, 0x73, 0x63, 0x6f, 0x20,
936            0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x0a,
937            0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x53, 0x61, 0x74, 0x20, 0x30,
938            0x35, 0x2d, 0x4a, 0x61, 0x6e, 0x2d, 0x30, 0x38, 0x20, 0x30, 0x30, 0x3a, 0x31, 0x35,
939            0x20, 0x62, 0x79, 0x20, 0x77, 0x65, 0x69, 0x6c, 0x69, 0x75,
940        ];
941        let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
942        assert_eq!(
943            "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",
944            parsed.to_string()
945        );
946        let reserialized = parsed.to_wire().unwrap();
947        assert_eq!(reserialized, bytes);
948    }
949
950    #[test]
951    fn parse_port_description_tlv() {
952        let bytes = vec![
953            0x08, 0x13, 0x47, 0x69, 0x67, 0x61, 0x62, 0x69, 0x74, 0x45, 0x74, 0x68, 0x65, 0x72,
954            0x6e, 0x65, 0x74, 0x30, 0x2f, 0x31, 0x33,
955        ];
956        let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
957        assert_eq!("PortDescription: GigabitEthernet0/13", parsed.to_string());
958        let reserialized = parsed.to_wire().unwrap();
959        assert_eq!(reserialized, bytes);
960    }
961
962    #[test]
963    fn parse_capabilities_tlv() {
964        let bytes = vec![0x0e, 0x04, 0x00, 0x14, 0x00, 0x04];
965        let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
966        assert_eq!(
967            "SystemCapabilities system: 20, enabled: 4",
968            parsed.to_string()
969        );
970        let reserialized = parsed.to_wire().unwrap();
971        assert_eq!(reserialized, bytes);
972    }
973
974    #[test]
975    fn parse_organization_specific() {
976        let bytes = vec![
977            0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, 0x03, 0xc0, 0x36, 0x00, 0x10,
978        ];
979        let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
980        assert_eq!(
981            "OrganizationSpecific oui: [00, 12, 0f] subtype: 1, value: [03, c0, 36, 00, 10]",
982            parsed.to_string()
983        );
984        let reserialized = parsed.to_wire().unwrap();
985        assert_eq!(reserialized, bytes);
986    }
987
988    #[test]
989    fn parse_unknown_tlv() {
990        let bytes = vec![0xaa, 0x01, 0x42];
991        let parsed = LldpTlv::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
992        assert_eq!("Unknown TLV type: 85, payload: [42]", parsed.to_string());
993        let reserialized = parsed.to_wire().unwrap();
994        assert_eq!(reserialized, bytes);
995    }
996
997    #[test]
998    fn parse_lldp_packet() {
999        let bytes = vec![
1000            0x02, 0x07, 0x04, 0x00, 0x19, 0x2f, 0xa7, 0xb2, 0x8d, 0x04, 0x0d, 0x01, 0x55, 0x70,
1001            0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x74, 0x6f, 0x20, 0x53, 0x31, 0x06, 0x02, 0x00, 0x78,
1002            0x0a, 0x0c, 0x53, 0x32, 0x2e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x2e, 0x63, 0x6f, 0x6d,
1003            0x0c, 0xbe, 0x43, 0x69, 0x73, 0x63, 0x6f, 0x20, 0x49, 0x4f, 0x53, 0x20, 0x53, 0x6f,
1004            0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2c, 0x20, 0x43, 0x33, 0x35, 0x36, 0x30, 0x20,
1005            0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x28, 0x43, 0x33, 0x35, 0x36,
1006            0x30, 0x2d, 0x41, 0x44, 0x56, 0x49, 0x50, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45,
1007            0x53, 0x4b, 0x39, 0x2d, 0x4d, 0x29, 0x2c, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
1008            0x6e, 0x20, 0x31, 0x32, 0x2e, 0x32, 0x28, 0x34, 0x34, 0x29, 0x53, 0x45, 0x2c, 0x20,
1009            0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x20, 0x53, 0x4f, 0x46, 0x54, 0x57, 0x41,
1010            0x52, 0x45, 0x20, 0x28, 0x66, 0x63, 0x31, 0x29, 0x0a, 0x43, 0x6f, 0x70, 0x79, 0x72,
1011            0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x38, 0x36, 0x2d,
1012            0x32, 0x30, 0x30, 0x38, 0x20, 0x62, 0x79, 0x20, 0x43, 0x69, 0x73, 0x63, 0x6f, 0x20,
1013            0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x0a,
1014            0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x53, 0x61, 0x74, 0x20, 0x30,
1015            0x35, 0x2d, 0x4a, 0x61, 0x6e, 0x2d, 0x30, 0x38, 0x20, 0x30, 0x30, 0x3a, 0x31, 0x35,
1016            0x20, 0x62, 0x79, 0x20, 0x77, 0x65, 0x69, 0x6c, 0x69, 0x75, 0x08, 0x13, 0x47, 0x69,
1017            0x67, 0x61, 0x62, 0x69, 0x74, 0x45, 0x74, 0x68, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x30,
1018            0x2f, 0x31, 0x33, 0x0e, 0x04, 0x00, 0x14, 0x00, 0x04, 0xfe, 0x06, 0x00, 0x80, 0xc2,
1019            0x01, 0x00, 0x01, 0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, 0x03, 0xc0, 0x36, 0x00, 0x10,
1020            0x00, 0x00,
1021        ];
1022        let parsed = LldpPacket::from_wire(&mut pktparser::Buffer::new(&bytes)).unwrap();
1023        assert_eq!(
1024            "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]",
1025            parsed.to_string()
1026        );
1027        let reserialized = parsed.to_wire().unwrap();
1028        assert_eq!(reserialized, bytes);
1029    }
1030
1031    #[test]
1032    fn parse2() {
1033        let bytes = [
1034            1, 128, 194, 0, 0, 14, /* src mac */
1035            32, 12, 200, 58, 251, 201, /* dst mac */
1036            136, 204, /* ethertype */
1037            2, 7, 4, 32, 12, 200, 58, 251, 199, /* type 2 */
1038            4, 3, 7, 103, 50, /* type 4 */
1039            6, 2, 0, 120, /* type 6 */
1040            8, 0, /* type 8 */
1041            10, 7, 115, 119, 105, 116, 99, 104, 49, /* type 10 */
1042            12, 28, 78, 101, 116, 103, 101, 97, 114, 32, 71, 105, 103, 97, 98, 105, 116, 32, 83,
1043            109, 97, 114, 116, 32, 83, 119, 105, 116, 99, 104, /* type 12 */
1044            14, 4, 0, 4, 0, 4, /* type 14 */
1045            16, 20, 5, 1, 192, 168, 0, 238, 2, 0, 0, 0, 13, 8, 98, 114, 111, 97, 100, 99, 111,
1046            109, /* type 16 */
1047            0, 0, /* eof */
1048        ];
1049        let _parsed = LldpPacket::from_wire(&mut pktparser::Buffer::new(&bytes[14..])).unwrap();
1050    }
1051}