ospf_parser/ospfv2.rs
1use crate::parser::{parse_ospf_external_tos_routes, parse_ospf_tos_routes, parse_ospf_vec_u32};
2use nom::number::streaming::be_u24;
3use nom_derive::*;
4use rusticata_macros::newtype_enum;
5use std::net::Ipv4Addr;
6
7#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, NomBE)]
8pub struct OspfPacketType(pub u8);
9
10newtype_enum! {
11impl display OspfPacketType {
12 Hello = 1,
13 DatabaseDescription = 2,
14 LinkStateRequest = 3,
15 LinkStateUpdate = 4,
16 LinkStateAcknowledgment = 5,
17}
18}
19
20/// An OSPF version 2 packet
21#[derive(Debug)]
22pub enum Ospfv2Packet {
23 Hello(OspfHelloPacket),
24 DatabaseDescription(OspfDatabaseDescriptionPacket),
25 LinkStateRequest(OspfLinkStateRequestPacket),
26 LinkStateUpdate(OspfLinkStateUpdatePacket),
27 LinkStateAcknowledgment(OspfLinkStateAcknowledgmentPacket),
28}
29
30/// The OSPF packet header
31///
32/// Every OSPF packet starts with a common 24 byte header. This header
33/// contains all the necessary information to determine whether the
34/// packet should be accepted for further processing. This
35/// determination is described in Section 8.2 of the specification.
36#[derive(Debug, NomBE)]
37pub struct Ospfv2PacketHeader {
38 #[nom(Verify = "*version == 2")]
39 pub version: u8,
40 pub packet_type: OspfPacketType,
41 pub packet_length: u16,
42 pub router_id: u32,
43 pub area_id: u32,
44 pub checksum: u16,
45 pub au_type: u16,
46 pub authentication: u64,
47}
48
49impl Ospfv2PacketHeader {
50 pub fn source_router(&self) -> Ipv4Addr {
51 Ipv4Addr::from(self.router_id)
52 }
53}
54
55/// The Hello packet
56///
57/// Hello packets are OSPF packet type 1. These packets are sent
58/// periodically on all interfaces (including virtual links) in order to
59/// establish and maintain neighbor relationships. In addition, Hello
60/// Packets are multicast on those physical networks having a multicast
61/// or broadcast capability, enabling dynamic discovery of neighboring
62/// routers.
63///
64/// All routers connected to a common network must agree on certain
65/// parameters (Network mask, HelloInterval and RouterDeadInterval).
66/// These parameters are included in Hello packets, so that differences
67/// can inhibit the forming of neighbor relationships. A detailed
68/// explanation of the receive processing for Hello packets is presented
69/// in Section 10.5. The sending of Hello packets is covered in Section
70/// 9.5.
71#[derive(Debug, NomBE)]
72pub struct OspfHelloPacket {
73 #[nom(Verify = "header.packet_type == OspfPacketType::Hello")]
74 pub header: Ospfv2PacketHeader,
75 pub network_mask: u32,
76 pub hello_interval: u16,
77 pub options: u8,
78 pub router_priority: u8,
79 pub router_dead_interval: u32,
80 pub designated_router: u32,
81 pub backup_designated_router: u32,
82 // limit parsing to (length-xxx) bytes
83 #[nom(Parse = "parse_ospf_vec_u32(header.packet_length, 44)")]
84 pub neighbor_list: Vec<u32>,
85}
86
87impl OspfHelloPacket {
88 pub fn network_mask(&self) -> Ipv4Addr {
89 Ipv4Addr::from(self.network_mask)
90 }
91
92 pub fn designated_router(&self) -> Ipv4Addr {
93 Ipv4Addr::from(self.designated_router)
94 }
95
96 pub fn backup_designated_router(&self) -> Ipv4Addr {
97 Ipv4Addr::from(self.backup_designated_router)
98 }
99}
100
101/// The Database Description packet
102///
103/// Database Description packets are OSPF packet type 2. These packets
104/// are exchanged when an adjacency is being initialized. They describe
105/// the contents of the topological database. Multiple packets may be
106/// used to describe the database. For this purpose a poll-response
107/// procedure is used. One of the routers is designated to be master,
108/// the other a slave. The master sends Database Description packets
109/// (polls) which are acknowledged by Database Description packets sent
110/// by the slave (responses). The responses are linked to the polls via
111/// the packets' DD sequence numbers.
112#[derive(Debug, NomBE)]
113pub struct OspfDatabaseDescriptionPacket {
114 #[nom(Verify = "header.packet_type == OspfPacketType::DatabaseDescription")]
115 pub header: Ospfv2PacketHeader,
116 pub if_mtu: u16,
117 pub options: u8,
118 pub flags: u8,
119 pub dd_sequence_number: u32,
120 pub lsa_headers: Vec<OspfLinkStateAdvertisementHeader>,
121}
122
123/// The Link State Request packet
124///
125/// Link State Request packets are OSPF packet type 3. After exchanging
126/// Database Description packets with a neighboring router, a router may
127/// find that parts of its topological database are out of date. The
128/// Link State Request packet is used to request the pieces of the
129/// neighbor's database that are more up to date. Multiple Link State
130/// Request packets may need to be used. The sending of Link State
131/// Request packets is the last step in bringing up an adjacency.
132///
133/// A router that sends a Link State Request packet has in mind the
134/// precise instance of the database pieces it is requesting, defined by
135/// LS sequence number, LS checksum, and LS age, although these fields
136/// are not specified in the Link State Request Packet itself. The
137/// router may receive even more recent instances in response.
138///
139/// The sending of Link State Request packets is documented in Section
140/// 10.9. The reception of Link State Request packets is documented in
141/// Section 10.7.
142#[derive(Debug, NomBE)]
143pub struct OspfLinkStateRequestPacket {
144 #[nom(Verify = "header.packet_type == OspfPacketType::LinkStateRequest")]
145 pub header: Ospfv2PacketHeader,
146 pub requests: Vec<OspfLinkStateRequest>,
147}
148
149#[derive(Debug, NomBE)]
150pub struct OspfLinkStateRequest {
151 // XXX should be a OspfLinkStateType, but it is only an u8
152 pub link_state_type: u32,
153 pub link_state_id: u32,
154 pub advertising_router: u32,
155}
156
157impl OspfLinkStateRequest {
158 pub fn link_state_id(&self) -> Ipv4Addr {
159 Ipv4Addr::from(self.link_state_id)
160 }
161
162 pub fn advertising_router(&self) -> Ipv4Addr {
163 Ipv4Addr::from(self.advertising_router)
164 }
165}
166
167/// The Link State Update packet
168///
169/// Link State Update packets are OSPF packet type 4. These packets
170/// implement the flooding of link state advertisements. Each Link
171/// State Update packet carries a collection of link state
172/// advertisements one hop further from its origin. Several link state
173/// advertisements may be included in a single packet.
174///
175/// Link State Update packets are multicast on those physical networks
176/// that support multicast/broadcast. In order to make the flooding
177/// procedure reliable, flooded advertisements are acknowledged in Link
178/// State Acknowledgment packets. If retransmission of certain
179/// advertisements is necessary, the retransmitted advertisements are
180/// always carried by unicast Link State Update packets. For more
181/// information on the reliable flooding of link state advertisements,
182/// consult Section 13.
183#[derive(Debug, NomBE)]
184pub struct OspfLinkStateUpdatePacket {
185 #[nom(Verify = "header.packet_type == OspfPacketType::LinkStateUpdate")]
186 pub header: Ospfv2PacketHeader,
187 pub num_advertisements: u32,
188 #[nom(Count = "num_advertisements")]
189 pub lsa: Vec<OspfLinkStateAdvertisement>,
190}
191
192/// The Link State Acknowledgment packet
193///
194/// Link State Acknowledgment Packets are OSPF packet type 5. To make
195/// the flooding of link state advertisements reliable, flooded
196/// advertisements are explicitly acknowledged. This acknowledgment is
197/// accomplished through the sending and receiving of Link State
198/// Acknowledgment packets. Multiple link state advertisements can be
199/// acknowledged in a single Link State Acknowledgment packet.
200///
201/// Depending on the state of the sending interface and the source of
202/// the advertisements being acknowledged, a Link State Acknowledgment
203/// packet is sent either to the multicast address AllSPFRouters, to the
204/// multicast address AllDRouters, or as a unicast. The sending of Link
205/// State Acknowledgement packets is documented in Section 13.5. The
206/// reception of Link State Acknowledgement packets is documented in
207/// Section 13.7.
208///
209/// The format of this packet is similar to that of the Data Description
210/// packet. The body of both packets is simply a list of link state
211/// advertisement headers.
212#[derive(Debug, NomBE)]
213pub struct OspfLinkStateAcknowledgmentPacket {
214 #[nom(Verify = "header.packet_type == OspfPacketType::LinkStateAcknowledgment")]
215 pub header: Ospfv2PacketHeader,
216 pub lsa_headers: Vec<OspfLinkStateAdvertisementHeader>,
217}
218
219#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, NomBE)]
220pub struct OspfLinkStateType(pub u8);
221
222newtype_enum! {
223impl display OspfLinkStateType {
224 RouterLinks = 1,
225 NetworkLinks = 2,
226 SummaryLinkIpNetwork = 3,
227 SummaryLinkAsbr = 4,
228 ASExternalLink = 5,
229 NSSAASExternal = 7,
230 OpaqueLinkLocalScope = 9,
231 OpaqueAreaLocalScope = 10,
232 OpaqueASWideScope = 11,
233}
234}
235
236/// The Link State Advertisement header
237///
238/// All link state advertisements begin with a common 20 byte header.
239/// This header contains enough information to uniquely identify the
240/// advertisement (LS type, Link State ID, and Advertising Router).
241/// Multiple instances of the link state advertisement may exist in the
242/// routing domain at the same time. It is then necessary to determine
243/// which instance is more recent. This is accomplished by examining
244/// the LS age, LS sequence number and LS checksum fields that are also
245/// contained in the link state advertisement header.
246#[derive(Debug, NomBE)]
247pub struct OspfLinkStateAdvertisementHeader {
248 pub ls_age: u16,
249 pub options: u8,
250 pub link_state_type: OspfLinkStateType,
251 pub link_state_id: u32,
252 pub advertising_router: u32,
253 pub ls_seq_number: u32,
254 pub ls_checksum: u16,
255 pub length: u16,
256}
257
258impl OspfLinkStateAdvertisementHeader {
259 pub fn link_state_id(&self) -> Ipv4Addr {
260 Ipv4Addr::from(self.link_state_id)
261 }
262
263 pub fn advertising_router(&self) -> Ipv4Addr {
264 Ipv4Addr::from(self.advertising_router)
265 }
266}
267
268/// Link state advertisements
269#[derive(Debug)]
270pub enum OspfLinkStateAdvertisement {
271 RouterLinks(OspfRouterLinksAdvertisement),
272 NetworkLinks(OspfNetworkLinksAdvertisement),
273 SummaryLinkIpNetwork(OspfSummaryLinkAdvertisement),
274 SummaryLinkAsbr(OspfSummaryLinkAdvertisement),
275 ASExternalLink(OspfASExternalLinkAdvertisement),
276 NSSAASExternal(OspfNSSAExternalLinkAdvertisement),
277 OpaqueLinkLocalScope(OspfOpaqueLinkAdvertisement),
278 OpaqueAreaLocalScope(OspfOpaqueLinkAdvertisement),
279 OpaqueASWideScope(OspfOpaqueLinkAdvertisement),
280}
281
282/// Router links advertisements
283///
284/// Router links advertisements are the Type 1 link state
285/// advertisements. Each router in an area originates a router links
286/// advertisement. The advertisement describes the state and cost of
287/// the router's links (i.e., interfaces) to the area. All of the
288/// router's links to the area must be described in a single router
289/// links advertisement. For details concerning the construction of
290/// router links advertisements, see Section 12.4.1.
291#[derive(Debug, NomBE)]
292pub struct OspfRouterLinksAdvertisement {
293 #[nom(Verify = "header.link_state_type == OspfLinkStateType::RouterLinks")]
294 pub header: OspfLinkStateAdvertisementHeader,
295 pub flags: u16,
296 pub num_links: u16,
297 #[nom(Count = "num_links")]
298 pub links: Vec<OspfRouterLink>,
299}
300
301#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, NomBE)]
302pub struct OspfRouterLinkType(pub u8);
303
304newtype_enum! {
305impl display OspfRouterLinkType {
306 PointToPoint = 1,
307 Transit = 2,
308 Stub = 3,
309 Virtual = 4,
310}
311}
312
313/// OSPF router link (i.e., interface)
314#[derive(Debug, NomBE)]
315pub struct OspfRouterLink {
316 pub link_id: u32,
317 pub link_data: u32,
318 pub link_type: OspfRouterLinkType,
319 pub num_tos: u8,
320 pub tos_0_metric: u16,
321 #[nom(Count = "num_tos")]
322 pub tos_list: Vec<OspfRouterTOS>,
323}
324
325impl OspfRouterLink {
326 pub fn link_id(&self) -> Ipv4Addr {
327 Ipv4Addr::from(self.link_id)
328 }
329
330 pub fn link_data(&self) -> Ipv4Addr {
331 Ipv4Addr::from(self.link_data)
332 }
333}
334
335/// OSPF Router Type Of Service (TOS)
336#[derive(Debug, NomBE)]
337pub struct OspfRouterTOS {
338 pub tos: u8,
339 pub reserved: u8,
340 pub metric: u16,
341}
342
343/// Network links advertisements
344///
345/// Network links advertisements are the Type 2 link state
346/// advertisements. A network links advertisement is originated for
347/// each transit network in the area. A transit network is a multi-
348/// access network that has more than one attached router. The network
349/// links advertisement is originated by the network's Designated
350/// Router. The advertisement describes all routers attached to the
351/// network, including the Designated Router itself. The
352/// advertisement's Link State ID field lists the IP interface address
353/// of the Designated Router.
354///
355/// The distance from the network to all attached routers is zero, for
356/// all Types of Service. This is why the TOS and metric fields need
357/// not be specified in the network links advertisement. For details
358/// concerning the construction of network links advertisements, see
359/// Section 12.4.2.
360#[derive(Debug, NomBE)]
361pub struct OspfNetworkLinksAdvertisement {
362 #[nom(Verify = "header.link_state_type == OspfLinkStateType::NetworkLinks")]
363 pub header: OspfLinkStateAdvertisementHeader,
364 pub network_mask: u32,
365 // limit parsing to (length-xxx) bytes
366 #[nom(Parse = "parse_ospf_vec_u32(header.length, 24)")]
367 pub attached_routers: Vec<u32>,
368}
369
370impl OspfNetworkLinksAdvertisement {
371 pub fn network_mask(&self) -> Ipv4Addr {
372 Ipv4Addr::from(self.network_mask)
373 }
374
375 pub fn iter_attached_routers(&self) -> impl Iterator<Item = Ipv4Addr> + '_ {
376 self.attached_routers.iter().map(|&u| Ipv4Addr::from(u))
377 }
378}
379
380/// Summary link advertisements
381///
382/// Summary link advertisements are the Type 3 and 4 link state
383/// advertisements. These advertisements are originated by area border
384/// routers. A separate summary link advertisement is made for each
385/// destination (known to the router) which belongs to the AS, yet is
386/// outside the area. For details concerning the construction of
387/// summary link advertisements, see Section 12.4.3.
388///
389/// Type 3 link state advertisements are used when the destination is an
390/// IP network. In this case the advertisement's Link State ID field is
391/// an IP network number (if necessary, the Link State ID can also have
392/// one or more of the network's "host" bits set; see Appendix F for
393/// details). When the destination is an AS boundary router, a Type 4
394/// advertisement is used, and the Link State ID field is the AS
395/// boundary router's OSPF Router ID. (To see why it is necessary to
396/// advertise the location of each ASBR, consult Section 16.4.) Other
397/// than the difference in the Link State ID field, the format of Type 3
398/// and 4 link state advertisements is identical.
399#[derive(Debug, NomBE)]
400pub struct OspfSummaryLinkAdvertisement {
401 #[nom(
402 Verify = "header.link_state_type == OspfLinkStateType::SummaryLinkIpNetwork ||
403 header.link_state_type == OspfLinkStateType::SummaryLinkAsbr"
404 )]
405 pub header: OspfLinkStateAdvertisementHeader,
406 pub network_mask: u32,
407 pub tos: u8,
408 #[nom(Parse = "be_u24")]
409 pub metric: u32,
410 // limit parsing to (length-xxx) bytes
411 #[nom(Parse = "parse_ospf_tos_routes(header.length)")]
412 pub tos_routes: Vec<OspfTosRoute>,
413}
414
415impl OspfSummaryLinkAdvertisement {
416 pub fn network_mask(&self) -> Ipv4Addr {
417 Ipv4Addr::from(self.network_mask)
418 }
419}
420
421#[derive(Debug, NomBE)]
422pub struct OspfTosRoute {
423 pub tos: u8,
424 #[nom(Parse = "be_u24")]
425 pub metric: u32,
426}
427
428/// AS external link advertisements
429///
430/// AS external link advertisements are the Type 5 link state
431/// advertisements. These advertisements are originated by AS boundary
432/// routers. A separate advertisement is made for each destination
433/// (known to the router) which is external to the AS. For details
434/// concerning the construction of AS external link advertisements, see
435/// Section 12.4.3.
436///
437/// AS external link advertisements usually describe a particular
438/// external destination. For these advertisements the Link State ID
439/// field specifies an IP network number (if necessary, the Link State
440/// ID can also have one or more of the network's "host" bits set; see
441/// Appendix F for details). AS external link advertisements are also
442/// used to describe a default route. Default routes are used when no
443/// specific route exists to the destination. When describing a default
444/// route, the Link State ID is always set to DefaultDestination
445/// (0.0.0.0) and the Network Mask is set to 0.0.0.0.
446#[derive(Debug, NomBE)]
447pub struct OspfASExternalLinkAdvertisement {
448 #[nom(Verify = "header.link_state_type == OspfLinkStateType::ASExternalLink")]
449 pub header: OspfLinkStateAdvertisementHeader,
450 pub network_mask: u32,
451 pub external_and_reserved: u8,
452 #[nom(Parse = "be_u24")]
453 pub metric: u32,
454 pub forwarding_address: u32,
455 pub external_route_tag: u32,
456 // limit parsing to (length-xxx) bytes
457 #[nom(Parse = "parse_ospf_external_tos_routes(header.length)")]
458 pub tos_list: Vec<OspfExternalTosRoute>,
459}
460
461impl OspfASExternalLinkAdvertisement {
462 pub fn forwarding_address(&self) -> Ipv4Addr {
463 Ipv4Addr::from(self.forwarding_address)
464 }
465 pub fn network_mask(&self) -> Ipv4Addr {
466 Ipv4Addr::from(self.network_mask)
467 }
468}
469
470#[derive(Debug, NomBE)]
471pub struct OspfExternalTosRoute {
472 pub tos: u8,
473 #[nom(Parse = "be_u24")]
474 pub metric: u32,
475 pub forwarding_address: u32,
476 pub external_route_tag: u32,
477}
478
479impl OspfExternalTosRoute {
480 pub fn forwarding_address(&self) -> Ipv4Addr {
481 Ipv4Addr::from(self.forwarding_address)
482 }
483}
484
485/// NSSA AS-External LSA (type 7, rfc1587, rfc3101)
486#[derive(Debug, NomBE)]
487pub struct OspfNSSAExternalLinkAdvertisement {
488 #[nom(Verify = "header.link_state_type == OspfLinkStateType::NSSAASExternal")]
489 pub header: OspfLinkStateAdvertisementHeader,
490 pub network_mask: u32,
491 pub external_and_tos: u8,
492 #[nom(Parse = "be_u24")]
493 pub metric: u32,
494 pub forwarding_address: u32,
495 pub external_route_tag: u32,
496 // limit parsing to (length-xxx) bytes
497 #[nom(Parse = "parse_ospf_external_tos_routes(header.length)")]
498 pub tos_list: Vec<OspfExternalTosRoute>,
499}
500
501impl OspfNSSAExternalLinkAdvertisement {
502 pub fn forwarding_address(&self) -> Ipv4Addr {
503 Ipv4Addr::from(self.forwarding_address)
504 }
505 pub fn network_mask(&self) -> Ipv4Addr {
506 Ipv4Addr::from(self.network_mask)
507 }
508}
509
510/// The Opaque LSA (RFC5250)
511///
512/// Opaque LSAs are Type 9, 10, and 11 link state advertisements. These
513/// advertisements MAY be used directly by OSPF or indirectly by some
514/// application wishing to distribute information throughout the OSPF
515/// domain. The function of the Opaque LSA option is to provide for
516/// future OSPF extensibility.
517///
518/// Opaque LSAs contain some number of octets (of application-specific
519/// data) padded to 32-bit alignment. Like any other LSA, the Opaque LSA
520/// uses the link-state database distribution mechanism for flooding this
521/// information throughout the topology. However, the Opaque LSA has a
522/// flooding scope associated with it so that the scope of flooding may
523/// be link-local (type-9), area-local (type-10), or the entire OSPF
524/// routing domain (type-11). Section 3 of this document describes the
525/// flooding procedures for the Opaque LSA.
526#[derive(Debug, NomBE)]
527pub struct OspfOpaqueLinkAdvertisement {
528 #[nom(
529 Verify = "header.link_state_type == OspfLinkStateType::OpaqueLinkLocalScope ||
530 header.link_state_type == OspfLinkStateType::OpaqueAreaLocalScope ||
531 header.link_state_type == OspfLinkStateType::OpaqueASWideScope"
532 )]
533 pub header: OspfLinkStateAdvertisementHeader,
534 pub data: Vec<u8>,
535}
536
537impl OspfOpaqueLinkAdvertisement {
538 pub fn opaque_type(&self) -> u8 {
539 (self.header.link_state_id >> 24) as u8
540 }
541
542 pub fn opaque_id(&self) -> u32 {
543 self.header.link_state_id & 0xff_ffff
544 }
545}