Skip to main content

bgpkit_parser/models/bgp/
linkstate.rs

1//! BGP Link-State data structures based on RFC 7752
2
3use crate::models::*;
4use num_enum::{FromPrimitive, IntoPrimitive};
5use std::collections::HashMap;
6use std::net::{Ipv4Addr, Ipv6Addr};
7
8/// BGP Link-State NLRI Types as defined in RFC 7752 and IANA registry
9#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[repr(u16)]
12pub enum NlriType {
13    #[num_enum(default)]
14    Reserved = 0,
15    Node = 1,
16    Link = 2,
17    Ipv4TopologyPrefix = 3,
18    Ipv6TopologyPrefix = 4,
19    SrPolicyCandidatePath = 5,
20    Srv6Sid = 6,
21    StubLink = 7,
22}
23
24/// Protocol Identifier as defined in RFC 7752
25#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27#[repr(u8)]
28pub enum ProtocolId {
29    #[num_enum(default)]
30    Reserved = 0,
31    IsisL1 = 1,
32    IsisL2 = 2,
33    Ospfv2 = 3,
34    Direct = 4,
35    Static = 5,
36    Ospfv3 = 6,
37    Bgp = 7,
38    RsvpTe = 8,
39    SegmentRouting = 9,
40}
41
42/// Node Descriptor Sub-TLV Types
43#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45#[repr(u16)]
46pub enum NodeDescriptorType {
47    #[num_enum(default)]
48    Reserved = 0,
49    AutonomousSystem = 512,
50    BgpLsIdentifier = 513,
51    OspfAreaId = 514,
52    IgpRouterId = 515,
53}
54
55/// Link Descriptor Sub-TLV Types
56#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
57#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
58#[repr(u16)]
59pub enum LinkDescriptorType {
60    #[num_enum(default)]
61    Reserved = 0,
62    LinkLocalRemoteIdentifiers = 258,
63    Ipv4InterfaceAddress = 259,
64    Ipv4NeighborAddress = 260,
65    Ipv6InterfaceAddress = 261,
66    Ipv6NeighborAddress = 262,
67    MultiTopologyId = 263,
68}
69
70/// Prefix Descriptor Sub-TLV Types
71#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73#[repr(u16)]
74pub enum PrefixDescriptorType {
75    #[num_enum(default)]
76    Reserved = 0,
77    MultiTopologyId = 263,
78    OspfRouteType = 264,
79    IpReachabilityInformation = 265,
80}
81
82/// Node Attribute TLV Types
83#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
84#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
85#[repr(u16)]
86pub enum NodeAttributeType {
87    #[num_enum(default)]
88    Reserved = 0,
89    NodeFlagBits = 1024,
90    OpaqueNodeAttribute = 1025,
91    NodeName = 1026,
92    IsisAreaIdentifier = 1027,
93    Ipv4RouterIdOfLocalNode = 1028,
94    Ipv6RouterIdOfLocalNode = 1029,
95    SrCapabilities = 1034,
96    SrAlgorithm = 1035,
97    SrLocalBlock = 1036,
98    SrmsPreference = 1037,
99}
100
101/// Link Attribute TLV Types
102#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
104#[repr(u16)]
105pub enum LinkAttributeType {
106    #[num_enum(default)]
107    Reserved = 0,
108    Ipv4RouterIdOfLocalNode = 1028,
109    Ipv6RouterIdOfLocalNode = 1029,
110    Ipv4RouterIdOfRemoteNode = 1030,
111    Ipv6RouterIdOfRemoteNode = 1031,
112    AdministrativeGroup = 1088,
113    MaximumLinkBandwidth = 1089,
114    MaxReservableLinkBandwidth = 1090,
115    UnreservedBandwidth = 1091,
116    TeDefaultMetric = 1092,
117    LinkProtectionType = 1093,
118    MplsProtocolMask = 1094,
119    IgpMetric = 1095,
120    SharedRiskLinkGroups = 1096,
121    OpaqueLinkAttribute = 1097,
122    LinkName = 1098,
123    SrAdjacencySid = 1099,
124    SrLanAdjacencySid = 1100,
125    PeerNodeSid = 1101,
126    PeerAdjacencySid = 1102,
127    PeerSetSid = 1103,
128    /// Unidirectional Link Delay - RFC 8571
129    UnidirectionalLinkDelay = 1114,
130    /// Min/Max Unidirectional Link Delay - RFC 8571
131    MinMaxUnidirectionalLinkDelay = 1115,
132    /// Unidirectional Delay Variation - RFC 8571
133    UnidirectionalDelayVariation = 1116,
134    /// Unidirectional Link Loss - RFC 8571
135    UnidirectionalLinkLoss = 1117,
136    /// Unidirectional Residual Bandwidth - RFC 8571
137    UnidirectionalResidualBandwidth = 1118,
138    /// Unidirectional Available Bandwidth - RFC 8571
139    UnidirectionalAvailableBandwidth = 1119,
140    /// Unidirectional Utilized Bandwidth - RFC 8571
141    UnidirectionalUtilizedBandwidth = 1120,
142    /// L2 Bundle Member Attributes - RFC 9085
143    L2BundleMemberAttributes = 1172,
144    /// Application-Specific Link Attributes - RFC 9294
145    ApplicationSpecificLinkAttributes = 1122,
146}
147
148/// Prefix Attribute TLV Types
149#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
150#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
151#[repr(u16)]
152pub enum PrefixAttributeType {
153    #[num_enum(default)]
154    Reserved = 0,
155    IgpFlags = 1152,
156    IgpRouteTag = 1153,
157    IgpExtendedRouteTag = 1154,
158    PrefixMetric = 1155,
159    OspfForwardingAddress = 1156,
160    OpaquePrefixAttribute = 1157,
161    PrefixSid = 1158,
162    RangeSid = 1159,
163    SidLabelIndex = 1161,
164    SidLabelBinding = 1162,
165    Srv6LocatorTlv = 1163,
166    /// Prefix Attribute Flags - RFC 9085
167    PrefixAttributeFlags = 1170,
168    /// Source Router Identifier - RFC 9085
169    SourceRouterIdentifier = 1171,
170    /// Source OSPF Router-ID - RFC 9085
171    SourceOspfRouterId = 1174,
172}
173
174/// TLV (Type-Length-Value) structure for Link-State information
175#[derive(Debug, PartialEq, Clone, Eq)]
176#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
177pub struct Tlv {
178    pub tlv_type: u16,
179    pub value: Vec<u8>,
180}
181
182impl Tlv {
183    pub fn new(tlv_type: u16, value: Vec<u8>) -> Self {
184        Self { tlv_type, value }
185    }
186}
187
188/// Node Descriptor TLVs
189#[derive(Debug, PartialEq, Clone, Eq)]
190#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
191#[derive(Default)]
192pub struct NodeDescriptor {
193    pub autonomous_system: Option<u32>,
194    pub bgp_ls_identifier: Option<u32>,
195    pub ospf_area_id: Option<u32>,
196    pub igp_router_id: Option<Vec<u8>>,
197    pub unknown_tlvs: Vec<Tlv>,
198}
199
200/// Link Descriptor TLVs
201#[derive(Debug, PartialEq, Clone, Eq)]
202#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
203#[derive(Default)]
204pub struct LinkDescriptor {
205    pub link_local_remote_identifiers: Option<(u32, u32)>,
206    pub ipv4_interface_address: Option<Ipv4Addr>,
207    pub ipv4_neighbor_address: Option<Ipv4Addr>,
208    pub ipv6_interface_address: Option<Ipv6Addr>,
209    pub ipv6_neighbor_address: Option<Ipv6Addr>,
210    pub multi_topology_id: Option<u16>,
211    pub unknown_tlvs: Vec<Tlv>,
212}
213
214/// Prefix Descriptor TLVs
215#[derive(Debug, PartialEq, Clone, Eq)]
216#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
217#[derive(Default)]
218pub struct PrefixDescriptor {
219    pub multi_topology_id: Option<u16>,
220    pub ospf_route_type: Option<u8>,
221    pub ip_reachability_information: Option<NetworkPrefix>,
222    pub unknown_tlvs: Vec<Tlv>,
223}
224
225/// BGP Link-State NLRI structure
226#[derive(Debug, PartialEq, Clone, Eq)]
227#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
228pub struct LinkStateNlri {
229    pub nlri_type: NlriType,
230    pub protocol_id: ProtocolId,
231    pub identifier: u64,
232    pub local_node_descriptors: NodeDescriptor,
233    pub remote_node_descriptors: Option<NodeDescriptor>,
234    pub link_descriptors: Option<LinkDescriptor>,
235    pub prefix_descriptors: Option<PrefixDescriptor>,
236}
237
238impl LinkStateNlri {
239    pub fn new_node_nlri(
240        protocol_id: ProtocolId,
241        identifier: u64,
242        local_node_descriptors: NodeDescriptor,
243    ) -> Self {
244        Self {
245            nlri_type: NlriType::Node,
246            protocol_id,
247            identifier,
248            local_node_descriptors,
249            remote_node_descriptors: None,
250            link_descriptors: None,
251            prefix_descriptors: None,
252        }
253    }
254
255    pub fn new_link_nlri(
256        protocol_id: ProtocolId,
257        identifier: u64,
258        local_node_descriptors: NodeDescriptor,
259        remote_node_descriptors: NodeDescriptor,
260        link_descriptors: LinkDescriptor,
261    ) -> Self {
262        Self {
263            nlri_type: NlriType::Link,
264            protocol_id,
265            identifier,
266            local_node_descriptors,
267            remote_node_descriptors: Some(remote_node_descriptors),
268            link_descriptors: Some(link_descriptors),
269            prefix_descriptors: None,
270        }
271    }
272
273    pub fn new_prefix_nlri(
274        nlri_type: NlriType, // Ipv4TopologyPrefix or Ipv6TopologyPrefix
275        protocol_id: ProtocolId,
276        identifier: u64,
277        local_node_descriptors: NodeDescriptor,
278        prefix_descriptors: PrefixDescriptor,
279    ) -> Self {
280        Self {
281            nlri_type,
282            protocol_id,
283            identifier,
284            local_node_descriptors,
285            remote_node_descriptors: None,
286            link_descriptors: None,
287            prefix_descriptors: Some(prefix_descriptors),
288        }
289    }
290}
291
292/// BGP Link-State Attributes
293#[derive(Debug, PartialEq, Clone, Eq)]
294#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
295#[derive(Default)]
296pub struct LinkStateAttribute {
297    pub node_attributes: HashMap<NodeAttributeType, Vec<u8>>,
298    pub link_attributes: HashMap<LinkAttributeType, Vec<u8>>,
299    pub prefix_attributes: HashMap<PrefixAttributeType, Vec<u8>>,
300    pub unknown_attributes: Vec<Tlv>,
301}
302
303impl LinkStateAttribute {
304    pub fn new() -> Self {
305        Self::default()
306    }
307
308    pub fn add_node_attribute(&mut self, attr_type: NodeAttributeType, value: Vec<u8>) {
309        self.node_attributes.insert(attr_type, value);
310    }
311
312    pub fn add_link_attribute(&mut self, attr_type: LinkAttributeType, value: Vec<u8>) {
313        self.link_attributes.insert(attr_type, value);
314    }
315
316    pub fn add_prefix_attribute(&mut self, attr_type: PrefixAttributeType, value: Vec<u8>) {
317        self.prefix_attributes.insert(attr_type, value);
318    }
319
320    pub fn add_unknown_attribute(&mut self, tlv: Tlv) {
321        self.unknown_attributes.push(tlv);
322    }
323
324    pub fn get_node_name(&self) -> Option<String> {
325        self.node_attributes
326            .get(&NodeAttributeType::NodeName)
327            .and_then(|bytes| String::from_utf8(bytes.clone()).ok())
328    }
329
330    pub fn get_link_name(&self) -> Option<String> {
331        self.link_attributes
332            .get(&LinkAttributeType::LinkName)
333            .and_then(|bytes| String::from_utf8(bytes.clone()).ok())
334    }
335
336    pub fn get_node_flags(&self) -> Option<u8> {
337        self.node_attributes
338            .get(&NodeAttributeType::NodeFlagBits)
339            .and_then(|bytes| bytes.first().copied())
340    }
341
342    pub fn get_administrative_group(&self) -> Option<u32> {
343        self.link_attributes
344            .get(&LinkAttributeType::AdministrativeGroup)
345            .and_then(|bytes| {
346                if bytes.len() >= 4 {
347                    Some(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
348                } else {
349                    None
350                }
351            })
352    }
353
354    pub fn get_maximum_link_bandwidth(&self) -> Option<f32> {
355        self.link_attributes
356            .get(&LinkAttributeType::MaximumLinkBandwidth)
357            .and_then(|bytes| {
358                if bytes.len() >= 4 {
359                    Some(f32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
360                } else {
361                    None
362                }
363            })
364    }
365
366    pub fn get_igp_metric(&self) -> Option<u32> {
367        self.link_attributes
368            .get(&LinkAttributeType::IgpMetric)
369            .and_then(|bytes| match bytes.len() {
370                1 => Some(bytes[0] as u32),
371                2 => Some(u16::from_be_bytes([bytes[0], bytes[1]]) as u32),
372                3 => Some(
373                    (u32::from(bytes[0]) << 16) + (u32::from(bytes[1]) << 8) + u32::from(bytes[2]),
374                ),
375                _ => None,
376            })
377    }
378
379    pub fn get_prefix_metric(&self) -> Option<u32> {
380        self.prefix_attributes
381            .get(&PrefixAttributeType::PrefixMetric)
382            .and_then(|bytes| {
383                if bytes.len() >= 4 {
384                    Some(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
385                } else {
386                    None
387                }
388            })
389    }
390
391    /// Get unidirectional link delay in microseconds - RFC 8571
392    pub fn get_unidirectional_link_delay(&self) -> Option<u32> {
393        self.link_attributes
394            .get(&LinkAttributeType::UnidirectionalLinkDelay)
395            .and_then(|bytes| {
396                if bytes.len() >= 4 {
397                    Some(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) & 0x00FFFFFF)
398                } else {
399                    None
400                }
401            })
402    }
403
404    /// Get min/max unidirectional link delay in microseconds - RFC 8571
405    /// Returns (min_delay, max_delay)
406    pub fn get_min_max_unidirectional_link_delay(&self) -> Option<(u32, u32)> {
407        self.link_attributes
408            .get(&LinkAttributeType::MinMaxUnidirectionalLinkDelay)
409            .and_then(|bytes| {
410                if bytes.len() >= 8 {
411                    let min_delay =
412                        u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) & 0x00FFFFFF;
413                    let max_delay =
414                        u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) & 0x00FFFFFF;
415                    Some((min_delay, max_delay))
416                } else {
417                    None
418                }
419            })
420    }
421
422    /// Get unidirectional delay variation in microseconds - RFC 8571
423    pub fn get_unidirectional_delay_variation(&self) -> Option<u32> {
424        self.link_attributes
425            .get(&LinkAttributeType::UnidirectionalDelayVariation)
426            .and_then(|bytes| {
427                if bytes.len() >= 4 {
428                    Some(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) & 0x00FFFFFF)
429                } else {
430                    None
431                }
432            })
433    }
434
435    /// Get unidirectional link loss percentage - RFC 8571
436    /// Returns loss as a percentage (0.000003% to 50.331642%)
437    pub fn get_unidirectional_link_loss(&self) -> Option<f32> {
438        self.link_attributes
439            .get(&LinkAttributeType::UnidirectionalLinkLoss)
440            .and_then(|bytes| {
441                if bytes.len() >= 4 {
442                    let raw_value =
443                        u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) & 0x00FFFFFF;
444                    Some(raw_value as f32 * 0.000003)
445                } else {
446                    None
447                }
448            })
449    }
450
451    /// Get unidirectional residual bandwidth in bytes per second - RFC 8571
452    pub fn get_unidirectional_residual_bandwidth(&self) -> Option<f32> {
453        self.link_attributes
454            .get(&LinkAttributeType::UnidirectionalResidualBandwidth)
455            .and_then(|bytes| {
456                if bytes.len() >= 4 {
457                    Some(f32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
458                } else {
459                    None
460                }
461            })
462    }
463
464    /// Get unidirectional available bandwidth in bytes per second - RFC 8571
465    pub fn get_unidirectional_available_bandwidth(&self) -> Option<f32> {
466        self.link_attributes
467            .get(&LinkAttributeType::UnidirectionalAvailableBandwidth)
468            .and_then(|bytes| {
469                if bytes.len() >= 4 {
470                    Some(f32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
471                } else {
472                    None
473                }
474            })
475    }
476
477    /// Get unidirectional utilized bandwidth in bytes per second - RFC 8571
478    pub fn get_unidirectional_utilized_bandwidth(&self) -> Option<f32> {
479        self.link_attributes
480            .get(&LinkAttributeType::UnidirectionalUtilizedBandwidth)
481            .and_then(|bytes| {
482                if bytes.len() >= 4 {
483                    Some(f32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
484                } else {
485                    None
486                }
487            })
488    }
489}
490
491#[cfg(test)]
492mod tests {
493    use super::*;
494    use std::str::FromStr;
495
496    #[test]
497    fn test_nlri_type_conversion() {
498        assert_eq!(NlriType::Node as u16, 1);
499        assert_eq!(NlriType::Link as u16, 2);
500        assert_eq!(NlriType::Ipv4TopologyPrefix as u16, 3);
501        assert_eq!(NlriType::Ipv6TopologyPrefix as u16, 4);
502    }
503
504    #[test]
505    fn test_protocol_id_conversion() {
506        assert_eq!(ProtocolId::IsisL1 as u8, 1);
507        assert_eq!(ProtocolId::Ospfv2 as u8, 3);
508        assert_eq!(ProtocolId::Ospfv3 as u8, 6);
509    }
510
511    #[test]
512    fn test_node_nlri_creation() {
513        let node_desc = NodeDescriptor {
514            autonomous_system: Some(65001),
515            igp_router_id: Some(vec![192, 168, 1, 1]),
516            ..Default::default()
517        };
518
519        let nlri = LinkStateNlri::new_node_nlri(ProtocolId::Ospfv2, 123456, node_desc);
520
521        assert_eq!(nlri.nlri_type, NlriType::Node);
522        assert_eq!(nlri.protocol_id, ProtocolId::Ospfv2);
523        assert_eq!(nlri.identifier, 123456);
524        assert_eq!(nlri.local_node_descriptors.autonomous_system, Some(65001));
525        assert!(nlri.remote_node_descriptors.is_none());
526        assert!(nlri.link_descriptors.is_none());
527        assert!(nlri.prefix_descriptors.is_none());
528    }
529
530    #[test]
531    fn test_link_nlri_creation() {
532        let local_desc = NodeDescriptor::default();
533        let remote_desc = NodeDescriptor::default();
534        let link_desc = LinkDescriptor::default();
535
536        let nlri = LinkStateNlri::new_link_nlri(
537            ProtocolId::IsisL1,
538            789012,
539            local_desc,
540            remote_desc,
541            link_desc,
542        );
543
544        assert_eq!(nlri.nlri_type, NlriType::Link);
545        assert_eq!(nlri.protocol_id, ProtocolId::IsisL1);
546        assert_eq!(nlri.identifier, 789012);
547        assert!(nlri.remote_node_descriptors.is_some());
548        assert!(nlri.link_descriptors.is_some());
549        assert!(nlri.prefix_descriptors.is_none());
550    }
551
552    #[test]
553    fn test_prefix_nlri_creation() {
554        let local_desc = NodeDescriptor::default();
555        let prefix_desc = PrefixDescriptor {
556            ip_reachability_information: Some(NetworkPrefix::from_str("192.168.1.0/24").unwrap()),
557            ..Default::default()
558        };
559
560        let nlri = LinkStateNlri::new_prefix_nlri(
561            NlriType::Ipv4TopologyPrefix,
562            ProtocolId::Ospfv2,
563            345678,
564            local_desc,
565            prefix_desc,
566        );
567
568        assert_eq!(nlri.nlri_type, NlriType::Ipv4TopologyPrefix);
569        assert_eq!(nlri.protocol_id, ProtocolId::Ospfv2);
570        assert_eq!(nlri.identifier, 345678);
571        assert!(nlri.remote_node_descriptors.is_none());
572        assert!(nlri.link_descriptors.is_none());
573        assert!(nlri.prefix_descriptors.is_some());
574    }
575
576    #[test]
577    fn test_link_state_attribute() {
578        let mut attr = LinkStateAttribute::new();
579
580        // Test node name
581        attr.add_node_attribute(NodeAttributeType::NodeName, b"router1".to_vec());
582        assert_eq!(attr.get_node_name(), Some("router1".to_string()));
583
584        // Test administrative group
585        attr.add_link_attribute(
586            LinkAttributeType::AdministrativeGroup,
587            vec![0x00, 0x00, 0x00, 0xFF],
588        );
589        assert_eq!(attr.get_administrative_group(), Some(255));
590
591        // Test IGP metric
592        attr.add_link_attribute(LinkAttributeType::IgpMetric, vec![0x01, 0x00]);
593        assert_eq!(attr.get_igp_metric(), Some(256));
594
595        // Test prefix metric
596        attr.add_prefix_attribute(
597            PrefixAttributeType::PrefixMetric,
598            vec![0x00, 0x00, 0x03, 0xE8],
599        );
600        assert_eq!(attr.get_prefix_metric(), Some(1000));
601    }
602
603    #[test]
604    fn test_tlv_creation() {
605        let tlv = Tlv::new(1024, vec![0x01, 0x02, 0x03]);
606        assert_eq!(tlv.tlv_type, 1024);
607        assert_eq!(tlv.value, vec![0x01, 0x02, 0x03]);
608    }
609
610    #[test]
611    #[cfg(feature = "serde")]
612    fn test_serde_serialization() {
613        let mut attr = LinkStateAttribute::new();
614        attr.add_node_attribute(NodeAttributeType::NodeName, b"test".to_vec());
615
616        let serialized = serde_json::to_string(&attr).unwrap();
617        let deserialized: LinkStateAttribute = serde_json::from_str(&serialized).unwrap();
618
619        assert_eq!(attr, deserialized);
620    }
621}