diff --git a/Cargo.toml b/Cargo.toml index 14dec4a..599492f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ospf-parser" -version = "0.5.0" +version = "0.5.4" description = "Parser for the OSPF version 2 protocol" license = "MIT/Apache-2.0" keywords = ["OSPF","routing","protocol","parser","nom"] diff --git a/src/ospfv2.rs b/src/ospfv2.rs index 4b6e8b3..53c50a7 100644 --- a/src/ospfv2.rs +++ b/src/ospfv2.rs @@ -1,9 +1,15 @@ +use std::fmt; use crate::parser::{parse_ospf_external_tos_routes, parse_ospf_tos_routes, parse_ospf_vec_u32}; +use nom::bytes::complete::take; +use nom::combinator::complete; +use nom::combinator::map_parser; +use nom::multi::many0; use nom::number::streaming::be_u24; use nom_derive::*; use rusticata_macros::newtype_enum; use std::net::Ipv4Addr; + #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, NomBE)] pub struct OspfPacketType(pub u8); @@ -33,7 +39,7 @@ pub enum Ospfv2Packet { /// contains all the necessary information to determine whether the /// packet should be accepted for further processing. This /// determination is described in Section 8.2 of the specification. -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Clone)] pub struct Ospfv2PacketHeader { #[nom(Verify = "*version == 2")] pub version: u8, @@ -52,6 +58,21 @@ impl Ospfv2PacketHeader { } } +impl fmt::Display for Ospfv2PacketHeader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Ospfv2PacketHeader {{ ")?; + write!(f, "version: {}, ", self.version)?; + write!(f, "packet_type: {} ({}), ", self.packet_type, self.packet_type.0)?; + write!(f, "packet_length: {}, ", self.packet_length)?; + write!(f, "router_id: {}, ", Ipv4Addr::from(self.router_id))?; + write!(f, "area_id: {}, ", Ipv4Addr::from(self.area_id))?; + write!(f, "checksum: {:#06X}, ", self.checksum)?; + write!(f, "au_type: {}, ", self.au_type)?; + write!(f, "authentication: {:#018X}", self.authentication)?; + write!(f, " }}") + } +} + /// The Hello packet /// /// Hello packets are OSPF packet type 1. These packets are sent @@ -98,6 +119,27 @@ impl OspfHelloPacket { } } +impl fmt::Display for OspfHelloPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let neighbors: Vec = self + .neighbor_list + .iter() + .map(|&n| Ipv4Addr::from(n).to_string()) + .collect(); + write!(f, "OspfHelloPacket {{ ")?; + write!(f, "header: {}, ", self.header)?; + write!(f, "network_mask: {}, ", Ipv4Addr::from(self.network_mask))?; + write!(f, "hello_interval: {}, ", self.hello_interval)?; + write!(f, "options: {:#04X}, ", self.options)?; + write!(f, "router_priority: {}, ", self.router_priority)?; + write!(f, "router_dead_interval: {}, ", self.router_dead_interval)?; + write!(f, "designated_router: {}, ", Ipv4Addr::from(self.designated_router))?; + write!(f, "backup_designated_router: {}, ", Ipv4Addr::from(self.backup_designated_router))?; + write!(f, "neighbor_list: [{}]", neighbors.join(", "))?; + write!(f, " }}") + } +} + /// The Database Description packet /// /// Database Description packets are OSPF packet type 2. These packets @@ -120,6 +162,26 @@ pub struct OspfDatabaseDescriptionPacket { pub lsa_headers: Vec, } +impl fmt::Display for OspfDatabaseDescriptionPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfDatabaseDescriptionPacket {{ ")?; + write!(f, "header: {}, ", self.header)?; + write!(f, "if_mtu: {}, ", self.if_mtu)?; + write!(f, "options: {:#04X}, ", self.options)?; + write!(f, "flags: {:#04X}, ", self.flags)?; + write!(f, "dd_sequence_number: {:#010X}, ", self.dd_sequence_number)?; + write!(f, "lsa_headers: [")?; + for (i, lsa) in self.lsa_headers.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", lsa)?; + } + write!(f, "]")?; + write!(f, " }}") + } +} + /// The Link State Request packet /// /// Link State Request packets are OSPF packet type 3. After exchanging @@ -143,9 +205,30 @@ pub struct OspfDatabaseDescriptionPacket { pub struct OspfLinkStateRequestPacket { #[nom(Verify = "header.packet_type == OspfPacketType::LinkStateRequest")] pub header: Ospfv2PacketHeader, + // Subtract the 24-byte OSPF header from total packet_length + #[nom(Parse = "{ + let len = (header.packet_length as usize).saturating_sub(24); + map_parser(take(len), many0(complete(OspfLinkStateRequest::parse_be))) + }")] pub requests: Vec, } +impl fmt::Display for OspfLinkStateRequestPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfLinkStateRequestPacket {{ ")?; + write!(f, "header: {}, ", self.header)?; + write!(f, "requests: [")?; + for (i, req) in self.requests.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", req)?; + } + write!(f, "]")?; + write!(f, " }}") + } +} + #[derive(Debug, NomBE)] pub struct OspfLinkStateRequest { // XXX should be a OspfLinkStateType, but it is only an u8 @@ -164,6 +247,16 @@ impl OspfLinkStateRequest { } } +impl fmt::Display for OspfLinkStateRequest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfLinkStateRequest {{ ")?; + write!(f, "link_state_type: {}, ", self.link_state_type)?; + write!(f, "link_state_id: {}, ", Ipv4Addr::from(self.link_state_id))?; + write!(f, "advertising_router: {}", Ipv4Addr::from(self.advertising_router))?; + write!(f, " }}") + } +} + /// The Link State Update packet /// /// Link State Update packets are OSPF packet type 4. These packets @@ -180,7 +273,7 @@ impl OspfLinkStateRequest { /// always carried by unicast Link State Update packets. For more /// information on the reliable flooding of link state advertisements, /// consult Section 13. -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Clone)] pub struct OspfLinkStateUpdatePacket { #[nom(Verify = "header.packet_type == OspfPacketType::LinkStateUpdate")] pub header: Ospfv2PacketHeader, @@ -189,6 +282,23 @@ pub struct OspfLinkStateUpdatePacket { pub lsa: Vec, } +impl fmt::Display for OspfLinkStateUpdatePacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfLinkStateUpdatePacket {{ ")?; + write!(f, "header: {}, ", self.header)?; + write!(f, "num_advertisements: {}, ", self.num_advertisements)?; + write!(f, "lsa: [")?; + for (i, lsa) in self.lsa.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", lsa)?; + } + write!(f, "]")?; + write!(f, " }}") + } +} + /// The Link State Acknowledgment packet /// /// Link State Acknowledgment Packets are OSPF packet type 5. To make @@ -213,10 +323,31 @@ pub struct OspfLinkStateUpdatePacket { pub struct OspfLinkStateAcknowledgmentPacket { #[nom(Verify = "header.packet_type == OspfPacketType::LinkStateAcknowledgment")] pub header: Ospfv2PacketHeader, + // Subtract the 24-byte OSPF header from total packet_length + #[nom(Parse = "{ + let len = (header.packet_length as usize).saturating_sub(24); + map_parser(take(len), many0(complete(OspfLinkStateAdvertisementHeader::parse_be))) + }")] pub lsa_headers: Vec, } -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, NomBE)] +impl fmt::Display for OspfLinkStateAcknowledgmentPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfLinkStateAcknowledgmentPacket {{ ")?; + write!(f, "header: {}, ", self.header)?; + write!(f, "lsa_headers: [")?; + for (i, lsa) in self.lsa_headers.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", lsa)?; + } + write!(f, "]")?; + write!(f, " }}") + } +} + +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, NomBE)] pub struct OspfLinkStateType(pub u8); newtype_enum! { @@ -243,7 +374,7 @@ impl display OspfLinkStateType { /// which instance is more recent. This is accomplished by examining /// the LS age, LS sequence number and LS checksum fields that are also /// contained in the link state advertisement header. -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Eq, PartialEq, Hash, Copy, Clone)] pub struct OspfLinkStateAdvertisementHeader { pub ls_age: u16, pub options: u8, @@ -265,8 +396,23 @@ impl OspfLinkStateAdvertisementHeader { } } +impl fmt::Display for OspfLinkStateAdvertisementHeader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfLinkStateAdvertisementHeader {{ ")?; + write!(f, "ls_age: {}, ", self.ls_age)?; + write!(f, "options: {:#04X}, ", self.options)?; + write!(f, "link_state_type: {} ({}), ", self.link_state_type, self.link_state_type.0)?; + write!(f, "link_state_id: {}, ", Ipv4Addr::from(self.link_state_id))?; + write!(f, "advertising_router: {}, ", Ipv4Addr::from(self.advertising_router))?; + write!(f, "ls_seq_number: {:#010X}, ", self.ls_seq_number)?; + write!(f, "ls_checksum: {:#06X}, ", self.ls_checksum)?; + write!(f, "length: {}", self.length)?; + write!(f, " }}") + } +} + /// Link state advertisements -#[derive(Debug)] +#[derive(Debug, Hash, Eq, PartialEq, Clone)] pub enum OspfLinkStateAdvertisement { RouterLinks(OspfRouterLinksAdvertisement), NetworkLinks(OspfNetworkLinksAdvertisement), @@ -279,6 +425,22 @@ pub enum OspfLinkStateAdvertisement { OpaqueASWideScope(OspfOpaqueLinkAdvertisement), } +impl fmt::Display for OspfLinkStateAdvertisement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + OspfLinkStateAdvertisement::RouterLinks(lsa) => write!(f, "{}", lsa), + OspfLinkStateAdvertisement::NetworkLinks(lsa) => write!(f, "{}", lsa), + OspfLinkStateAdvertisement::SummaryLinkIpNetwork(lsa) => write!(f, "{}", lsa), + OspfLinkStateAdvertisement::SummaryLinkAsbr(lsa) => write!(f, "{}", lsa), + OspfLinkStateAdvertisement::ASExternalLink(lsa) => write!(f, "{}", lsa), + OspfLinkStateAdvertisement::NSSAASExternal(lsa) => write!(f, "{}", lsa), + OspfLinkStateAdvertisement::OpaqueLinkLocalScope(lsa) => write!(f, "{}", lsa), + OspfLinkStateAdvertisement::OpaqueAreaLocalScope(lsa) => write!(f, "{}", lsa), + OspfLinkStateAdvertisement::OpaqueASWideScope(lsa) => write!(f, "{}", lsa), + } + } +} + /// Router links advertisements /// /// Router links advertisements are the Type 1 link state @@ -288,7 +450,7 @@ pub enum OspfLinkStateAdvertisement { /// router's links to the area must be described in a single router /// links advertisement. For details concerning the construction of /// router links advertisements, see Section 12.4.1. -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Hash, Eq, PartialEq, Clone)] pub struct OspfRouterLinksAdvertisement { #[nom(Verify = "header.link_state_type == OspfLinkStateType::RouterLinks")] pub header: OspfLinkStateAdvertisementHeader, @@ -298,7 +460,25 @@ pub struct OspfRouterLinksAdvertisement { pub links: Vec, } -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, NomBE)] +impl fmt::Display for OspfRouterLinksAdvertisement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfRouterLinksAdvertisement {{ ")?; + write!(f, "header: {}, ", self.header)?; + write!(f, "flags: {}, ", self.flags)?; + write!(f, "num_links: {}, ", self.num_links)?; + write!(f, "links: [")?; + for (i, link) in self.links.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", link)?; + } + write!(f, "]")?; + write!(f, " }}") + } +} + +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, NomBE, Hash)] pub struct OspfRouterLinkType(pub u8); newtype_enum! { @@ -311,7 +491,7 @@ impl display OspfRouterLinkType { } /// OSPF router link (i.e., interface) -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Hash, Eq, PartialEq, Clone)] pub struct OspfRouterLink { pub link_id: u32, pub link_data: u32, @@ -322,6 +502,26 @@ pub struct OspfRouterLink { pub tos_list: Vec, } +impl fmt::Display for OspfRouterLink { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfRouterLink {{ ")?; + write!(f, "link_id: {}, ", Ipv4Addr::from(self.link_id))?; + write!(f, "link_data: {}, ", Ipv4Addr::from(self.link_data))?; + write!(f, "link_type: {} ({}), ", self.link_type, self.link_type.0)?; + write!(f, "num_tos: {}, ", self.num_tos)?; + write!(f, "tos_0_metric: {}, ", self.tos_0_metric)?; + write!(f, "tos_list: [")?; + for (i, tos) in self.tos_list.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", tos)?; + } + write!(f, "]")?; + write!(f, " }}") + } +} + impl OspfRouterLink { pub fn link_id(&self) -> Ipv4Addr { Ipv4Addr::from(self.link_id) @@ -333,13 +533,23 @@ impl OspfRouterLink { } /// OSPF Router Type Of Service (TOS) -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Hash, Eq, PartialEq, Clone)] pub struct OspfRouterTOS { pub tos: u8, pub reserved: u8, pub metric: u16, } +impl fmt::Display for OspfRouterTOS { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfRouterTOS {{ ")?; + write!(f, "tos: {}, ", self.tos)?; + write!(f, "reserved: {}, ", self.reserved)?; + write!(f, "metric: {}", self.metric)?; + write!(f, " }}") + } +} + /// Network links advertisements /// /// Network links advertisements are the Type 2 link state @@ -357,7 +567,7 @@ pub struct OspfRouterTOS { /// not be specified in the network links advertisement. For details /// concerning the construction of network links advertisements, see /// Section 12.4.2. -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Hash, Eq, PartialEq, Clone)] pub struct OspfNetworkLinksAdvertisement { #[nom(Verify = "header.link_state_type == OspfLinkStateType::NetworkLinks")] pub header: OspfLinkStateAdvertisementHeader, @@ -367,6 +577,21 @@ pub struct OspfNetworkLinksAdvertisement { pub attached_routers: Vec, } +impl fmt::Display for OspfNetworkLinksAdvertisement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let routers: Vec = self + .attached_routers + .iter() + .map(|&r| Ipv4Addr::from(r).to_string()) + .collect(); + write!(f, "OspfNetworkLinksAdvertisement {{ ")?; + write!(f, "header: {}, ", self.header)?; + write!(f, "network_mask: {}, ", Ipv4Addr::from(self.network_mask))?; + write!(f, "attached_routers: [{}]", routers.join(", "))?; + write!(f, " }}") + } +} + impl OspfNetworkLinksAdvertisement { pub fn network_mask(&self) -> Ipv4Addr { Ipv4Addr::from(self.network_mask) @@ -396,7 +621,7 @@ impl OspfNetworkLinksAdvertisement { /// advertise the location of each ASBR, consult Section 16.4.) Other /// than the difference in the Link State ID field, the format of Type 3 /// and 4 link state advertisements is identical. -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Hash, Eq, PartialEq, Clone)] pub struct OspfSummaryLinkAdvertisement { #[nom( Verify = "header.link_state_type == OspfLinkStateType::SummaryLinkIpNetwork || @@ -412,19 +637,47 @@ pub struct OspfSummaryLinkAdvertisement { pub tos_routes: Vec, } +impl fmt::Display for OspfSummaryLinkAdvertisement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfSummaryLinkAdvertisement {{ ")?; + write!(f, "header: {}, ", self.header)?; + write!(f, "network_mask: {}, ", Ipv4Addr::from(self.network_mask))?; + write!(f, "tos: {}, ", self.tos)?; + write!(f, "metric: {}, ", self.metric)?; + write!(f, "tos_routes: [")?; + for (i, route) in self.tos_routes.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", route)?; + } + write!(f, "]")?; + write!(f, " }}") + } +} + impl OspfSummaryLinkAdvertisement { pub fn network_mask(&self) -> Ipv4Addr { Ipv4Addr::from(self.network_mask) } } -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Hash, Eq, PartialEq, Clone)] pub struct OspfTosRoute { pub tos: u8, #[nom(Parse = "be_u24")] pub metric: u32, } +impl fmt::Display for OspfTosRoute { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfTosRoute {{ ")?; + write!(f, "tos: {}, ", self.tos)?; + write!(f, "metric: {}", self.metric)?; + write!(f, " }}") + } +} + /// AS external link advertisements /// /// AS external link advertisements are the Type 5 link state @@ -437,13 +690,14 @@ pub struct OspfTosRoute { /// AS external link advertisements usually describe a particular /// external destination. For these advertisements the Link State ID /// field specifies an IP network number (if necessary, the Link State +/// /// ID can also have one or more of the network's "host" bits set; see /// Appendix F for details). AS external link advertisements are also /// used to describe a default route. Default routes are used when no /// specific route exists to the destination. When describing a default /// route, the Link State ID is always set to DefaultDestination /// (0.0.0.0) and the Network Mask is set to 0.0.0.0. -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Hash, Eq, PartialEq, Clone)] pub struct OspfASExternalLinkAdvertisement { #[nom(Verify = "header.link_state_type == OspfLinkStateType::ASExternalLink")] pub header: OspfLinkStateAdvertisementHeader, @@ -458,6 +712,27 @@ pub struct OspfASExternalLinkAdvertisement { pub tos_list: Vec, } +impl fmt::Display for OspfASExternalLinkAdvertisement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfASExternalLinkAdvertisement {{ ")?; + write!(f, "header: {}, ", self.header)?; + write!(f, "network_mask: {}, ", Ipv4Addr::from(self.network_mask))?; + write!(f, "external_and_reserved: {}, ", self.external_and_reserved)?; + write!(f, "metric: {}, ", self.metric)?; + write!(f, "forwarding_address: {}, ", Ipv4Addr::from(self.forwarding_address))?; + write!(f, "external_route_tag: {}, ", self.external_route_tag)?; + write!(f, "tos_list: [")?; + for (i, tos) in self.tos_list.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", tos)?; + } + write!(f, "]")?; + write!(f, " }}") + } +} + impl OspfASExternalLinkAdvertisement { pub fn forwarding_address(&self) -> Ipv4Addr { Ipv4Addr::from(self.forwarding_address) @@ -467,7 +742,7 @@ impl OspfASExternalLinkAdvertisement { } } -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Hash, Eq, PartialEq, Clone)] pub struct OspfExternalTosRoute { pub tos: u8, #[nom(Parse = "be_u24")] @@ -476,6 +751,17 @@ pub struct OspfExternalTosRoute { pub external_route_tag: u32, } +impl fmt::Display for OspfExternalTosRoute { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfExternalTosRoute {{ ")?; + write!(f, "tos: {}, ", self.tos)?; + write!(f, "metric: {}, ", self.metric)?; + write!(f, "forwarding_address: {}, ", Ipv4Addr::from(self.forwarding_address))?; + write!(f, "external_route_tag: {}", self.external_route_tag)?; + write!(f, " }}") + } +} + impl OspfExternalTosRoute { pub fn forwarding_address(&self) -> Ipv4Addr { Ipv4Addr::from(self.forwarding_address) @@ -483,7 +769,7 @@ impl OspfExternalTosRoute { } /// NSSA AS-External LSA (type 7, rfc1587, rfc3101) -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Hash, Eq, PartialEq, Clone)] pub struct OspfNSSAExternalLinkAdvertisement { #[nom(Verify = "header.link_state_type == OspfLinkStateType::NSSAASExternal")] pub header: OspfLinkStateAdvertisementHeader, @@ -498,6 +784,27 @@ pub struct OspfNSSAExternalLinkAdvertisement { pub tos_list: Vec, } +impl fmt::Display for OspfNSSAExternalLinkAdvertisement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfNSSAExternalLinkAdvertisement {{ ")?; + write!(f, "header: {}, ", self.header)?; + write!(f, "network_mask: {}, ", Ipv4Addr::from(self.network_mask))?; + write!(f, "external_and_tos: {}, ", self.external_and_tos)?; + write!(f, "metric: {}, ", self.metric)?; + write!(f, "forwarding_address: {}, ", Ipv4Addr::from(self.forwarding_address))?; + write!(f, "external_route_tag: {}, ", self.external_route_tag)?; + write!(f, "tos_list: [")?; + for (i, tos) in self.tos_list.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", tos)?; + } + write!(f, "]")?; + write!(f, " }}") + } +} + impl OspfNSSAExternalLinkAdvertisement { pub fn forwarding_address(&self) -> Ipv4Addr { Ipv4Addr::from(self.forwarding_address) @@ -523,7 +830,7 @@ impl OspfNSSAExternalLinkAdvertisement { /// be link-local (type-9), area-local (type-10), or the entire OSPF /// routing domain (type-11). Section 3 of this document describes the /// flooding procedures for the Opaque LSA. -#[derive(Debug, NomBE)] +#[derive(Debug, NomBE, Hash, Eq, PartialEq, Clone)] pub struct OspfOpaqueLinkAdvertisement { #[nom( Verify = "header.link_state_type == OspfLinkStateType::OpaqueLinkLocalScope || @@ -534,6 +841,15 @@ pub struct OspfOpaqueLinkAdvertisement { pub data: Vec, } +impl fmt::Display for OspfOpaqueLinkAdvertisement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OspfOpaqueLinkAdvertisement {{ ")?; + write!(f, "header: {}, ", self.header)?; + write!(f, "data: {:X?}", self.data)?; + write!(f, " }}") + } +} + impl OspfOpaqueLinkAdvertisement { pub fn opaque_type(&self) -> u8 { (self.header.link_state_id >> 24) as u8 diff --git a/src/parser.rs b/src/parser.rs index 7fa73d7..732f43c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -87,7 +87,7 @@ pub fn parse_ospfv2_link_state_request_packet( OspfLinkStateRequestPacket::parse(input) } -impl<'a> Parse<&'a [u8]> for OspfLinkStateAdvertisement { +impl Parse<& [u8]> for OspfLinkStateAdvertisement { fn parse(input: &[u8]) -> IResult<&[u8], OspfLinkStateAdvertisement> { let (_, word) = peek(be_u32)(input)?; let ls_type = (word & 0xff) as u8; @@ -133,7 +133,7 @@ impl<'a> Parse<&'a [u8]> for OspfLinkStateAdvertisement { } } -impl<'a> Parse<&'a [u8]> for Ospfv3LinkStateAdvertisement { +impl Parse<&[u8]> for Ospfv3LinkStateAdvertisement { fn parse(input: &[u8]) -> IResult<&[u8], Ospfv3LinkStateAdvertisement> { let (_, word) = peek(be_u32)(input)?; let ls_type = (word & 0xffff) as u16; diff --git a/tests/ospf_parser.rs b/tests/ospf_parser.rs index 31793b6..eb90dcc 100644 --- a/tests/ospf_parser.rs +++ b/tests/ospf_parser.rs @@ -294,3 +294,200 @@ pub fn test_lsa_type7() { panic!("wrong lsa type"); } } + +#[test] +pub fn test_link_state_request_with_auth() { + let lsa_request_bytes: Vec = vec![ + 0x2, // version + 0x3, // packet type + 0x0, 0x24, // packet length (36) + 0x2, 0x2, 0x2, 0x2, // router + 0x0, 0x0, 0x0, 0x0, // area + 0x0, 0x0, // checksum + 0x0, 0x2, // au type + 0x0, 0x0, 0x1, 0x10, // authentication + 0x69, 0xa, 0xc0, 0xb2, + // ----- + 0x0, 0x0, 0x0, 0x1, // LS type + 0x1, 0x1, 0x1, 0x1, // link state ID + 0x1, 0x1, 0x1, 0x1, // adv. router + // ----- + // signature + 0x98, 0x35, 0xda, 0x13, 0xd5, 0x3f, 0xe9, 0x51, + 0xd8, 0x40, 0xf4, 0xab, 0x10, 0x17, 0xc0, 0x2c]; + + let (remaining, ospfv2_packet) = ospf_parser::parse_ospfv2_packet(&lsa_request_bytes).unwrap(); + let Ospfv2Packet::LinkStateRequest(lsa_request) = ospfv2_packet else { + panic!("failed to parse Ospfv2"); + }; + assert_eq!(*remaining, lsa_request_bytes[36..52]); + assert_eq!(lsa_request.requests.len(), 1); +} + +#[test] +pub fn test_ospfv2_packet_header_display() { + let header = Ospfv2PacketHeader { + version: 2, + packet_type: OspfPacketType::Hello, + packet_length: 42, + router_id: Ipv4Addr::new(10, 1, 1, 1).into(), + area_id: 0, + checksum: 0xABCD, + au_type: 2, + authentication: 0x0102030405060708, + }; + let s = format!("{}", header); + assert!(s.contains("version: 2")); + assert!(s.contains("packet_type: Hello (1)")); + assert!(s.contains("packet_length: 42")); + assert!(s.contains("router_id: 10.1.1.1")); + assert!(s.contains("area_id: 0.0.0.0")); + assert!(s.contains("checksum: 0xABCD")); + assert!(s.contains("au_type: 2")); + assert!(s.contains("authentication: 0x0102030405060708")); +} + +#[test] +pub fn test_ospf_hello_packet_display() { + let header = Ospfv2PacketHeader { + version: 2, + packet_type: OspfPacketType::Hello, + packet_length: 56, + router_id: Ipv4Addr::new(10, 1, 1, 1).into(), + area_id: 0, + checksum: 0xABCD, + au_type: 2, + authentication: 0x0102030405060708, + }; + let hello_packet = OspfHelloPacket { + header, + network_mask: Ipv4Addr::new(255, 255, 255, 0).into(), + hello_interval: 10, + options: 0x42, + router_priority: 1, + router_dead_interval: 40, + designated_router: Ipv4Addr::new(10, 1, 1, 2).into(), + backup_designated_router: Ipv4Addr::new(10, 1, 1, 3).into(), + neighbor_list: vec![ + Ipv4Addr::new(10, 1, 1, 4).into(), + Ipv4Addr::new(10, 1, 1, 5).into(), + ], + }; + let s = format!("{}", hello_packet); + assert!(s.contains("network_mask: 255.255.255.0")); + assert!(s.contains("options: 0x42")); + assert!(s.contains("designated_router: 10.1.1.2")); + assert!(s.contains("backup_designated_router: 10.1.1.3")); + assert!(s.contains(r#"neighbor_list: [10.1.1.4, 10.1.1.5]"#)); +} + +#[test] +pub fn test_ospf_database_description_packet_display() { + let header = Ospfv2PacketHeader { + version: 2, + packet_type: OspfPacketType::DatabaseDescription, + packet_length: 68, + router_id: Ipv4Addr::new(10, 1, 1, 1).into(), + area_id: 0, + checksum: 0xABCD, + au_type: 2, + authentication: 0x0102030405060708, + }; + let lsa_header = OspfLinkStateAdvertisementHeader { + ls_age: 3600, + options: 0x42, + link_state_type: OspfLinkStateType::RouterLinks, + link_state_id: Ipv4Addr::new(10, 1, 1, 2).into(), + advertising_router: Ipv4Addr::new(10, 1, 1, 1).into(), + ls_seq_number: 0x80000001, + ls_checksum: 0x1234, + length: 20, + }; + let db_packet = OspfDatabaseDescriptionPacket { + header, + if_mtu: 1500, + options: 0x42, + flags: 0x07, + dd_sequence_number: 12345, + lsa_headers: vec![lsa_header], + }; + let s = format!("{}", db_packet); + assert!(s.contains("if_mtu: 1500")); + assert!(s.contains("options: 0x42")); + assert!(s.contains("flags: 0x07")); + assert!(s.contains("dd_sequence_number: 0x00003039")); + assert!(s.contains("lsa_headers: [")); + assert!(s.contains("ls_age: 3600")); + assert!(s.contains("link_state_type: RouterLinks (1)")); + assert!(s.contains("link_state_id: 10.1.1.2")); + assert!(s.contains("advertising_router: 10.1.1.1")); + assert!(s.contains("ls_seq_number: 0x80000001")); + assert!(s.contains("ls_checksum: 0x1234")); +} + +#[test] +pub fn test_ospf_link_state_request_packet_display() { + let header = Ospfv2PacketHeader { + version: 2, + packet_type: OspfPacketType::LinkStateRequest, + packet_length: 48, + router_id: Ipv4Addr::new(10, 1, 1, 1).into(), + area_id: 0, + checksum: 0xABCD, + au_type: 2, + authentication: 0x0102030405060708, + }; + let request = OspfLinkStateRequest { + link_state_type: 1, + link_state_id: Ipv4Addr::new(10, 1, 1, 2).into(), + advertising_router: Ipv4Addr::new(10, 1, 1, 1).into(), + }; + let req_packet = OspfLinkStateRequestPacket { + header, + requests: vec![request], + }; + let s = format!("{}", req_packet); + assert!(s.contains("requests: [")); + assert!(s.contains("link_state_type: 1")); + assert!(s.contains("link_state_id: 10.1.1.2")); + assert!(s.contains("advertising_router: 10.1.1.1")); +} + +#[test] +pub fn test_ospf_link_state_update_packet_display() { + let header = Ospfv2PacketHeader { + version: 2, + packet_type: OspfPacketType::LinkStateUpdate, + packet_length: 68, + router_id: Ipv4Addr::new(10, 1, 1, 1).into(), + area_id: 0, + checksum: 0xABCD, + au_type: 2, + authentication: 0x0102030405060708, + }; + let lsa_header = OspfLinkStateAdvertisementHeader { + ls_age: 3600, + options: 0x42, + link_state_type: OspfLinkStateType::RouterLinks, + link_state_id: Ipv4Addr::new(10, 1, 1, 2).into(), + advertising_router: Ipv4Addr::new(10, 1, 1, 1).into(), + ls_seq_number: 0x80000001, + ls_checksum: 0x1234, + length: 20, + }; + let router_lsa = OspfRouterLinksAdvertisement { + header: lsa_header, + flags: 0, + num_links: 0, + links: vec![], + }; + let lsu_packet = OspfLinkStateUpdatePacket { + header, + num_advertisements: 1, + lsa: vec![OspfLinkStateAdvertisement::RouterLinks(router_lsa)], + }; + let s = format!("{}", lsu_packet); + assert!(s.contains("num_advertisements: 1")); + assert!(s.contains("lsa: [")); + assert!(s.contains("ls_age: 3600")); +}