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