1use serde::{de::IgnoredAny, Deserialize, Deserializer, Serialize};
4
5const LLDP_SYS_CAP_OTHER: u16 = 1;
6const LLDP_SYS_CAP_REPEATER: u16 = 2;
7const LLDP_SYS_CAP_MAC_BRIDGE: u16 = 3;
8const LLDP_SYS_CAP_AP: u16 = 4;
9const LLDP_SYS_CAP_ROUTER: u16 = 5;
10const LLDP_SYS_CAP_TELEPHONE: u16 = 6;
11const LLDP_SYS_CAP_DOCSIS: u16 = 7;
12const LLDP_SYS_CAP_STATION_ONLY: u16 = 8;
13const LLDP_SYS_CAP_CVLAN: u16 = 9;
14const LLDP_SYS_CAP_SVLAN: u16 = 10;
15const LLDP_SYS_CAP_TWO_PORT_MAC_RELAY: u16 = 11;
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
18#[repr(u8)]
19#[serde(into = "u8")]
20pub enum LldpNeighborTlvType {
21 ChassisId = 1,
22 Port = 2,
23 SystemName = 5,
24 SystemDescription = 6,
25 SystemCapabilities = 7,
26 ManagementAddress = 8,
27 OrganizationSpecific = 127,
28}
29
30impl From<LldpNeighborTlvType> for u8 {
31 fn from(value: LldpNeighborTlvType) -> Self {
32 value as Self
33 }
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
37#[repr(u8)]
38#[serde(into = "u8")]
39pub enum LldpOrgSubtype {
40 Vlan = 3,
41 MacPhyConf = 1,
42 Ppvids = 2,
43 MaxFrameSize = 4,
44}
45
46impl From<LldpOrgSubtype> for u8 {
47 fn from(value: LldpOrgSubtype) -> Self {
48 value as Self
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
53pub enum LldpOrgOiu {
54 #[serde(rename = "00:80:c2")]
55 Vlan,
56 #[serde(rename = "00:12:0f")]
57 MacPhyConf,
58 #[serde(rename = "00:80:c2")]
59 Ppvids,
60 #[serde(rename = "00:12:0f")]
61 MaxFrameSize,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Default, Deserialize, Serialize)]
65#[non_exhaustive]
66#[serde(deny_unknown_fields)]
67pub struct LldpConfig {
68 #[serde(deserialize_with = "crate::deserializer::bool_or_string")]
69 pub enabled: bool,
70 #[serde(
71 default,
72 deserialize_with = "skip",
73 skip_serializing_if = "Vec::is_empty"
74 )]
75 pub neighbors: Vec<Vec<LldpNeighborTlv>>,
76}
77
78fn skip<'de, D, T>(deserializer: D) -> Result<T, D::Error>
81where
82 D: Deserializer<'de>,
83 T: Default,
84{
85 IgnoredAny::deserialize(deserializer)?;
87 Ok(T::default())
88}
89
90impl LldpConfig {
91 pub(crate) fn sanitize(&mut self) {
92 self.neighbors = Vec::new();
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
98#[serde(untagged)]
99#[non_exhaustive]
100pub enum LldpNeighborTlv {
101 SystemName(LldpSystemName),
102 SystemDescription(LldpSystemDescription),
103 SystemCapabilities(LldpSystemCapabilities),
104 ChassisId(LldpChassisId),
105 PortId(LldpPortId),
106 Ieee8021Vlans(LldpVlans),
107 Ieee8023MacPhyConf(LldpMacPhy),
108 Ieee8021Ppvids(LldpPpvids),
109 ManagementAddresses(LldpMgmtAddrs),
110 Ieee8023MaxFrameSize(LldpMaxFrameSize),
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
114#[non_exhaustive]
115#[serde(rename_all = "kebab-case", deny_unknown_fields)]
116pub struct LldpSystemName {
117 #[serde(rename = "type")]
118 pub ty: LldpNeighborTlvType,
119 pub system_name: String,
120}
121
122impl LldpSystemName {
123 pub fn new(value: String) -> Self {
124 Self {
125 system_name: value,
126 ty: LldpNeighborTlvType::SystemName,
127 }
128 }
129}
130
131#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
132#[non_exhaustive]
133#[serde(rename_all = "kebab-case", deny_unknown_fields)]
134pub struct LldpSystemDescription {
135 #[serde(rename = "type")]
136 pub ty: LldpNeighborTlvType,
137 pub system_description: String,
138}
139
140impl LldpSystemDescription {
141 pub fn new(value: String) -> Self {
142 Self {
143 system_description: value,
144 ty: LldpNeighborTlvType::SystemDescription,
145 }
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
150#[non_exhaustive]
151#[serde(rename_all = "kebab-case", deny_unknown_fields)]
152pub struct LldpChassisId {
153 #[serde(rename = "type")]
154 pub ty: LldpNeighborTlvType,
155 pub chassis_id: String,
156 pub chassis_id_type: LldpChassisIdType,
157 #[serde(rename = "_description")]
158 pub description: String,
159}
160
161impl LldpChassisId {
162 pub fn new(chassis_id: String, chassis_id_type: LldpChassisIdType) -> Self {
163 Self {
164 chassis_id,
165 chassis_id_type: chassis_id_type.clone(),
166 description: chassis_id_type.into(),
167 ty: LldpNeighborTlvType::ChassisId,
168 }
169 }
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
173#[repr(u8)]
174#[serde(into = "u8")]
175pub enum LldpChassisIdType {
176 Reserved = 0,
177 ChassisComponent = 1,
178 InterfaceAlias = 2,
179 PortComponent = 3,
180 MacAddress = 4,
181 NetworkAddress = 5,
182 InterfaceName = 6,
183 LocallyAssigned = 7,
184}
185
186impl From<LldpChassisIdType> for u8 {
187 fn from(value: LldpChassisIdType) -> Self {
188 value as Self
189 }
190}
191
192impl From<u8> for LldpChassisIdType {
193 fn from(v: u8) -> LldpChassisIdType {
194 if v == LldpChassisIdType::ChassisComponent as u8 {
195 LldpChassisIdType::ChassisComponent
196 } else if v == LldpChassisIdType::InterfaceAlias as u8 {
197 LldpChassisIdType::InterfaceAlias
198 } else if v == LldpChassisIdType::PortComponent as u8 {
199 LldpChassisIdType::PortComponent
200 } else if v == LldpChassisIdType::MacAddress as u8 {
201 LldpChassisIdType::MacAddress
202 } else if v == LldpChassisIdType::NetworkAddress as u8 {
203 LldpChassisIdType::NetworkAddress
204 } else if v == LldpChassisIdType::InterfaceName as u8 {
205 LldpChassisIdType::InterfaceName
206 } else if v == LldpChassisIdType::LocallyAssigned as u8 {
207 LldpChassisIdType::LocallyAssigned
208 } else {
209 LldpChassisIdType::Reserved
210 }
211 }
212}
213
214impl From<LldpChassisIdType> for String {
215 fn from(v: LldpChassisIdType) -> String {
216 match v {
217 LldpChassisIdType::Reserved => "Reserved",
218 LldpChassisIdType::ChassisComponent => "Chasis compontent",
219 LldpChassisIdType::InterfaceAlias => "Interface alias",
220 LldpChassisIdType::PortComponent => "Port component",
221 LldpChassisIdType::MacAddress => "MAC address",
222 LldpChassisIdType::NetworkAddress => "Network address",
223 LldpChassisIdType::InterfaceName => "Interface name",
224 LldpChassisIdType::LocallyAssigned => "Locally assigned",
225 }
226 .to_string()
227 }
228}
229
230impl Default for LldpChassisIdType {
231 fn default() -> Self {
232 Self::Reserved
233 }
234}
235
236#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
237#[non_exhaustive]
238#[serde(rename_all = "kebab-case", deny_unknown_fields)]
239pub struct LldpSystemCapabilities {
240 #[serde(rename = "type")]
241 pub ty: LldpNeighborTlvType,
242 pub system_capabilities: Vec<LldpSystemCapability>,
243}
244
245impl LldpSystemCapabilities {
246 pub fn new(system_capabilities: Vec<LldpSystemCapability>) -> Self {
247 Self {
248 system_capabilities,
249 ty: LldpNeighborTlvType::SystemCapabilities,
250 }
251 }
252}
253
254impl From<u16> for LldpSystemCapabilities {
255 fn from(caps: u16) -> Self {
256 let mut ret = Vec::new();
257 if (caps & (1 << (LLDP_SYS_CAP_OTHER - 1))) > 0 {
258 ret.push(LldpSystemCapability::Other);
259 }
260 if (caps & (1 << (LLDP_SYS_CAP_REPEATER - 1))) > 0 {
261 ret.push(LldpSystemCapability::Repeater);
262 }
263 if (caps & (1 << (LLDP_SYS_CAP_MAC_BRIDGE - 1))) > 0 {
264 ret.push(LldpSystemCapability::MacBridgeComponent);
265 }
266 if (caps & (1 << (LLDP_SYS_CAP_AP - 1))) > 0 {
267 ret.push(LldpSystemCapability::AccessPoint);
268 }
269 if (caps & (1 << (LLDP_SYS_CAP_ROUTER - 1))) > 0 {
270 ret.push(LldpSystemCapability::Router);
271 }
272 if (caps & (1 << (LLDP_SYS_CAP_TELEPHONE - 1))) > 0 {
273 ret.push(LldpSystemCapability::Telephone);
274 }
275 if (caps & (1 << (LLDP_SYS_CAP_DOCSIS - 1))) > 0 {
276 ret.push(LldpSystemCapability::DocsisCableDevice);
277 }
278 if (caps & (1 << (LLDP_SYS_CAP_STATION_ONLY - 1))) > 0 {
279 ret.push(LldpSystemCapability::StationOnly);
280 }
281 if (caps & (1 << (LLDP_SYS_CAP_CVLAN - 1))) > 0 {
282 ret.push(LldpSystemCapability::CVlanComponent);
283 }
284 if (caps & (1 << (LLDP_SYS_CAP_SVLAN - 1))) > 0 {
285 ret.push(LldpSystemCapability::SVlanComponent);
286 }
287 if (caps & (1 << (LLDP_SYS_CAP_TWO_PORT_MAC_RELAY - 1))) > 0 {
288 ret.push(LldpSystemCapability::TwoPortMacRelayComponent);
289 }
290 Self::new(ret)
291 }
292}
293
294#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
295#[non_exhaustive]
296pub enum LldpSystemCapability {
297 Other,
298 Repeater,
299 #[serde(rename = "MAC Bridge component")]
300 MacBridgeComponent,
301 #[serde(rename = "802.11 Access Point (AP)")]
302 AccessPoint,
303 Router,
304 Telephone,
305 #[serde(rename = "DOCSIS cable device")]
306 DocsisCableDevice,
307 #[serde(rename = "Station Only")]
308 StationOnly,
309 #[serde(rename = "C-VLAN component")]
310 CVlanComponent,
311 #[serde(rename = "S-VLAN component")]
312 SVlanComponent,
313 #[serde(rename = "Two-port MAC Relay component")]
314 TwoPortMacRelayComponent,
315}
316
317impl Default for LldpSystemCapability {
318 fn default() -> Self {
319 Self::Other
320 }
321}
322
323#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
324#[non_exhaustive]
325#[serde(rename_all = "kebab-case", deny_unknown_fields)]
326pub struct LldpPortId {
327 #[serde(rename = "type")]
328 pub ty: LldpNeighborTlvType,
329 pub port_id: String,
330 pub port_id_type: LldpPortIdType,
331 #[serde(rename = "_description")]
332 pub description: String,
333}
334
335#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
336#[repr(u8)]
337#[serde(into = "u8")]
338pub enum LldpPortIdType {
339 Reserved = 0,
340 InterfaceAlias = 1,
341 PortComponent = 2,
342 MacAddress = 3,
343 NetworkAddress = 4,
344 InterfaceName = 5,
345 AgentCircuitId = 6,
346 LocallyAssigned = 7,
347}
348
349impl From<LldpPortIdType> for u8 {
350 fn from(value: LldpPortIdType) -> Self {
351 value as Self
352 }
353}
354
355impl From<LldpPortIdType> for String {
356 fn from(v: LldpPortIdType) -> String {
357 match v {
358 LldpPortIdType::Reserved => "Reserved",
359 LldpPortIdType::InterfaceAlias => "Interface alias",
360 LldpPortIdType::PortComponent => "Port component",
361 LldpPortIdType::MacAddress => "MAC address",
362 LldpPortIdType::NetworkAddress => "Network address",
363 LldpPortIdType::InterfaceName => "Interface name",
364 LldpPortIdType::AgentCircuitId => "Agent circuit ID",
365 LldpPortIdType::LocallyAssigned => "Locally assigned",
366 }
367 .to_string()
368 }
369}
370
371impl From<u8> for LldpPortIdType {
372 fn from(v: u8) -> LldpPortIdType {
373 if v == LldpPortIdType::InterfaceName as u8 {
374 LldpPortIdType::InterfaceName
375 } else if v == LldpPortIdType::PortComponent as u8 {
376 LldpPortIdType::PortComponent
377 } else if v == LldpPortIdType::MacAddress as u8 {
378 LldpPortIdType::MacAddress
379 } else if v == LldpPortIdType::NetworkAddress as u8 {
380 LldpPortIdType::NetworkAddress
381 } else if v == LldpPortIdType::AgentCircuitId as u8 {
382 LldpPortIdType::AgentCircuitId
383 } else if v == LldpPortIdType::LocallyAssigned as u8 {
384 LldpPortIdType::LocallyAssigned
385 } else {
386 LldpPortIdType::Reserved
387 }
388 }
389}
390
391impl LldpPortId {
392 pub fn new(port_id: String, port_id_type: LldpPortIdType) -> Self {
393 Self {
394 port_id,
395 port_id_type: port_id_type.clone(),
396 description: port_id_type.into(),
397 ty: LldpNeighborTlvType::Port,
398 }
399 }
400}
401
402impl Default for LldpPortIdType {
403 fn default() -> Self {
404 Self::Reserved
405 }
406}
407
408#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
409#[non_exhaustive]
410#[serde(rename_all = "kebab-case", deny_unknown_fields)]
411pub struct LldpVlans {
412 #[serde(rename = "type")]
413 pub ty: LldpNeighborTlvType,
414 pub ieee_802_1_vlans: Vec<LldpVlan>,
415 pub oui: LldpOrgOiu,
416 pub subtype: LldpOrgSubtype,
417}
418
419impl LldpVlans {
420 pub fn new(ieee_802_1_vlans: Vec<LldpVlan>) -> Self {
421 Self {
422 ieee_802_1_vlans,
423 oui: LldpOrgOiu::Vlan,
424 subtype: LldpOrgSubtype::Vlan,
425 ty: LldpNeighborTlvType::OrganizationSpecific,
426 }
427 }
428}
429
430#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
431#[non_exhaustive]
432pub struct LldpVlan {
433 pub name: String,
434 pub vid: u32,
435}
436
437#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
438#[non_exhaustive]
439#[serde(rename_all = "kebab-case")]
440pub struct LldpMacPhy {
441 #[serde(rename = "type")]
442 pub ty: LldpNeighborTlvType,
443 pub ieee_802_3_mac_phy_conf: LldpMacPhyConf,
444 pub oui: LldpOrgOiu,
445 pub subtype: LldpOrgSubtype,
446}
447
448#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
449#[non_exhaustive]
450#[serde(rename_all = "kebab-case")]
451pub struct LldpMacPhyConf {
452 pub autoneg: bool,
453 pub operational_mau_type: u16,
454 pub pmd_autoneg_cap: u16,
455}
456
457impl LldpMacPhy {
458 pub fn new(
459 autoneg: bool,
460 operational_mau_type: u16,
461 pmd_autoneg_cap: u16,
462 ) -> Self {
463 Self {
464 ieee_802_3_mac_phy_conf: LldpMacPhyConf {
465 autoneg,
466 operational_mau_type,
467 pmd_autoneg_cap,
468 },
469 oui: LldpOrgOiu::MacPhyConf,
470 subtype: LldpOrgSubtype::MacPhyConf,
471 ty: LldpNeighborTlvType::OrganizationSpecific,
472 }
473 }
474}
475
476#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
477#[non_exhaustive]
478#[serde(rename_all = "kebab-case", deny_unknown_fields)]
479pub struct LldpPpvids {
480 #[serde(rename = "type")]
481 pub ty: LldpNeighborTlvType,
482 pub ieee_802_1_ppvids: Vec<u32>,
483 pub oui: LldpOrgOiu,
484 pub subtype: LldpOrgSubtype,
485}
486
487impl LldpPpvids {
488 pub fn new(ieee_802_1_ppvids: Vec<u32>) -> Self {
489 Self {
490 ieee_802_1_ppvids,
491 oui: LldpOrgOiu::Ppvids,
492 subtype: LldpOrgSubtype::Ppvids,
493 ty: LldpNeighborTlvType::OrganizationSpecific,
494 }
495 }
496}
497
498#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
499#[non_exhaustive]
500#[serde(rename_all = "kebab-case")]
501pub struct LldpMgmtAddrs {
502 #[serde(rename = "type")]
503 pub ty: LldpNeighborTlvType,
504 pub management_addresses: Vec<LldpMgmtAddr>,
505}
506
507impl LldpMgmtAddrs {
508 pub fn new(management_addresses: Vec<LldpMgmtAddr>) -> Self {
509 Self {
510 management_addresses,
511 ty: LldpNeighborTlvType::ManagementAddress,
512 }
513 }
514}
515
516#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
517#[non_exhaustive]
518#[serde(rename_all = "kebab-case")]
519pub struct LldpMgmtAddr {
520 pub address: String,
521 pub address_subtype: LldpAddressFamily,
522 pub interface_number: u32,
523 pub interface_number_subtype: u32,
524}
525
526#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
527#[non_exhaustive]
528pub enum LldpAddressFamily {
529 Unknown,
530 #[serde(rename = "IPv4")]
531 Ipv4,
532 #[serde(rename = "IPv6")]
533 Ipv6,
534 #[serde(rename = "MAC")]
535 Mac,
536}
537
538impl Default for LldpAddressFamily {
539 fn default() -> Self {
540 Self::Unknown
541 }
542}
543
544const ADDRESS_FAMILY_IP4: u16 = 1;
545const ADDRESS_FAMILY_IP6: u16 = 2;
546const ADDRESS_FAMILY_MAC: u16 = 6;
547
548impl From<u16> for LldpAddressFamily {
549 fn from(i: u16) -> Self {
550 match i {
551 ADDRESS_FAMILY_IP4 => Self::Ipv4,
552 ADDRESS_FAMILY_IP6 => Self::Ipv6,
553 ADDRESS_FAMILY_MAC => Self::Mac,
554 _ => Self::Unknown,
555 }
556 }
557}
558
559#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
560#[non_exhaustive]
561#[serde(rename_all = "kebab-case", deny_unknown_fields)]
562pub struct LldpMaxFrameSize {
563 #[serde(rename = "type")]
564 pub ty: LldpNeighborTlvType,
565 pub ieee_802_3_max_frame_size: u32,
566 pub oui: LldpOrgOiu,
567 pub subtype: LldpOrgSubtype,
568}
569
570impl LldpMaxFrameSize {
571 pub fn new(ieee_802_3_max_frame_size: u32) -> Self {
572 Self {
573 ieee_802_3_max_frame_size,
574 oui: LldpOrgOiu::MaxFrameSize,
575 subtype: LldpOrgSubtype::MaxFrameSize,
576 ty: LldpNeighborTlvType::OrganizationSpecific,
577 }
578 }
579}