1use compact_str::CompactString;
10use smallvec::SmallVec;
11
12use super::{FieldValue, ParseContext, ParseResult, Protocol};
13use crate::schema::{DataKind, FieldDescriptor};
14
15pub const IP_PROTOCOL_OSPF: u8 = 89;
17
18pub mod packet_type {
20 pub const HELLO: u8 = 1;
21 pub const DATABASE_DESCRIPTION: u8 = 2;
22 pub const LINK_STATE_REQUEST: u8 = 3;
23 pub const LINK_STATE_UPDATE: u8 = 4;
24 pub const LINK_STATE_ACK: u8 = 5;
25}
26
27pub mod lsa_type {
29 pub const ROUTER: u8 = 1;
31 pub const NETWORK: u8 = 2;
33 pub const SUMMARY_NETWORK: u8 = 3;
35 pub const SUMMARY_ASBR: u8 = 4;
37 pub const AS_EXTERNAL: u8 = 5;
39}
40
41fn lsa_type_name(ls_type: u8) -> &'static str {
43 match ls_type {
44 lsa_type::ROUTER => "Router-LSA",
45 lsa_type::NETWORK => "Network-LSA",
46 lsa_type::SUMMARY_NETWORK => "Summary-LSA-Network",
47 lsa_type::SUMMARY_ASBR => "Summary-LSA-ASBR",
48 lsa_type::AS_EXTERNAL => "AS-External-LSA",
49 _ => "Unknown",
50 }
51}
52
53fn packet_type_name(pkt_type: u8) -> &'static str {
55 match pkt_type {
56 packet_type::HELLO => "Hello",
57 packet_type::DATABASE_DESCRIPTION => "Database Description",
58 packet_type::LINK_STATE_REQUEST => "Link State Request",
59 packet_type::LINK_STATE_UPDATE => "Link State Update",
60 packet_type::LINK_STATE_ACK => "Link State Acknowledgment",
61 _ => "Unknown",
62 }
63}
64
65#[derive(Debug, Clone, Copy)]
67pub struct OspfProtocol;
68
69impl Protocol for OspfProtocol {
70 fn name(&self) -> &'static str {
71 "ospf"
72 }
73
74 fn display_name(&self) -> &'static str {
75 "OSPF"
76 }
77
78 fn can_parse(&self, context: &ParseContext) -> Option<u32> {
79 match context.hint("ip_protocol") {
81 Some(proto) if proto == IP_PROTOCOL_OSPF as u64 => Some(100),
82 _ => None,
83 }
84 }
85
86 fn parse<'a>(&self, data: &'a [u8], _context: &ParseContext) -> ParseResult<'a> {
87 if data.len() < 24 {
89 return ParseResult::error("OSPF header too short".to_string(), data);
90 }
91
92 let mut fields = SmallVec::new();
93
94 let version = data[0];
96 fields.push(("version", FieldValue::UInt8(version)));
97
98 let msg_type = data[1];
100 fields.push(("message_type", FieldValue::UInt8(msg_type)));
101 fields.push((
102 "message_type_name",
103 FieldValue::Str(packet_type_name(msg_type)),
104 ));
105
106 let length = u16::from_be_bytes([data[2], data[3]]);
108 fields.push(("length", FieldValue::UInt16(length)));
109
110 let router_id = format!("{}.{}.{}.{}", data[4], data[5], data[6], data[7]);
112 fields.push((
113 "router_id",
114 FieldValue::OwnedString(CompactString::new(router_id)),
115 ));
116
117 let area_id = format!("{}.{}.{}.{}", data[8], data[9], data[10], data[11]);
119 fields.push((
120 "area_id",
121 FieldValue::OwnedString(CompactString::new(area_id)),
122 ));
123
124 let checksum = u16::from_be_bytes([data[12], data[13]]);
126 fields.push(("checksum", FieldValue::UInt16(checksum)));
127
128 let auth_type = u16::from_be_bytes([data[14], data[15]]);
130 fields.push(("auth_type", FieldValue::UInt16(auth_type)));
131
132 let packet_data = if length as usize > 24 && data.len() >= length as usize {
139 &data[24..length as usize]
140 } else if data.len() > 24 {
141 &data[24..]
142 } else {
143 &[]
144 };
145
146 match (msg_type, version) {
147 (packet_type::HELLO, 2) => {
148 self.parse_hello_v2(packet_data, &mut fields);
149 }
150 (packet_type::DATABASE_DESCRIPTION, 2) => {
151 self.parse_db_description_v2(packet_data, &mut fields);
152 }
153 (packet_type::LINK_STATE_UPDATE, 2) => {
154 self.parse_ls_update_v2(packet_data, &mut fields);
155 }
156 (packet_type::LINK_STATE_ACK, 2) => {
157 self.parse_ls_ack_v2(packet_data, &mut fields);
158 }
159 _ => {
160 }
162 }
163
164 let consumed = std::cmp::min(length as usize, data.len());
166 ParseResult::success(fields, &data[consumed..], SmallVec::new())
167 }
168
169 fn schema_fields(&self) -> Vec<FieldDescriptor> {
170 vec![
171 FieldDescriptor::new("ospf.version", DataKind::UInt8).set_nullable(true),
173 FieldDescriptor::new("ospf.message_type", DataKind::UInt8).set_nullable(true),
174 FieldDescriptor::new("ospf.message_type_name", DataKind::String).set_nullable(true),
175 FieldDescriptor::new("ospf.length", DataKind::UInt16).set_nullable(true),
176 FieldDescriptor::new("ospf.router_id", DataKind::String).set_nullable(true),
177 FieldDescriptor::new("ospf.area_id", DataKind::String).set_nullable(true),
178 FieldDescriptor::new("ospf.auth_type", DataKind::UInt16).set_nullable(true),
179 FieldDescriptor::new("ospf.hello_interval", DataKind::UInt16).set_nullable(true),
181 FieldDescriptor::new("ospf.dead_interval", DataKind::UInt32).set_nullable(true),
182 FieldDescriptor::new("ospf.designated_router", DataKind::String).set_nullable(true),
183 FieldDescriptor::new("ospf.backup_dr", DataKind::String).set_nullable(true),
184 FieldDescriptor::new("ospf.neighbor_count", DataKind::UInt16).set_nullable(true),
185 FieldDescriptor::new("ospf.dd_interface_mtu", DataKind::UInt16).set_nullable(true),
187 FieldDescriptor::new("ospf.dd_options", DataKind::UInt8).set_nullable(true),
188 FieldDescriptor::new("ospf.dd_flags", DataKind::UInt8).set_nullable(true),
189 FieldDescriptor::new("ospf.dd_sequence", DataKind::UInt32).set_nullable(true),
190 FieldDescriptor::new("ospf.dd_lsa_count", DataKind::UInt16).set_nullable(true),
191 FieldDescriptor::new("ospf.lsu_lsa_count", DataKind::UInt32).set_nullable(true),
193 FieldDescriptor::new("ospf.lsa_age", DataKind::UInt16).set_nullable(true),
195 FieldDescriptor::new("ospf.lsa_type", DataKind::UInt8).set_nullable(true),
196 FieldDescriptor::new("ospf.lsa_type_name", DataKind::String).set_nullable(true),
197 FieldDescriptor::new("ospf.lsa_id", DataKind::String).set_nullable(true),
198 FieldDescriptor::new("ospf.lsa_advertising_router", DataKind::String)
199 .set_nullable(true),
200 FieldDescriptor::new("ospf.lsa_sequence", DataKind::UInt32).set_nullable(true),
201 FieldDescriptor::new("ospf.lsa_ack_count", DataKind::UInt16).set_nullable(true),
203 ]
204 }
205
206 fn child_protocols(&self) -> &[&'static str] {
207 &[]
208 }
209
210 fn dependencies(&self) -> &'static [&'static str] {
211 &["ipv4"] }
213}
214
215impl OspfProtocol {
216 fn parse_hello_v2(&self, data: &[u8], fields: &mut SmallVec<[(&'static str, FieldValue); 16]>) {
218 if data.len() < 20 {
219 return;
220 }
221
222 let hello_interval = u16::from_be_bytes([data[4], data[5]]);
226 fields.push(("hello_interval", FieldValue::UInt16(hello_interval)));
227
228 let dead_interval = u32::from_be_bytes([data[8], data[9], data[10], data[11]]);
232 fields.push(("dead_interval", FieldValue::UInt32(dead_interval)));
233
234 let dr = format!("{}.{}.{}.{}", data[12], data[13], data[14], data[15]);
236 fields.push((
237 "designated_router",
238 FieldValue::OwnedString(CompactString::new(dr)),
239 ));
240
241 let bdr = format!("{}.{}.{}.{}", data[16], data[17], data[18], data[19]);
243 fields.push((
244 "backup_dr",
245 FieldValue::OwnedString(CompactString::new(bdr)),
246 ));
247
248 let neighbor_data = &data[20..];
250 let neighbor_count = (neighbor_data.len() / 4) as u16;
251 if neighbor_count > 0 {
252 fields.push(("neighbor_count", FieldValue::UInt16(neighbor_count)));
253 }
254 }
255
256 fn parse_db_description_v2(
258 &self,
259 data: &[u8],
260 fields: &mut SmallVec<[(&'static str, FieldValue); 16]>,
261 ) {
262 if data.len() < 8 {
263 return;
264 }
265
266 let interface_mtu = u16::from_be_bytes([data[0], data[1]]);
268 fields.push(("dd_interface_mtu", FieldValue::UInt16(interface_mtu)));
269
270 let options = data[2];
272 fields.push(("dd_options", FieldValue::UInt8(options)));
273
274 let dd_flags = data[3];
276 fields.push(("dd_flags", FieldValue::UInt8(dd_flags)));
277
278 let dd_sequence = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
280 fields.push(("dd_sequence", FieldValue::UInt32(dd_sequence)));
281
282 let lsa_data = &data[8..];
284 let lsa_count = (lsa_data.len() / 20) as u16;
285 if lsa_count > 0 {
286 fields.push(("dd_lsa_count", FieldValue::UInt16(lsa_count)));
287 self.parse_lsa_header(&lsa_data[..20.min(lsa_data.len())], fields);
289 }
290 }
291
292 fn parse_ls_update_v2(
294 &self,
295 data: &[u8],
296 fields: &mut SmallVec<[(&'static str, FieldValue); 16]>,
297 ) {
298 if data.len() < 4 {
299 return;
300 }
301
302 let lsa_count = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
304 fields.push(("lsu_lsa_count", FieldValue::UInt32(lsa_count)));
305
306 if lsa_count > 0 && data.len() >= 24 {
308 self.parse_lsa_header(&data[4..24], fields);
310 }
311 }
312
313 fn parse_ls_ack_v2(
315 &self,
316 data: &[u8],
317 fields: &mut SmallVec<[(&'static str, FieldValue); 16]>,
318 ) {
319 let lsa_count = (data.len() / 20) as u16;
321 if lsa_count > 0 {
322 fields.push(("lsa_ack_count", FieldValue::UInt16(lsa_count)));
323 self.parse_lsa_header(&data[..20.min(data.len())], fields);
325 }
326 }
327
328 fn parse_lsa_header(
347 &self,
348 data: &[u8],
349 fields: &mut SmallVec<[(&'static str, FieldValue); 16]>,
350 ) {
351 if data.len() < 20 {
352 return;
353 }
354
355 let ls_age = u16::from_be_bytes([data[0], data[1]]);
357 fields.push(("lsa_age", FieldValue::UInt16(ls_age)));
358
359 let ls_type = data[3];
362 fields.push(("lsa_type", FieldValue::UInt8(ls_type)));
363 fields.push(("lsa_type_name", FieldValue::Str(lsa_type_name(ls_type))));
364
365 let ls_id = format!("{}.{}.{}.{}", data[4], data[5], data[6], data[7]);
367 fields.push(("lsa_id", FieldValue::OwnedString(CompactString::new(ls_id))));
368
369 let adv_router = format!("{}.{}.{}.{}", data[8], data[9], data[10], data[11]);
371 fields.push((
372 "lsa_advertising_router",
373 FieldValue::OwnedString(CompactString::new(adv_router)),
374 ));
375
376 let ls_sequence = u32::from_be_bytes([data[12], data[13], data[14], data[15]]);
378 fields.push(("lsa_sequence", FieldValue::UInt32(ls_sequence)));
379
380 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388
389 fn create_ospf_header(
391 version: u8,
392 msg_type: u8,
393 length: u16,
394 router_id: [u8; 4],
395 area_id: [u8; 4],
396 ) -> Vec<u8> {
397 let mut header = Vec::new();
398
399 header.push(version);
401
402 header.push(msg_type);
404
405 header.extend_from_slice(&length.to_be_bytes());
407
408 header.extend_from_slice(&router_id);
410
411 header.extend_from_slice(&area_id);
413
414 header.extend_from_slice(&[0x00, 0x00]);
416
417 header.extend_from_slice(&[0x00, 0x00]);
419
420 header.extend_from_slice(&[0x00; 8]);
422
423 header
424 }
425
426 fn create_ospf_hello(
428 router_id: [u8; 4],
429 area_id: [u8; 4],
430 hello_interval: u16,
431 dead_interval: u32,
432 dr: [u8; 4],
433 bdr: [u8; 4],
434 ) -> Vec<u8> {
435 let mut pkt = create_ospf_header(2, packet_type::HELLO, 44, router_id, area_id);
436
437 pkt.extend_from_slice(&[255, 255, 255, 0]);
439
440 pkt.extend_from_slice(&hello_interval.to_be_bytes());
442
443 pkt.push(0x02);
445
446 pkt.push(1);
448
449 pkt.extend_from_slice(&dead_interval.to_be_bytes());
451
452 pkt.extend_from_slice(&dr);
454
455 pkt.extend_from_slice(&bdr);
457
458 pkt
459 }
460
461 #[test]
463 fn test_can_parse_with_ip_protocol_89() {
464 let parser = OspfProtocol;
465
466 let ctx1 = ParseContext::new(1);
468 assert!(parser.can_parse(&ctx1).is_none());
469
470 let mut ctx2 = ParseContext::new(1);
472 ctx2.insert_hint("ip_protocol", 6); assert!(parser.can_parse(&ctx2).is_none());
474
475 let mut ctx3 = ParseContext::new(1);
477 ctx3.insert_hint("ip_protocol", 89);
478 assert!(parser.can_parse(&ctx3).is_some());
479 assert_eq!(parser.can_parse(&ctx3), Some(100));
480 }
481
482 #[test]
484 fn test_ospf_header_parsing() {
485 let parser = OspfProtocol;
486 let mut context = ParseContext::new(1);
487 context.insert_hint("ip_protocol", 89);
488
489 let pkt = create_ospf_header(2, packet_type::HELLO, 24, [192, 168, 1, 1], [0, 0, 0, 0]);
490
491 let result = parser.parse(&pkt, &context);
492
493 assert!(result.is_ok());
494 assert_eq!(result.get("version"), Some(&FieldValue::UInt8(2)));
495 assert_eq!(
496 result.get("message_type"),
497 Some(&FieldValue::UInt8(packet_type::HELLO))
498 );
499 assert_eq!(result.get("length"), Some(&FieldValue::UInt16(24)));
500 }
501
502 #[test]
504 fn test_version_detection() {
505 let parser = OspfProtocol;
506 let mut context = ParseContext::new(1);
507 context.insert_hint("ip_protocol", 89);
508
509 let pkt_v2 = create_ospf_header(2, packet_type::HELLO, 24, [1, 1, 1, 1], [0, 0, 0, 0]);
511 let result_v2 = parser.parse(&pkt_v2, &context);
512 assert!(result_v2.is_ok());
513 assert_eq!(result_v2.get("version"), Some(&FieldValue::UInt8(2)));
514
515 let pkt_v3 = create_ospf_header(3, packet_type::HELLO, 24, [1, 1, 1, 1], [0, 0, 0, 0]);
517 let result_v3 = parser.parse(&pkt_v3, &context);
518 assert!(result_v3.is_ok());
519 assert_eq!(result_v3.get("version"), Some(&FieldValue::UInt8(3)));
520 }
521
522 #[test]
524 fn test_hello_packet_parsing() {
525 let parser = OspfProtocol;
526 let mut context = ParseContext::new(1);
527 context.insert_hint("ip_protocol", 89);
528
529 let pkt = create_ospf_hello(
530 [192, 168, 1, 1],
531 [0, 0, 0, 0],
532 10, 40, [192, 168, 1, 1], [192, 168, 1, 2], );
537
538 let result = parser.parse(&pkt, &context);
539
540 assert!(result.is_ok());
541 assert_eq!(result.get("hello_interval"), Some(&FieldValue::UInt16(10)));
542 assert_eq!(result.get("dead_interval"), Some(&FieldValue::UInt32(40)));
543 assert_eq!(
544 result.get("designated_router"),
545 Some(&FieldValue::OwnedString(CompactString::new("192.168.1.1")))
546 );
547 assert_eq!(
548 result.get("backup_dr"),
549 Some(&FieldValue::OwnedString(CompactString::new("192.168.1.2")))
550 );
551 }
552
553 #[test]
555 fn test_router_id_extraction() {
556 let parser = OspfProtocol;
557 let mut context = ParseContext::new(1);
558 context.insert_hint("ip_protocol", 89);
559
560 let pkt = create_ospf_header(2, packet_type::HELLO, 24, [10, 0, 0, 1], [0, 0, 0, 0]);
561
562 let result = parser.parse(&pkt, &context);
563
564 assert!(result.is_ok());
565 assert_eq!(
566 result.get("router_id"),
567 Some(&FieldValue::OwnedString(CompactString::new("10.0.0.1")))
568 );
569 }
570
571 #[test]
573 fn test_area_id_extraction() {
574 let parser = OspfProtocol;
575 let mut context = ParseContext::new(1);
576 context.insert_hint("ip_protocol", 89);
577
578 let pkt1 = create_ospf_header(2, packet_type::HELLO, 24, [1, 1, 1, 1], [0, 0, 0, 0]);
580 let result1 = parser.parse(&pkt1, &context);
581 assert!(result1.is_ok());
582 assert_eq!(
583 result1.get("area_id"),
584 Some(&FieldValue::OwnedString(CompactString::new("0.0.0.0")))
585 );
586
587 let pkt2 = create_ospf_header(2, packet_type::HELLO, 24, [1, 1, 1, 1], [0, 0, 0, 1]);
589 let result2 = parser.parse(&pkt2, &context);
590 assert!(result2.is_ok());
591 assert_eq!(
592 result2.get("area_id"),
593 Some(&FieldValue::OwnedString(CompactString::new("0.0.0.1")))
594 );
595 }
596
597 #[test]
599 fn test_message_type_name_mapping() {
600 let parser = OspfProtocol;
601 let mut context = ParseContext::new(1);
602 context.insert_hint("ip_protocol", 89);
603
604 let test_types = [
605 (packet_type::HELLO, "Hello"),
606 (packet_type::DATABASE_DESCRIPTION, "Database Description"),
607 (packet_type::LINK_STATE_REQUEST, "Link State Request"),
608 (packet_type::LINK_STATE_UPDATE, "Link State Update"),
609 (packet_type::LINK_STATE_ACK, "Link State Acknowledgment"),
610 ];
611
612 for (pkt_type, name) in test_types {
613 let pkt = create_ospf_header(2, pkt_type, 24, [1, 1, 1, 1], [0, 0, 0, 0]);
614 let result = parser.parse(&pkt, &context);
615
616 assert!(result.is_ok());
617 assert_eq!(
618 result.get("message_type"),
619 Some(&FieldValue::UInt8(pkt_type))
620 );
621 assert_eq!(
622 result.get("message_type_name"),
623 Some(&FieldValue::Str(name))
624 );
625 }
626 }
627
628 #[test]
630 fn test_ospf_too_short() {
631 let parser = OspfProtocol;
632 let mut context = ParseContext::new(1);
633 context.insert_hint("ip_protocol", 89);
634
635 let short_pkt = [2u8, 1, 0, 24]; let result = parser.parse(&short_pkt, &context);
637
638 assert!(!result.is_ok());
639 assert!(result.error.is_some());
640 }
641
642 #[test]
644 fn test_auth_type() {
645 let parser = OspfProtocol;
646 let mut context = ParseContext::new(1);
647 context.insert_hint("ip_protocol", 89);
648
649 let mut pkt = create_ospf_header(2, packet_type::HELLO, 24, [1, 1, 1, 1], [0, 0, 0, 0]);
650 pkt[14] = 0;
652 pkt[15] = 2;
653
654 let result = parser.parse(&pkt, &context);
655
656 assert!(result.is_ok());
657 assert_eq!(result.get("auth_type"), Some(&FieldValue::UInt16(2)));
658 }
659
660 #[test]
662 fn test_ospf_schema_fields() {
663 let parser = OspfProtocol;
664 let fields = parser.schema_fields();
665
666 assert!(!fields.is_empty());
667 let field_names: Vec<&str> = fields.iter().map(|f| f.name).collect();
668 assert!(field_names.contains(&"ospf.version"));
669 assert!(field_names.contains(&"ospf.message_type"));
670 assert!(field_names.contains(&"ospf.message_type_name"));
671 assert!(field_names.contains(&"ospf.length"));
672 assert!(field_names.contains(&"ospf.router_id"));
673 assert!(field_names.contains(&"ospf.area_id"));
674 assert!(field_names.contains(&"ospf.auth_type"));
675 assert!(field_names.contains(&"ospf.hello_interval"));
676 assert!(field_names.contains(&"ospf.dead_interval"));
677 assert!(field_names.contains(&"ospf.designated_router"));
678 assert!(field_names.contains(&"ospf.backup_dr"));
679 }
680
681 #[test]
683 fn test_database_description_parsing() {
684 let parser = OspfProtocol;
685 let mut context = ParseContext::new(1);
686 context.insert_hint("ip_protocol", 89);
687
688 let mut pkt = create_ospf_header(
690 2,
691 packet_type::DATABASE_DESCRIPTION,
692 32,
693 [10, 0, 0, 1],
694 [0, 0, 0, 0],
695 );
696
697 pkt.extend_from_slice(&1500u16.to_be_bytes()); pkt.push(0x02); pkt.push(0x07); pkt.extend_from_slice(&0x12345678u32.to_be_bytes()); let result = parser.parse(&pkt, &context);
704
705 assert!(result.is_ok());
706 assert_eq!(
707 result.get("dd_interface_mtu"),
708 Some(&FieldValue::UInt16(1500))
709 );
710 assert_eq!(result.get("dd_options"), Some(&FieldValue::UInt8(0x02)));
711 assert_eq!(result.get("dd_flags"), Some(&FieldValue::UInt8(0x07)));
712 assert_eq!(
713 result.get("dd_sequence"),
714 Some(&FieldValue::UInt32(0x12345678))
715 );
716 }
717
718 #[test]
720 fn test_database_description_with_lsa() {
721 let parser = OspfProtocol;
722 let mut context = ParseContext::new(1);
723 context.insert_hint("ip_protocol", 89);
724
725 let mut pkt = create_ospf_header(
727 2,
728 packet_type::DATABASE_DESCRIPTION,
729 52, [10, 0, 0, 1],
731 [0, 0, 0, 0],
732 );
733
734 pkt.extend_from_slice(&1500u16.to_be_bytes()); pkt.push(0x02); pkt.push(0x01); pkt.extend_from_slice(&1u32.to_be_bytes()); pkt.extend_from_slice(&100u16.to_be_bytes()); pkt.push(0x00); pkt.push(lsa_type::ROUTER); pkt.extend_from_slice(&[10, 0, 0, 1]); pkt.extend_from_slice(&[10, 0, 0, 1]); pkt.extend_from_slice(&0x80000001u32.to_be_bytes()); pkt.extend_from_slice(&[0x00, 0x00]); pkt.extend_from_slice(&36u16.to_be_bytes()); let result = parser.parse(&pkt, &context);
751
752 assert!(result.is_ok());
753 assert_eq!(result.get("dd_lsa_count"), Some(&FieldValue::UInt16(1)));
754 assert_eq!(result.get("lsa_age"), Some(&FieldValue::UInt16(100)));
755 assert_eq!(
756 result.get("lsa_type"),
757 Some(&FieldValue::UInt8(lsa_type::ROUTER))
758 );
759 assert_eq!(
760 result.get("lsa_type_name"),
761 Some(&FieldValue::Str("Router-LSA"))
762 );
763 }
764
765 #[test]
767 fn test_ls_update_parsing() {
768 let parser = OspfProtocol;
769 let mut context = ParseContext::new(1);
770 context.insert_hint("ip_protocol", 89);
771
772 let mut pkt = create_ospf_header(
774 2,
775 packet_type::LINK_STATE_UPDATE,
776 48, [10, 0, 0, 1],
778 [0, 0, 0, 0],
779 );
780
781 pkt.extend_from_slice(&2u32.to_be_bytes()); pkt.extend_from_slice(&500u16.to_be_bytes()); pkt.push(0x00); pkt.push(lsa_type::NETWORK); pkt.extend_from_slice(&[192, 168, 1, 0]); pkt.extend_from_slice(&[192, 168, 1, 1]); pkt.extend_from_slice(&0x80000002u32.to_be_bytes()); pkt.extend_from_slice(&[0x00, 0x00]); pkt.extend_from_slice(&32u16.to_be_bytes()); let result = parser.parse(&pkt, &context);
795
796 assert!(result.is_ok());
797 assert_eq!(result.get("lsu_lsa_count"), Some(&FieldValue::UInt32(2)));
798 assert_eq!(result.get("lsa_age"), Some(&FieldValue::UInt16(500)));
799 assert_eq!(
800 result.get("lsa_type"),
801 Some(&FieldValue::UInt8(lsa_type::NETWORK))
802 );
803 assert_eq!(
804 result.get("lsa_type_name"),
805 Some(&FieldValue::Str("Network-LSA"))
806 );
807 assert_eq!(
808 result.get("lsa_id"),
809 Some(&FieldValue::OwnedString(CompactString::new("192.168.1.0")))
810 );
811 assert_eq!(
812 result.get("lsa_advertising_router"),
813 Some(&FieldValue::OwnedString(CompactString::new("192.168.1.1")))
814 );
815 }
816
817 #[test]
819 fn test_ls_ack_parsing() {
820 let parser = OspfProtocol;
821 let mut context = ParseContext::new(1);
822 context.insert_hint("ip_protocol", 89);
823
824 let mut pkt = create_ospf_header(
826 2,
827 packet_type::LINK_STATE_ACK,
828 64, [10, 0, 0, 1],
830 [0, 0, 0, 0],
831 );
832
833 pkt.extend_from_slice(&200u16.to_be_bytes()); pkt.push(0x00); pkt.push(lsa_type::AS_EXTERNAL); pkt.extend_from_slice(&[0, 0, 0, 0]); pkt.extend_from_slice(&[172, 16, 0, 1]); pkt.extend_from_slice(&0x80000010u32.to_be_bytes()); pkt.extend_from_slice(&[0x00, 0x00]); pkt.extend_from_slice(&36u16.to_be_bytes()); pkt.extend_from_slice(&300u16.to_be_bytes()); pkt.push(0x00); pkt.push(lsa_type::SUMMARY_NETWORK); pkt.extend_from_slice(&[10, 1, 0, 0]); pkt.extend_from_slice(&[10, 0, 0, 1]); pkt.extend_from_slice(&0x80000005u32.to_be_bytes()); pkt.extend_from_slice(&[0x00, 0x00]); pkt.extend_from_slice(&28u16.to_be_bytes()); let result = parser.parse(&pkt, &context);
854
855 assert!(result.is_ok());
856 assert_eq!(result.get("lsa_ack_count"), Some(&FieldValue::UInt16(2)));
857 assert_eq!(result.get("lsa_age"), Some(&FieldValue::UInt16(200)));
859 assert_eq!(
860 result.get("lsa_type"),
861 Some(&FieldValue::UInt8(lsa_type::AS_EXTERNAL))
862 );
863 assert_eq!(
864 result.get("lsa_type_name"),
865 Some(&FieldValue::Str("AS-External-LSA"))
866 );
867 }
868
869 #[test]
871 fn test_lsa_type_names() {
872 let parser = OspfProtocol;
873 let mut context = ParseContext::new(1);
874 context.insert_hint("ip_protocol", 89);
875
876 let test_cases = [
877 (lsa_type::ROUTER, "Router-LSA"),
878 (lsa_type::NETWORK, "Network-LSA"),
879 (lsa_type::SUMMARY_NETWORK, "Summary-LSA-Network"),
880 (lsa_type::SUMMARY_ASBR, "Summary-LSA-ASBR"),
881 (lsa_type::AS_EXTERNAL, "AS-External-LSA"),
882 ];
883
884 for (ls_type, expected_name) in test_cases {
885 let mut pkt = create_ospf_header(
886 2,
887 packet_type::LINK_STATE_UPDATE,
888 48,
889 [10, 0, 0, 1],
890 [0, 0, 0, 0],
891 );
892
893 pkt.extend_from_slice(&1u32.to_be_bytes()); pkt.extend_from_slice(&100u16.to_be_bytes()); pkt.push(0x00); pkt.push(ls_type); pkt.extend_from_slice(&[10, 0, 0, 0]); pkt.extend_from_slice(&[10, 0, 0, 1]); pkt.extend_from_slice(&0x80000001u32.to_be_bytes()); pkt.extend_from_slice(&[0x00, 0x00]); pkt.extend_from_slice(&20u16.to_be_bytes()); let result = parser.parse(&pkt, &context);
906 assert!(result.is_ok());
907 assert_eq!(result.get("lsa_type"), Some(&FieldValue::UInt8(ls_type)));
908 assert_eq!(
909 result.get("lsa_type_name"),
910 Some(&FieldValue::Str(expected_name))
911 );
912 }
913 }
914
915 #[test]
917 fn test_lsa_sequence_number() {
918 let parser = OspfProtocol;
919 let mut context = ParseContext::new(1);
920 context.insert_hint("ip_protocol", 89);
921
922 let mut pkt = create_ospf_header(
924 2,
925 packet_type::LINK_STATE_UPDATE,
926 48,
927 [10, 0, 0, 1],
928 [0, 0, 0, 0],
929 );
930
931 pkt.extend_from_slice(&1u32.to_be_bytes()); pkt.extend_from_slice(&100u16.to_be_bytes()); pkt.push(0x00); pkt.push(lsa_type::ROUTER); pkt.extend_from_slice(&[10, 0, 0, 1]); pkt.extend_from_slice(&[10, 0, 0, 1]); pkt.extend_from_slice(&0x80001234u32.to_be_bytes()); pkt.extend_from_slice(&[0x00, 0x00]); pkt.extend_from_slice(&20u16.to_be_bytes()); let result = parser.parse(&pkt, &context);
944
945 assert!(result.is_ok());
946 assert_eq!(
947 result.get("lsa_sequence"),
948 Some(&FieldValue::UInt32(0x80001234))
949 );
950 }
951
952 #[test]
954 fn test_hello_with_neighbors() {
955 let parser = OspfProtocol;
956 let mut context = ParseContext::new(1);
957 context.insert_hint("ip_protocol", 89);
958
959 let mut pkt = create_ospf_header(
962 2,
963 packet_type::HELLO,
964 56, [192, 168, 1, 1],
966 [0, 0, 0, 0],
967 );
968
969 pkt.extend_from_slice(&[255, 255, 255, 0]);
971 pkt.extend_from_slice(&10u16.to_be_bytes());
973 pkt.push(0x02);
975 pkt.push(1);
977 pkt.extend_from_slice(&40u32.to_be_bytes());
979 pkt.extend_from_slice(&[192, 168, 1, 1]);
981 pkt.extend_from_slice(&[192, 168, 1, 2]);
983
984 pkt.extend_from_slice(&[192, 168, 1, 2]); pkt.extend_from_slice(&[192, 168, 1, 3]); pkt.extend_from_slice(&[192, 168, 1, 4]); let result = parser.parse(&pkt, &context);
990
991 assert!(result.is_ok());
992 assert_eq!(result.get("neighbor_count"), Some(&FieldValue::UInt16(3)));
993 }
994
995 #[test]
997 fn test_malformed_length_less_than_header() {
998 let parser = OspfProtocol;
999 let mut context = ParseContext::new(1);
1000 context.insert_hint("ip_protocol", 89);
1001
1002 let crash_input: [u8; 32] = [
1005 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 112, 95, 112, 114, 111, 116, 111, 99, 111, 108,
1007 2, 7, 0, 0, 0, 1,
1008 ];
1009
1010 let result = parser.parse(&crash_input, &context);
1012 assert!(result.is_ok());
1013 assert_eq!(result.get("version"), Some(&FieldValue::UInt8(2)));
1014 assert_eq!(result.get("message_type"), Some(&FieldValue::UInt8(2)));
1015 assert_eq!(result.get("length"), Some(&FieldValue::UInt16(0)));
1016 }
1017
1018 #[test]
1020 fn test_lsa_age_field() {
1021 let parser = OspfProtocol;
1022 let mut context = ParseContext::new(1);
1023 context.insert_hint("ip_protocol", 89);
1024
1025 let mut pkt = create_ospf_header(
1027 2,
1028 packet_type::LINK_STATE_UPDATE,
1029 48,
1030 [10, 0, 0, 1],
1031 [0, 0, 0, 0],
1032 );
1033
1034 pkt.extend_from_slice(&1u32.to_be_bytes()); pkt.extend_from_slice(&1800u16.to_be_bytes()); pkt.push(0x00); pkt.push(lsa_type::ROUTER); pkt.extend_from_slice(&[10, 0, 0, 1]); pkt.extend_from_slice(&[10, 0, 0, 1]); pkt.extend_from_slice(&0x80000001u32.to_be_bytes()); pkt.extend_from_slice(&[0x00, 0x00]); pkt.extend_from_slice(&20u16.to_be_bytes()); let result = parser.parse(&pkt, &context);
1047
1048 assert!(result.is_ok());
1049 assert_eq!(result.get("lsa_age"), Some(&FieldValue::UInt16(1800)));
1050 }
1051}