1use std::net::Ipv4Addr;
7
8use crate::layer::field::FieldError;
9
10pub const MAX_OPTIONS_LEN: usize = 40;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[repr(u8)]
16pub enum Ipv4OptionClass {
17 Control = 0,
19 Reserved1 = 1,
21 DebuggingMeasurement = 2,
23 Reserved3 = 3,
25}
26
27impl Ipv4OptionClass {
28 #[inline]
29 pub fn from_type(opt_type: u8) -> Self {
30 match (opt_type >> 5) & 0x03 {
31 0 => Self::Control,
32 1 => Self::Reserved1,
33 2 => Self::DebuggingMeasurement,
34 3 => Self::Reserved3,
35 _ => unreachable!(),
36 }
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42#[repr(u8)]
43pub enum Ipv4OptionType {
44 EndOfList = 0,
46 Nop = 1,
48 Security = 130,
50 Lsrr = 131,
52 Timestamp = 68,
54 ExtendedSecurity = 133,
56 CommercialSecurity = 134,
58 RecordRoute = 7,
60 StreamId = 136,
62 Ssrr = 137,
64 ExperimentalMeasurement = 10,
66 MtuProbe = 11,
68 MtuReply = 12,
70 Traceroute = 82,
72 AddressExtension = 147,
74 RouterAlert = 148,
76 SelectiveDirectedBroadcast = 149,
78 Unknown(u8),
80}
81
82impl Ipv4OptionType {
83 pub fn from_byte(b: u8) -> Self {
85 match b {
86 0 => Self::EndOfList,
87 1 => Self::Nop,
88 7 => Self::RecordRoute,
89 10 => Self::ExperimentalMeasurement,
90 11 => Self::MtuProbe,
91 12 => Self::MtuReply,
92 68 => Self::Timestamp,
93 82 => Self::Traceroute,
94 130 => Self::Security,
95 131 => Self::Lsrr,
96 133 => Self::ExtendedSecurity,
97 134 => Self::CommercialSecurity,
98 136 => Self::StreamId,
99 137 => Self::Ssrr,
100 147 => Self::AddressExtension,
101 148 => Self::RouterAlert,
102 149 => Self::SelectiveDirectedBroadcast,
103 x => Self::Unknown(x),
104 }
105 }
106
107 pub fn to_byte(self) -> u8 {
109 match self {
110 Self::EndOfList => 0,
111 Self::Nop => 1,
112 Self::RecordRoute => 7,
113 Self::ExperimentalMeasurement => 10,
114 Self::MtuProbe => 11,
115 Self::MtuReply => 12,
116 Self::Timestamp => 68,
117 Self::Traceroute => 82,
118 Self::Security => 130,
119 Self::Lsrr => 131,
120 Self::ExtendedSecurity => 133,
121 Self::CommercialSecurity => 134,
122 Self::StreamId => 136,
123 Self::Ssrr => 137,
124 Self::AddressExtension => 147,
125 Self::RouterAlert => 148,
126 Self::SelectiveDirectedBroadcast => 149,
127 Self::Unknown(x) => x,
128 }
129 }
130
131 pub fn name(&self) -> &'static str {
133 match self {
134 Self::EndOfList => "EOL",
135 Self::Nop => "NOP",
136 Self::Security => "Security",
137 Self::Lsrr => "LSRR",
138 Self::Timestamp => "Timestamp",
139 Self::ExtendedSecurity => "Extended Security",
140 Self::CommercialSecurity => "Commercial Security",
141 Self::RecordRoute => "Record Route",
142 Self::StreamId => "Stream ID",
143 Self::Ssrr => "SSRR",
144 Self::ExperimentalMeasurement => "Experimental Measurement",
145 Self::MtuProbe => "MTU Probe",
146 Self::MtuReply => "MTU Reply",
147 Self::Traceroute => "Traceroute",
148 Self::AddressExtension => "Address Extension",
149 Self::RouterAlert => "Router Alert",
150 Self::SelectiveDirectedBroadcast => "Selective Directed Broadcast",
151 Self::Unknown(_) => "Unknown",
152 }
153 }
154
155 #[inline]
157 pub fn is_copied(&self) -> bool {
158 (self.to_byte() & 0x80) != 0
159 }
160
161 #[inline]
163 pub fn is_single_byte(&self) -> bool {
164 matches!(self, Self::EndOfList | Self::Nop)
165 }
166}
167
168impl std::fmt::Display for Ipv4OptionType {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 match self {
171 Self::Unknown(x) => write!(f, "Unknown({})", x),
172 _ => write!(f, "{}", self.name()),
173 }
174 }
175}
176
177#[derive(Debug, Clone, PartialEq, Eq)]
179pub enum Ipv4Option {
180 EndOfList,
182
183 Nop,
185
186 RecordRoute { pointer: u8, route: Vec<Ipv4Addr> },
188
189 Lsrr { pointer: u8, route: Vec<Ipv4Addr> },
191
192 Ssrr { pointer: u8, route: Vec<Ipv4Addr> },
194
195 Timestamp {
197 pointer: u8,
198 overflow: u8,
199 flag: u8,
200 data: Vec<(Option<Ipv4Addr>, u32)>,
201 },
202
203 Security {
205 security: u16,
206 compartment: u16,
207 handling_restrictions: u16,
208 transmission_control_code: [u8; 3],
209 },
210
211 StreamId { id: u16 },
213
214 MtuProbe { mtu: u16 },
216
217 MtuReply { mtu: u16 },
219
220 Traceroute {
222 id: u16,
223 outbound_hops: u16,
224 return_hops: u16,
225 originator: Ipv4Addr,
226 },
227
228 RouterAlert { value: u16 },
230
231 AddressExtension {
233 src_ext: Ipv4Addr,
234 dst_ext: Ipv4Addr,
235 },
236
237 Unknown { option_type: u8, data: Vec<u8> },
239}
240
241impl Ipv4Option {
242 pub fn option_type(&self) -> Ipv4OptionType {
244 match self {
245 Self::EndOfList => Ipv4OptionType::EndOfList,
246 Self::Nop => Ipv4OptionType::Nop,
247 Self::RecordRoute { .. } => Ipv4OptionType::RecordRoute,
248 Self::Lsrr { .. } => Ipv4OptionType::Lsrr,
249 Self::Ssrr { .. } => Ipv4OptionType::Ssrr,
250 Self::Timestamp { .. } => Ipv4OptionType::Timestamp,
251 Self::Security { .. } => Ipv4OptionType::Security,
252 Self::StreamId { .. } => Ipv4OptionType::StreamId,
253 Self::MtuProbe { .. } => Ipv4OptionType::MtuProbe,
254 Self::MtuReply { .. } => Ipv4OptionType::MtuReply,
255 Self::Traceroute { .. } => Ipv4OptionType::Traceroute,
256 Self::RouterAlert { .. } => Ipv4OptionType::RouterAlert,
257 Self::AddressExtension { .. } => Ipv4OptionType::AddressExtension,
258 Self::Unknown { option_type, .. } => Ipv4OptionType::Unknown(*option_type),
259 }
260 }
261
262 pub fn len(&self) -> usize {
264 match self {
265 Self::EndOfList => 1,
266 Self::Nop => 1,
267 Self::RecordRoute { route, .. }
268 | Self::Lsrr { route, .. }
269 | Self::Ssrr { route, .. } => 3 + route.len() * 4,
270 Self::Timestamp { data, flag, .. } => 4 + data.len() * if *flag == 0 { 4 } else { 8 },
271 Self::Security { .. } => 11,
272 Self::StreamId { .. } => 4,
273 Self::MtuProbe { .. } | Self::MtuReply { .. } => 4,
274 Self::Traceroute { .. } => 12,
275 Self::RouterAlert { .. } => 4,
276 Self::AddressExtension { .. } => 10,
277 Self::Unknown { data, .. } => 2 + data.len(),
278 }
279 }
280
281 pub fn is_empty(&self) -> bool {
283 self.len() == 0
284 }
285
286 pub fn to_bytes(&self) -> Vec<u8> {
288 match self {
289 Self::EndOfList => vec![0],
290 Self::Nop => vec![1],
291
292 Self::RecordRoute { pointer, route } => {
293 let mut buf = vec![7, (3 + route.len() * 4) as u8, *pointer];
294 for ip in route {
295 buf.extend_from_slice(&ip.octets());
296 }
297 buf
298 }
299
300 Self::Lsrr { pointer, route } => {
301 let mut buf = vec![131, (3 + route.len() * 4) as u8, *pointer];
302 for ip in route {
303 buf.extend_from_slice(&ip.octets());
304 }
305 buf
306 }
307
308 Self::Ssrr { pointer, route } => {
309 let mut buf = vec![137, (3 + route.len() * 4) as u8, *pointer];
310 for ip in route {
311 buf.extend_from_slice(&ip.octets());
312 }
313 buf
314 }
315
316 Self::Timestamp {
317 pointer,
318 overflow,
319 flag,
320 data,
321 } => {
322 let entry_size = if *flag == 0 { 4 } else { 8 };
323 let mut buf = vec![
324 68,
325 (4 + data.len() * entry_size) as u8,
326 *pointer,
327 (*overflow << 4) | (*flag & 0x0F),
328 ];
329 for (ip, ts) in data {
330 if let Some(addr) = ip {
331 buf.extend_from_slice(&addr.octets());
332 }
333 buf.extend_from_slice(&ts.to_be_bytes());
334 }
335 buf
336 }
337
338 Self::Security {
339 security,
340 compartment,
341 handling_restrictions,
342 transmission_control_code,
343 } => {
344 let mut buf = vec![130, 11];
345 buf.extend_from_slice(&security.to_be_bytes());
346 buf.extend_from_slice(&compartment.to_be_bytes());
347 buf.extend_from_slice(&handling_restrictions.to_be_bytes());
348 buf.extend_from_slice(transmission_control_code);
349 buf
350 }
351
352 Self::StreamId { id } => {
353 let mut buf = vec![136, 4];
354 buf.extend_from_slice(&id.to_be_bytes());
355 buf
356 }
357
358 Self::MtuProbe { mtu } => {
359 let mut buf = vec![11, 4];
360 buf.extend_from_slice(&mtu.to_be_bytes());
361 buf
362 }
363
364 Self::MtuReply { mtu } => {
365 let mut buf = vec![12, 4];
366 buf.extend_from_slice(&mtu.to_be_bytes());
367 buf
368 }
369
370 Self::Traceroute {
371 id,
372 outbound_hops,
373 return_hops,
374 originator,
375 } => {
376 let mut buf = vec![82, 12];
377 buf.extend_from_slice(&id.to_be_bytes());
378 buf.extend_from_slice(&outbound_hops.to_be_bytes());
379 buf.extend_from_slice(&return_hops.to_be_bytes());
380 buf.extend_from_slice(&originator.octets());
381 buf
382 }
383
384 Self::RouterAlert { value } => {
385 let mut buf = vec![148, 4];
386 buf.extend_from_slice(&value.to_be_bytes());
387 buf
388 }
389
390 Self::AddressExtension { src_ext, dst_ext } => {
391 let mut buf = vec![147, 10];
392 buf.extend_from_slice(&src_ext.octets());
393 buf.extend_from_slice(&dst_ext.octets());
394 buf
395 }
396
397 Self::Unknown { option_type, data } => {
398 let mut buf = vec![*option_type, (2 + data.len()) as u8];
399 buf.extend_from_slice(data);
400 buf
401 }
402 }
403 }
404}
405
406#[derive(Debug, Clone, Default, PartialEq, Eq)]
408pub struct Ipv4Options {
409 pub options: Vec<Ipv4Option>,
410}
411
412impl Ipv4Options {
413 pub fn new() -> Self {
415 Self::default()
416 }
417
418 pub fn from_vec(options: Vec<Ipv4Option>) -> Self {
420 Self { options }
421 }
422
423 pub fn is_empty(&self) -> bool {
425 self.options.is_empty()
426 }
427
428 pub fn len(&self) -> usize {
430 self.options.len()
431 }
432
433 pub fn byte_len(&self) -> usize {
435 self.options.iter().map(|o| o.len()).sum()
436 }
437
438 pub fn padded_len(&self) -> usize {
440 let len = self.byte_len();
441 (len + 3) & !3
442 }
443
444 pub fn push(&mut self, option: Ipv4Option) {
446 self.options.push(option);
447 }
448
449 pub fn source_route(&self) -> Option<&Ipv4Option> {
451 self.options
452 .iter()
453 .find(|o| matches!(o, Ipv4Option::Lsrr { .. } | Ipv4Option::Ssrr { .. }))
454 }
455
456 pub fn final_destination(&self) -> Option<Ipv4Addr> {
458 self.source_route().and_then(|opt| match opt {
459 Ipv4Option::Lsrr { route, .. } | Ipv4Option::Ssrr { route, .. } => {
460 route.last().copied()
461 }
462 _ => None,
463 })
464 }
465
466 pub fn to_bytes(&self) -> Vec<u8> {
468 let mut buf = Vec::new();
469 for opt in &self.options {
470 buf.extend_from_slice(&opt.to_bytes());
471 }
472
473 let pad = (4 - (buf.len() % 4)) % 4;
475 buf.extend(std::iter::repeat(0u8).take(pad));
476
477 buf
478 }
479
480 pub fn copied_options(&self) -> Self {
482 Self {
483 options: self
484 .options
485 .iter()
486 .filter(|o| o.option_type().is_copied())
487 .cloned()
488 .collect(),
489 }
490 }
491}
492
493impl IntoIterator for Ipv4Options {
494 type Item = Ipv4Option;
495 type IntoIter = std::vec::IntoIter<Ipv4Option>;
496
497 fn into_iter(self) -> Self::IntoIter {
498 self.options.into_iter()
499 }
500}
501
502impl<'a> IntoIterator for &'a Ipv4Options {
503 type Item = &'a Ipv4Option;
504 type IntoIter = std::slice::Iter<'a, Ipv4Option>;
505
506 fn into_iter(self) -> Self::IntoIter {
507 self.options.iter()
508 }
509}
510
511pub fn parse_options(data: &[u8]) -> Result<Ipv4Options, FieldError> {
513 let mut options = Vec::new();
514 let mut offset = 0;
515
516 while offset < data.len() {
517 let opt_type = data[offset];
518
519 match opt_type {
520 0 => {
522 options.push(Ipv4Option::EndOfList);
523 break;
524 }
525
526 1 => {
528 options.push(Ipv4Option::Nop);
529 offset += 1;
530 }
531
532 _ => {
534 if offset + 1 >= data.len() {
535 return Err(FieldError::InvalidValue(
536 "option length field missing".to_string(),
537 ));
538 }
539
540 let length = data[offset + 1] as usize;
541 if length < 2 {
542 return Err(FieldError::InvalidValue(format!(
543 "option length {} is less than minimum (2)",
544 length
545 )));
546 }
547
548 if offset + length > data.len() {
549 return Err(FieldError::BufferTooShort {
550 offset,
551 need: length,
552 have: data.len() - offset,
553 });
554 }
555
556 let opt_data = &data[offset..offset + length];
557 let opt = parse_single_option(opt_type, opt_data)?;
558 options.push(opt);
559
560 offset += length;
561 }
562 }
563 }
564
565 Ok(Ipv4Options { options })
566}
567
568fn parse_single_option(opt_type: u8, data: &[u8]) -> Result<Ipv4Option, FieldError> {
570 let length = data[1] as usize;
571
572 match opt_type {
573 7 => {
575 if length < 3 {
576 return Err(FieldError::InvalidValue(
577 "Record Route option too short".to_string(),
578 ));
579 }
580 let pointer = data[2];
581 let route = parse_ip_list(&data[3..length]);
582 Ok(Ipv4Option::RecordRoute { pointer, route })
583 }
584
585 131 => {
587 if length < 3 {
588 return Err(FieldError::InvalidValue(
589 "LSRR option too short".to_string(),
590 ));
591 }
592 let pointer = data[2];
593 let route = parse_ip_list(&data[3..length]);
594 Ok(Ipv4Option::Lsrr { pointer, route })
595 }
596
597 137 => {
599 if length < 3 {
600 return Err(FieldError::InvalidValue(
601 "SSRR option too short".to_string(),
602 ));
603 }
604 let pointer = data[2];
605 let route = parse_ip_list(&data[3..length]);
606 Ok(Ipv4Option::Ssrr { pointer, route })
607 }
608
609 68 => {
611 if length < 4 {
612 return Err(FieldError::InvalidValue(
613 "Timestamp option too short".to_string(),
614 ));
615 }
616 let pointer = data[2];
617 let oflw_flag = data[3];
618 let overflow = oflw_flag >> 4;
619 let flag = oflw_flag & 0x0F;
620
621 let timestamps = parse_timestamps(&data[4..length], flag)?;
622 Ok(Ipv4Option::Timestamp {
623 pointer,
624 overflow,
625 flag,
626 data: timestamps,
627 })
628 }
629
630 130 => {
632 if length != 11 {
633 return Err(FieldError::InvalidValue(format!(
634 "Security option length {} != 11",
635 length
636 )));
637 }
638 let security = u16::from_be_bytes([data[2], data[3]]);
639 let compartment = u16::from_be_bytes([data[4], data[5]]);
640 let handling_restrictions = u16::from_be_bytes([data[6], data[7]]);
641 let mut tcc = [0u8; 3];
642 tcc.copy_from_slice(&data[8..11]);
643
644 Ok(Ipv4Option::Security {
645 security,
646 compartment,
647 handling_restrictions,
648 transmission_control_code: tcc,
649 })
650 }
651
652 136 => {
654 if length != 4 {
655 return Err(FieldError::InvalidValue(format!(
656 "Stream ID option length {} != 4",
657 length
658 )));
659 }
660 let id = u16::from_be_bytes([data[2], data[3]]);
661 Ok(Ipv4Option::StreamId { id })
662 }
663
664 11 => {
666 if length != 4 {
667 return Err(FieldError::InvalidValue(format!(
668 "MTU Probe option length {} != 4",
669 length
670 )));
671 }
672 let mtu = u16::from_be_bytes([data[2], data[3]]);
673 Ok(Ipv4Option::MtuProbe { mtu })
674 }
675
676 12 => {
678 if length != 4 {
679 return Err(FieldError::InvalidValue(format!(
680 "MTU Reply option length {} != 4",
681 length
682 )));
683 }
684 let mtu = u16::from_be_bytes([data[2], data[3]]);
685 Ok(Ipv4Option::MtuReply { mtu })
686 }
687
688 82 => {
690 if length != 12 {
691 return Err(FieldError::InvalidValue(format!(
692 "Traceroute option length {} != 12",
693 length
694 )));
695 }
696 let id = u16::from_be_bytes([data[2], data[3]]);
697 let outbound_hops = u16::from_be_bytes([data[4], data[5]]);
698 let return_hops = u16::from_be_bytes([data[6], data[7]]);
699 let originator = Ipv4Addr::new(data[8], data[9], data[10], data[11]);
700
701 Ok(Ipv4Option::Traceroute {
702 id,
703 outbound_hops,
704 return_hops,
705 originator,
706 })
707 }
708
709 148 => {
711 if length != 4 {
712 return Err(FieldError::InvalidValue(format!(
713 "Router Alert option length {} != 4",
714 length
715 )));
716 }
717 let value = u16::from_be_bytes([data[2], data[3]]);
718 Ok(Ipv4Option::RouterAlert { value })
719 }
720
721 147 => {
723 if length != 10 {
724 return Err(FieldError::InvalidValue(format!(
725 "Address Extension option length {} != 10",
726 length
727 )));
728 }
729 let src_ext = Ipv4Addr::new(data[2], data[3], data[4], data[5]);
730 let dst_ext = Ipv4Addr::new(data[6], data[7], data[8], data[9]);
731 Ok(Ipv4Option::AddressExtension { src_ext, dst_ext })
732 }
733
734 _ => Ok(Ipv4Option::Unknown {
736 option_type: opt_type,
737 data: data[2..length].to_vec(),
738 }),
739 }
740}
741
742fn parse_ip_list(data: &[u8]) -> Vec<Ipv4Addr> {
744 data.chunks_exact(4)
745 .map(|c| Ipv4Addr::new(c[0], c[1], c[2], c[3]))
746 .collect()
747}
748
749fn parse_timestamps(data: &[u8], flag: u8) -> Result<Vec<(Option<Ipv4Addr>, u32)>, FieldError> {
751 match flag {
752 0 => {
754 let timestamps: Vec<_> = data
755 .chunks_exact(4)
756 .map(|c| {
757 let ts = u32::from_be_bytes([c[0], c[1], c[2], c[3]]);
758 (None, ts)
759 })
760 .collect();
761 Ok(timestamps)
762 }
763
764 1 | 3 => {
766 if data.len() % 8 != 0 {
767 return Err(FieldError::InvalidValue(
768 "Timestamp data not aligned to 8 bytes".to_string(),
769 ));
770 }
771 let timestamps: Vec<_> = data
772 .chunks_exact(8)
773 .map(|c| {
774 let ip = Ipv4Addr::new(c[0], c[1], c[2], c[3]);
775 let ts = u32::from_be_bytes([c[4], c[5], c[6], c[7]]);
776 (Some(ip), ts)
777 })
778 .collect();
779 Ok(timestamps)
780 }
781
782 _ => Err(FieldError::InvalidValue(format!(
783 "Unknown timestamp flag: {}",
784 flag
785 ))),
786 }
787}
788
789#[derive(Debug, Clone, Default)]
791pub struct Ipv4OptionsBuilder {
792 options: Vec<Ipv4Option>,
793}
794
795impl Ipv4OptionsBuilder {
796 pub fn new() -> Self {
798 Self::default()
799 }
800
801 pub fn eol(mut self) -> Self {
803 self.options.push(Ipv4Option::EndOfList);
804 self
805 }
806
807 pub fn nop(mut self) -> Self {
809 self.options.push(Ipv4Option::Nop);
810 self
811 }
812
813 pub fn record_route(mut self, route: Vec<Ipv4Addr>) -> Self {
815 self.options.push(Ipv4Option::RecordRoute {
816 pointer: 4, route,
818 });
819 self
820 }
821
822 pub fn lsrr(mut self, route: Vec<Ipv4Addr>) -> Self {
824 self.options.push(Ipv4Option::Lsrr { pointer: 4, route });
825 self
826 }
827
828 pub fn ssrr(mut self, route: Vec<Ipv4Addr>) -> Self {
830 self.options.push(Ipv4Option::Ssrr { pointer: 4, route });
831 self
832 }
833
834 pub fn timestamp(mut self) -> Self {
836 self.options.push(Ipv4Option::Timestamp {
837 pointer: 5,
838 overflow: 0,
839 flag: 0,
840 data: vec![],
841 });
842 self
843 }
844
845 pub fn timestamp_with_addresses(mut self, prespecified: bool) -> Self {
847 self.options.push(Ipv4Option::Timestamp {
848 pointer: 9,
849 overflow: 0,
850 flag: if prespecified { 3 } else { 1 },
851 data: vec![],
852 });
853 self
854 }
855
856 pub fn router_alert(mut self, value: u16) -> Self {
858 self.options.push(Ipv4Option::RouterAlert { value });
859 self
860 }
861
862 pub fn option(mut self, option: Ipv4Option) -> Self {
864 self.options.push(option);
865 self
866 }
867
868 pub fn build(self) -> Ipv4Options {
870 Ipv4Options {
871 options: self.options,
872 }
873 }
874}
875
876#[cfg(test)]
877mod tests {
878 use super::*;
879
880 #[test]
881 fn test_parse_nop_eol() {
882 let data = [1, 1, 1, 0]; let opts = parse_options(&data).unwrap();
884
885 assert_eq!(opts.len(), 4);
886 assert!(matches!(opts.options[0], Ipv4Option::Nop));
887 assert!(matches!(opts.options[1], Ipv4Option::Nop));
888 assert!(matches!(opts.options[2], Ipv4Option::Nop));
889 assert!(matches!(opts.options[3], Ipv4Option::EndOfList));
890 }
891
892 #[test]
893 fn test_parse_record_route() {
894 let data = [
895 7, 11, 4, 192, 168, 1, 1, 192, 168, 1, 2, ];
901
902 let opts = parse_options(&data).unwrap();
903 assert_eq!(opts.len(), 1);
904
905 if let Ipv4Option::RecordRoute { pointer, route } = &opts.options[0] {
906 assert_eq!(*pointer, 4);
907 assert_eq!(route.len(), 2);
908 assert_eq!(route[0], Ipv4Addr::new(192, 168, 1, 1));
909 assert_eq!(route[1], Ipv4Addr::new(192, 168, 1, 2));
910 } else {
911 panic!("Expected RecordRoute option");
912 }
913 }
914
915 #[test]
916 fn test_parse_timestamp() {
917 let data = [
918 68, 12, 5, 0x01, 192, 168, 1, 1, 0x00, 0x00, 0x10, 0x00, ];
925
926 let opts = parse_options(&data).unwrap();
927 assert_eq!(opts.len(), 1);
928
929 if let Ipv4Option::Timestamp {
930 pointer,
931 overflow,
932 flag,
933 data: ts_data,
934 } = &opts.options[0]
935 {
936 assert_eq!(*pointer, 5);
937 assert_eq!(*overflow, 0);
938 assert_eq!(*flag, 1);
939
940 assert_eq!(ts_data.len(), 1);
941 assert_eq!(ts_data[0].0, Some(Ipv4Addr::new(192, 168, 1, 1)));
942 assert_eq!(ts_data[0].1, 0x1000);
943 }
944 }
945
946 #[test]
947 fn test_parse_router_alert() {
948 let data = [
949 148, 4, 0x00, 0x00, ];
954
955 let opts = parse_options(&data).unwrap();
956 assert_eq!(opts.len(), 1);
957
958 if let Ipv4Option::RouterAlert { value } = opts.options[0] {
959 assert_eq!(value, 0);
960 } else {
961 panic!("Expected RouterAlert option");
962 }
963 }
964
965 #[test]
966 fn test_serialize_options() {
967 let opts = Ipv4OptionsBuilder::new()
968 .nop()
969 .router_alert(0)
970 .nop()
971 .build();
972
973 let bytes = opts.to_bytes();
974
975 assert_eq!(bytes.len() % 4, 0);
977
978 let parsed = parse_options(&bytes).unwrap();
980 assert!(matches!(parsed.options[0], Ipv4Option::Nop));
981 assert!(matches!(
982 parsed.options[1],
983 Ipv4Option::RouterAlert { value: 0 }
984 ));
985 }
986
987 #[test]
988 fn test_option_type_properties() {
989 assert!(Ipv4OptionType::Lsrr.is_copied());
991 assert!(!Ipv4OptionType::RecordRoute.is_copied());
993 assert!(Ipv4OptionType::Nop.is_single_byte());
995 assert!(!Ipv4OptionType::Timestamp.is_single_byte());
997 }
998
999 #[test]
1000 fn test_final_destination() {
1001 let opts = Ipv4OptionsBuilder::new()
1002 .lsrr(vec![
1003 Ipv4Addr::new(10, 0, 0, 1),
1004 Ipv4Addr::new(10, 0, 0, 2),
1005 Ipv4Addr::new(10, 0, 0, 3),
1006 ])
1007 .build();
1008
1009 assert_eq!(opts.final_destination(), Some(Ipv4Addr::new(10, 0, 0, 3)));
1010 }
1011
1012 #[test]
1013 fn test_copied_options() {
1014 let opts = Ipv4OptionsBuilder::new()
1015 .record_route(vec![]) .lsrr(vec![Ipv4Addr::new(10, 0, 0, 1)]) .nop() .build();
1019
1020 let copied = opts.copied_options();
1021 assert_eq!(copied.len(), 1);
1022 assert!(matches!(copied.options[0], Ipv4Option::Lsrr { .. }));
1023 }
1024}