1use crate::layer::field::FieldError;
23
24pub const MAX_OPTIONS_LEN: usize = 40;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29#[repr(u8)]
30pub enum TcpOptionKind {
31 Eol = 0,
33 Nop = 1,
35 Mss = 2,
37 WScale = 3,
39 SackOk = 4,
41 Sack = 5,
43 Timestamp = 8,
45 AltChkSum = 14,
47 AltChkSumOpt = 15,
49 Md5 = 19,
51 Mood = 25,
53 Uto = 28,
55 Ao = 29,
57 Tfo = 34,
59 Unknown(u8),
61}
62
63impl TcpOptionKind {
64 #[must_use]
66 pub fn from_byte(b: u8) -> Self {
67 match b {
68 0 => Self::Eol,
69 1 => Self::Nop,
70 2 => Self::Mss,
71 3 => Self::WScale,
72 4 => Self::SackOk,
73 5 => Self::Sack,
74 8 => Self::Timestamp,
75 14 => Self::AltChkSum,
76 15 => Self::AltChkSumOpt,
77 19 => Self::Md5,
78 25 => Self::Mood,
79 28 => Self::Uto,
80 29 => Self::Ao,
81 34 => Self::Tfo,
82 x => Self::Unknown(x),
83 }
84 }
85
86 #[must_use]
88 pub fn to_byte(self) -> u8 {
89 match self {
90 Self::Eol => 0,
91 Self::Nop => 1,
92 Self::Mss => 2,
93 Self::WScale => 3,
94 Self::SackOk => 4,
95 Self::Sack => 5,
96 Self::Timestamp => 8,
97 Self::AltChkSum => 14,
98 Self::AltChkSumOpt => 15,
99 Self::Md5 => 19,
100 Self::Mood => 25,
101 Self::Uto => 28,
102 Self::Ao => 29,
103 Self::Tfo => 34,
104 Self::Unknown(x) => x,
105 }
106 }
107
108 #[must_use]
110 pub fn name(&self) -> &'static str {
111 match self {
112 Self::Eol => "EOL",
113 Self::Nop => "NOP",
114 Self::Mss => "MSS",
115 Self::WScale => "WScale",
116 Self::SackOk => "SAckOK",
117 Self::Sack => "SAck",
118 Self::Timestamp => "Timestamp",
119 Self::AltChkSum => "AltChkSum",
120 Self::AltChkSumOpt => "AltChkSumOpt",
121 Self::Md5 => "MD5",
122 Self::Mood => "Mood",
123 Self::Uto => "UTO",
124 Self::Ao => "AO",
125 Self::Tfo => "TFO",
126 Self::Unknown(_) => "Unknown",
127 }
128 }
129
130 #[inline]
132 #[must_use]
133 pub fn is_single_byte(&self) -> bool {
134 matches!(self, Self::Eol | Self::Nop)
135 }
136
137 #[must_use]
140 pub fn expected_len(&self) -> Option<usize> {
141 match self {
142 Self::Eol | Self::Nop => Some(1),
143 Self::Mss => Some(4),
144 Self::WScale => Some(3),
145 Self::SackOk => Some(2),
146 Self::Timestamp => Some(10),
147 Self::AltChkSum => Some(4), Self::AltChkSumOpt => Some(2),
149 Self::Md5 => Some(18),
150 Self::Uto => Some(4),
151 Self::Tfo => Some(10), Self::Sack | Self::Ao | Self::Mood | Self::Unknown(_) => None,
153 }
154 }
155}
156
157impl std::fmt::Display for TcpOptionKind {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 match self {
160 Self::Unknown(x) => write!(f, "Unknown({x})"),
161 _ => write!(f, "{}", self.name()),
162 }
163 }
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub struct TcpSackBlock {
169 pub left: u32,
171 pub right: u32,
173}
174
175impl TcpSackBlock {
176 #[must_use]
178 pub fn new(left: u32, right: u32) -> Self {
179 Self { left, right }
180 }
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185pub struct TcpTimestamp {
186 pub ts_val: u32,
188 pub ts_ecr: u32,
190}
191
192impl TcpTimestamp {
193 #[must_use]
195 pub fn new(ts_val: u32, ts_ecr: u32) -> Self {
196 Self { ts_val, ts_ecr }
197 }
198}
199
200#[derive(Debug, Clone, PartialEq, Eq)]
202pub struct TcpAoValue {
203 pub key_id: u8,
205 pub rnext_key_id: u8,
207 pub mac: Vec<u8>,
209}
210
211impl TcpAoValue {
212 #[must_use]
214 pub fn new(key_id: u8, rnext_key_id: u8, mac: Vec<u8>) -> Self {
215 Self {
216 key_id,
217 rnext_key_id,
218 mac,
219 }
220 }
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
225pub enum TcpOption {
226 Eol,
228
229 Nop,
231
232 Mss(u16),
234
235 WScale(u8),
237
238 SackOk,
240
241 Sack(Vec<TcpSackBlock>),
243
244 Timestamp(TcpTimestamp),
246
247 AltChkSum { algorithm: u8, checksum: u16 },
249
250 AltChkSumOpt,
252
253 Md5([u8; 16]),
255
256 Mood(String),
258
259 Uto(u16),
261
262 Ao(TcpAoValue),
264
265 Tfo {
267 cookie: Option<Vec<u8>>,
269 },
270
271 Unknown { kind: u8, data: Vec<u8> },
273}
274
275impl TcpOption {
276 #[must_use]
278 pub fn kind(&self) -> TcpOptionKind {
279 match self {
280 Self::Eol => TcpOptionKind::Eol,
281 Self::Nop => TcpOptionKind::Nop,
282 Self::Mss(_) => TcpOptionKind::Mss,
283 Self::WScale(_) => TcpOptionKind::WScale,
284 Self::SackOk => TcpOptionKind::SackOk,
285 Self::Sack(_) => TcpOptionKind::Sack,
286 Self::Timestamp(_) => TcpOptionKind::Timestamp,
287 Self::AltChkSum { .. } => TcpOptionKind::AltChkSum,
288 Self::AltChkSumOpt => TcpOptionKind::AltChkSumOpt,
289 Self::Md5(_) => TcpOptionKind::Md5,
290 Self::Mood(_) => TcpOptionKind::Mood,
291 Self::Uto(_) => TcpOptionKind::Uto,
292 Self::Ao(_) => TcpOptionKind::Ao,
293 Self::Tfo { .. } => TcpOptionKind::Tfo,
294 Self::Unknown { kind, .. } => TcpOptionKind::Unknown(*kind),
295 }
296 }
297
298 #[must_use]
300 pub fn len(&self) -> usize {
301 match self {
302 Self::Eol => 1,
303 Self::Nop => 1,
304 Self::Mss(_) => 4,
305 Self::WScale(_) => 3,
306 Self::SackOk => 2,
307 Self::Sack(blocks) => 2 + blocks.len() * 8,
308 Self::Timestamp(_) => 10,
309 Self::AltChkSum { .. } => 4,
310 Self::AltChkSumOpt => 2,
311 Self::Md5(_) => 18,
312 Self::Mood(s) => 2 + s.len(),
313 Self::Uto(_) => 4,
314 Self::Ao(ao) => 4 + ao.mac.len(),
315 Self::Tfo { cookie: None } => 2,
316 Self::Tfo { cookie: Some(c) } => 2 + c.len(),
317 Self::Unknown { data, .. } => 2 + data.len(),
318 }
319 }
320
321 #[must_use]
323 pub fn is_empty(&self) -> bool {
324 self.len() == 0
325 }
326
327 #[must_use]
329 pub fn to_bytes(&self) -> Vec<u8> {
330 match self {
331 Self::Eol => vec![0],
332 Self::Nop => vec![1],
333
334 Self::Mss(mss) => {
335 let mut buf = vec![2, 4];
336 buf.extend_from_slice(&mss.to_be_bytes());
337 buf
338 },
339
340 Self::WScale(scale) => vec![3, 3, *scale],
341
342 Self::SackOk => vec![4, 2],
343
344 Self::Sack(blocks) => {
345 let mut buf = vec![5, (2 + blocks.len() * 8) as u8];
346 for block in blocks {
347 buf.extend_from_slice(&block.left.to_be_bytes());
348 buf.extend_from_slice(&block.right.to_be_bytes());
349 }
350 buf
351 },
352
353 Self::Timestamp(ts) => {
354 let mut buf = vec![8, 10];
355 buf.extend_from_slice(&ts.ts_val.to_be_bytes());
356 buf.extend_from_slice(&ts.ts_ecr.to_be_bytes());
357 buf
358 },
359
360 Self::AltChkSum {
361 algorithm,
362 checksum,
363 } => {
364 let mut buf = vec![14, 4, *algorithm];
365 buf.extend_from_slice(&checksum.to_be_bytes());
366 buf
367 },
368
369 Self::AltChkSumOpt => vec![15, 2],
370
371 Self::Md5(sig) => {
372 let mut buf = vec![19, 18];
373 buf.extend_from_slice(sig);
374 buf
375 },
376
377 Self::Mood(mood) => {
378 let mut buf = vec![25, (2 + mood.len()) as u8];
379 buf.extend_from_slice(mood.as_bytes());
380 buf
381 },
382
383 Self::Uto(timeout) => {
384 let mut buf = vec![28, 4];
385 buf.extend_from_slice(&timeout.to_be_bytes());
386 buf
387 },
388
389 Self::Ao(ao) => {
390 let len = 4 + ao.mac.len();
391 let mut buf = vec![29, len as u8, ao.key_id, ao.rnext_key_id];
392 buf.extend_from_slice(&ao.mac);
393 buf
394 },
395
396 Self::Tfo { cookie: None } => vec![34, 2],
397
398 Self::Tfo { cookie: Some(c) } => {
399 let mut buf = vec![34, (2 + c.len()) as u8];
400 buf.extend_from_slice(c);
401 buf
402 },
403
404 Self::Unknown { kind, data } => {
405 let mut buf = vec![*kind, (2 + data.len()) as u8];
406 buf.extend_from_slice(data);
407 buf
408 },
409 }
410 }
411
412 #[must_use]
414 pub fn mss(mss: u16) -> Self {
415 Self::Mss(mss)
416 }
417
418 #[must_use]
420 pub fn wscale(scale: u8) -> Self {
421 Self::WScale(scale)
422 }
423
424 #[must_use]
426 pub fn timestamp(ts_val: u32, ts_ecr: u32) -> Self {
427 Self::Timestamp(TcpTimestamp::new(ts_val, ts_ecr))
428 }
429
430 #[must_use]
432 pub fn sack(blocks: Vec<TcpSackBlock>) -> Self {
433 Self::Sack(blocks)
434 }
435}
436
437#[derive(Debug, Clone, Default, PartialEq, Eq)]
439pub struct TcpOptions {
440 pub options: Vec<TcpOption>,
441}
442
443impl TcpOptions {
444 #[must_use]
446 pub fn new() -> Self {
447 Self::default()
448 }
449
450 #[must_use]
452 pub fn from_vec(options: Vec<TcpOption>) -> Self {
453 Self { options }
454 }
455
456 #[must_use]
458 pub fn is_empty(&self) -> bool {
459 self.options.is_empty()
460 }
461
462 #[must_use]
464 pub fn len(&self) -> usize {
465 self.options.len()
466 }
467
468 #[must_use]
470 pub fn byte_len(&self) -> usize {
471 self.options.iter().map(TcpOption::len).sum()
472 }
473
474 #[must_use]
476 pub fn padded_len(&self) -> usize {
477 let len = self.byte_len();
478 (len + 3) & !3
479 }
480
481 pub fn push(&mut self, option: TcpOption) {
483 self.options.push(option);
484 }
485
486 #[must_use]
488 pub fn get(&self, kind: TcpOptionKind) -> Option<&TcpOption> {
489 self.options.iter().find(|o| o.kind() == kind)
490 }
491
492 #[must_use]
494 pub fn mss(&self) -> Option<u16> {
495 self.options.iter().find_map(|o| match o {
496 TcpOption::Mss(mss) => Some(*mss),
497 _ => None,
498 })
499 }
500
501 #[must_use]
503 pub fn wscale(&self) -> Option<u8> {
504 self.options.iter().find_map(|o| match o {
505 TcpOption::WScale(scale) => Some(*scale),
506 _ => None,
507 })
508 }
509
510 #[must_use]
512 pub fn timestamp(&self) -> Option<TcpTimestamp> {
513 self.options.iter().find_map(|o| match o {
514 TcpOption::Timestamp(ts) => Some(*ts),
515 _ => None,
516 })
517 }
518
519 #[must_use]
521 pub fn sack_permitted(&self) -> bool {
522 self.options.iter().any(|o| matches!(o, TcpOption::SackOk))
523 }
524
525 #[must_use]
527 pub fn sack_blocks(&self) -> Option<&[TcpSackBlock]> {
528 self.options.iter().find_map(|o| match o {
529 TcpOption::Sack(blocks) => Some(blocks.as_slice()),
530 _ => None,
531 })
532 }
533
534 #[must_use]
536 pub fn ao(&self) -> Option<&TcpAoValue> {
537 self.options.iter().find_map(|o| match o {
538 TcpOption::Ao(ao) => Some(ao),
539 _ => None,
540 })
541 }
542
543 #[must_use]
545 pub fn tfo_cookie(&self) -> Option<&[u8]> {
546 self.options.iter().find_map(|o| match o {
547 TcpOption::Tfo { cookie: Some(c) } => Some(c.as_slice()),
548 _ => None,
549 })
550 }
551
552 #[must_use]
554 pub fn to_bytes(&self) -> Vec<u8> {
555 let mut buf = Vec::new();
556 for opt in &self.options {
557 buf.extend_from_slice(&opt.to_bytes());
558 }
559
560 let pad = (4 - (buf.len() % 4)) % 4;
562 buf.extend(std::iter::repeat_n(0u8, pad));
563
564 buf
565 }
566
567 #[must_use]
569 pub fn to_bytes_unpadded(&self) -> Vec<u8> {
570 let mut buf = Vec::new();
571 for opt in &self.options {
572 buf.extend_from_slice(&opt.to_bytes());
573 }
574 buf
575 }
576}
577
578impl IntoIterator for TcpOptions {
579 type Item = TcpOption;
580 type IntoIter = std::vec::IntoIter<TcpOption>;
581
582 fn into_iter(self) -> Self::IntoIter {
583 self.options.into_iter()
584 }
585}
586
587impl<'a> IntoIterator for &'a TcpOptions {
588 type Item = &'a TcpOption;
589 type IntoIter = std::slice::Iter<'a, TcpOption>;
590
591 fn into_iter(self) -> Self::IntoIter {
592 self.options.iter()
593 }
594}
595
596pub fn parse_options(data: &[u8]) -> Result<TcpOptions, FieldError> {
598 let mut options = Vec::new();
599 let mut offset = 0;
600
601 while offset < data.len() {
602 let kind = data[offset];
603
604 match kind {
605 0 => {
607 options.push(TcpOption::Eol);
608 break;
609 },
610
611 1 => {
613 options.push(TcpOption::Nop);
614 offset += 1;
615 },
616
617 _ => {
619 if offset + 1 >= data.len() {
620 return Err(FieldError::InvalidValue(
621 "option length field missing".to_string(),
622 ));
623 }
624
625 let length = data[offset + 1] as usize;
626 if length < 2 {
627 return Err(FieldError::InvalidValue(format!(
628 "option length {length} is less than minimum (2)"
629 )));
630 }
631
632 if offset + length > data.len() {
633 return Err(FieldError::BufferTooShort {
634 offset,
635 need: length,
636 have: data.len() - offset,
637 });
638 }
639
640 let opt_data = &data[offset..offset + length];
641 let opt = parse_single_option(kind, opt_data)?;
642 options.push(opt);
643
644 offset += length;
645 },
646 }
647 }
648
649 Ok(TcpOptions { options })
650}
651
652fn parse_single_option(kind: u8, data: &[u8]) -> Result<TcpOption, FieldError> {
654 let length = data[1] as usize;
655 let value = &data[2..length];
656
657 match kind {
658 2 => {
660 if length != 4 {
661 return Err(FieldError::InvalidValue(format!(
662 "MSS option length {length} != 4"
663 )));
664 }
665 let mss = u16::from_be_bytes([value[0], value[1]]);
666 Ok(TcpOption::Mss(mss))
667 },
668
669 3 => {
671 if length != 3 {
672 return Err(FieldError::InvalidValue(format!(
673 "WScale option length {length} != 3"
674 )));
675 }
676 Ok(TcpOption::WScale(value[0]))
677 },
678
679 4 => {
681 if length != 2 {
682 return Err(FieldError::InvalidValue(format!(
683 "SAckOK option length {length} != 2"
684 )));
685 }
686 Ok(TcpOption::SackOk)
687 },
688
689 5 => {
691 let block_count = value.len() / 8;
692 let mut blocks = Vec::with_capacity(block_count);
693
694 for chunk in value.chunks_exact(8) {
695 let left = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
696 let right = u32::from_be_bytes([chunk[4], chunk[5], chunk[6], chunk[7]]);
697 blocks.push(TcpSackBlock::new(left, right));
698 }
699
700 Ok(TcpOption::Sack(blocks))
701 },
702
703 8 => {
705 if length != 10 {
706 return Err(FieldError::InvalidValue(format!(
707 "Timestamp option length {length} != 10"
708 )));
709 }
710 let ts_val = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
711 let ts_ecr = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);
712 Ok(TcpOption::Timestamp(TcpTimestamp::new(ts_val, ts_ecr)))
713 },
714
715 14 => {
717 if length < 3 {
718 return Err(FieldError::InvalidValue(format!(
719 "AltChkSum option length {length} < 3"
720 )));
721 }
722 let algorithm = value[0];
723 let checksum = if value.len() >= 3 {
724 u16::from_be_bytes([value[1], value[2]])
725 } else {
726 0
727 };
728 Ok(TcpOption::AltChkSum {
729 algorithm,
730 checksum,
731 })
732 },
733
734 15 => Ok(TcpOption::AltChkSumOpt),
736
737 19 => {
739 if length != 18 {
740 return Err(FieldError::InvalidValue(format!(
741 "MD5 option length {length} != 18"
742 )));
743 }
744 let mut sig = [0u8; 16];
745 sig.copy_from_slice(value);
746 Ok(TcpOption::Md5(sig))
747 },
748
749 25 => {
751 let mood = String::from_utf8_lossy(value).to_string();
752 Ok(TcpOption::Mood(mood))
753 },
754
755 28 => {
757 if length != 4 {
758 return Err(FieldError::InvalidValue(format!(
759 "UTO option length {length} != 4"
760 )));
761 }
762 let timeout = u16::from_be_bytes([value[0], value[1]]);
763 Ok(TcpOption::Uto(timeout))
764 },
765
766 29 => {
768 if length < 4 {
769 return Err(FieldError::InvalidValue(format!(
770 "AO option length {length} < 4"
771 )));
772 }
773 let key_id = value[0];
774 let rnext_key_id = value[1];
775 let mac = value[2..].to_vec();
776 Ok(TcpOption::Ao(TcpAoValue::new(key_id, rnext_key_id, mac)))
777 },
778
779 34 => {
781 let cookie = if value.is_empty() {
782 None
783 } else {
784 Some(value.to_vec())
785 };
786 Ok(TcpOption::Tfo { cookie })
787 },
788
789 _ => Ok(TcpOption::Unknown {
791 kind,
792 data: value.to_vec(),
793 }),
794 }
795}
796
797#[derive(Debug, Clone, Default)]
799pub struct TcpOptionsBuilder {
800 options: Vec<TcpOption>,
801}
802
803impl TcpOptionsBuilder {
804 #[must_use]
806 pub fn new() -> Self {
807 Self::default()
808 }
809
810 #[must_use]
812 pub fn nop(mut self) -> Self {
813 self.options.push(TcpOption::Nop);
814 self
815 }
816
817 #[must_use]
819 pub fn eol(mut self) -> Self {
820 self.options.push(TcpOption::Eol);
821 self
822 }
823
824 #[must_use]
826 pub fn mss(mut self, mss: u16) -> Self {
827 self.options.push(TcpOption::Mss(mss));
828 self
829 }
830
831 #[must_use]
833 pub fn wscale(mut self, scale: u8) -> Self {
834 self.options.push(TcpOption::WScale(scale));
835 self
836 }
837
838 #[must_use]
840 pub fn sack_ok(mut self) -> Self {
841 self.options.push(TcpOption::SackOk);
842 self
843 }
844
845 #[must_use]
847 pub fn sack(mut self, blocks: Vec<TcpSackBlock>) -> Self {
848 self.options.push(TcpOption::Sack(blocks));
849 self
850 }
851
852 #[must_use]
854 pub fn timestamp(mut self, ts_val: u32, ts_ecr: u32) -> Self {
855 self.options
856 .push(TcpOption::Timestamp(TcpTimestamp::new(ts_val, ts_ecr)));
857 self
858 }
859
860 #[must_use]
862 pub fn tfo(mut self, cookie: Option<Vec<u8>>) -> Self {
863 self.options.push(TcpOption::Tfo { cookie });
864 self
865 }
866
867 #[must_use]
869 pub fn ao(mut self, key_id: u8, rnext_key_id: u8, mac: Vec<u8>) -> Self {
870 self.options
871 .push(TcpOption::Ao(TcpAoValue::new(key_id, rnext_key_id, mac)));
872 self
873 }
874
875 #[must_use]
877 pub fn md5(mut self, signature: [u8; 16]) -> Self {
878 self.options.push(TcpOption::Md5(signature));
879 self
880 }
881
882 #[must_use]
884 pub fn option(mut self, option: TcpOption) -> Self {
885 self.options.push(option);
886 self
887 }
888
889 #[must_use]
891 pub fn build(self) -> TcpOptions {
892 TcpOptions {
893 options: self.options,
894 }
895 }
896}
897
898#[must_use]
900pub fn get_tcp_ao(options: &TcpOptions) -> Option<&TcpAoValue> {
901 options.ao()
902}
903
904#[cfg(test)]
905mod tests {
906 use super::*;
907
908 #[test]
909 fn test_parse_nop_eol() {
910 let data = [1, 1, 1, 0]; let opts = parse_options(&data).unwrap();
912
913 assert_eq!(opts.len(), 4);
914 assert!(matches!(opts.options[0], TcpOption::Nop));
915 assert!(matches!(opts.options[1], TcpOption::Nop));
916 assert!(matches!(opts.options[2], TcpOption::Nop));
917 assert!(matches!(opts.options[3], TcpOption::Eol));
918 }
919
920 #[test]
921 fn test_parse_mss() {
922 let data = [
923 2, 4, 0x05, 0xB4, ];
926
927 let opts = parse_options(&data).unwrap();
928 assert_eq!(opts.len(), 1);
929 assert_eq!(opts.mss(), Some(1460));
930 }
931
932 #[test]
933 fn test_parse_wscale() {
934 let data = [
935 3, 3, 7, ];
938
939 let opts = parse_options(&data).unwrap();
940 assert_eq!(opts.len(), 1);
941 assert_eq!(opts.wscale(), Some(7));
942 }
943
944 #[test]
945 fn test_parse_timestamp() {
946 let data = [
947 8, 10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, ];
951
952 let opts = parse_options(&data).unwrap();
953 assert_eq!(opts.len(), 1);
954
955 let ts = opts.timestamp().unwrap();
956 assert_eq!(ts.ts_val, 0x1000);
957 assert_eq!(ts.ts_ecr, 0x2000);
958 }
959
960 #[test]
961 fn test_parse_sack() {
962 let data = [
963 5, 18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x40, 0x00, ];
969
970 let opts = parse_options(&data).unwrap();
971 assert_eq!(opts.len(), 1);
972
973 let blocks = opts.sack_blocks().unwrap();
974 assert_eq!(blocks.len(), 2);
975 assert_eq!(blocks[0].left, 0x1000);
976 assert_eq!(blocks[0].right, 0x2000);
977 assert_eq!(blocks[1].left, 0x3000);
978 assert_eq!(blocks[1].right, 0x4000);
979 }
980
981 #[test]
982 fn test_parse_sack_ok() {
983 let data = [4, 2]; let opts = parse_options(&data).unwrap();
986 assert_eq!(opts.len(), 1);
987 assert!(opts.sack_permitted());
988 }
989
990 #[test]
991 fn test_parse_ao() {
992 let data = [
993 29, 6, 1, 2, 0xAB, 0xCD, ];
998
999 let opts = parse_options(&data).unwrap();
1000 assert_eq!(opts.len(), 1);
1001
1002 let ao = opts.ao().unwrap();
1003 assert_eq!(ao.key_id, 1);
1004 assert_eq!(ao.rnext_key_id, 2);
1005 assert_eq!(ao.mac, vec![0xAB, 0xCD]);
1006 }
1007
1008 #[test]
1009 fn test_serialize_options() {
1010 let opts = TcpOptionsBuilder::new()
1011 .mss(1460)
1012 .wscale(7)
1013 .sack_ok()
1014 .timestamp(1000, 2000)
1015 .build();
1016
1017 let bytes = opts.to_bytes();
1018
1019 assert_eq!(bytes.len() % 4, 0);
1021
1022 let parsed = parse_options(&bytes).unwrap();
1024 assert_eq!(parsed.mss(), Some(1460));
1025 assert_eq!(parsed.wscale(), Some(7));
1026 assert!(parsed.sack_permitted());
1027
1028 let ts = parsed.timestamp().unwrap();
1029 assert_eq!(ts.ts_val, 1000);
1030 assert_eq!(ts.ts_ecr, 2000);
1031 }
1032
1033 #[test]
1034 fn test_option_kind_properties() {
1035 assert!(TcpOptionKind::Eol.is_single_byte());
1036 assert!(TcpOptionKind::Nop.is_single_byte());
1037 assert!(!TcpOptionKind::Mss.is_single_byte());
1038
1039 assert_eq!(TcpOptionKind::Mss.expected_len(), Some(4));
1040 assert_eq!(TcpOptionKind::Timestamp.expected_len(), Some(10));
1041 assert_eq!(TcpOptionKind::Sack.expected_len(), None);
1042 }
1043
1044 #[test]
1045 fn test_typical_syn_options() {
1046 let opts = TcpOptionsBuilder::new()
1048 .mss(1460)
1049 .sack_ok()
1050 .timestamp(12345, 0)
1051 .nop()
1052 .wscale(7)
1053 .build();
1054
1055 let bytes = opts.to_bytes();
1056
1057 let parsed = parse_options(&bytes).unwrap();
1059 assert_eq!(parsed.mss(), Some(1460));
1060 assert!(parsed.sack_permitted());
1061 assert_eq!(parsed.wscale(), Some(7));
1062
1063 let ts = parsed.timestamp().unwrap();
1064 assert_eq!(ts.ts_val, 12345);
1065 assert_eq!(ts.ts_ecr, 0);
1066 }
1067
1068 #[test]
1069 fn test_tfo_option() {
1070 let cookie = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
1071 let opts = TcpOptionsBuilder::new().tfo(Some(cookie.clone())).build();
1072
1073 let bytes = opts.to_bytes();
1074 let parsed = parse_options(&bytes).unwrap();
1075
1076 assert_eq!(parsed.tfo_cookie(), Some(cookie.as_slice()));
1077 }
1078
1079 #[test]
1080 fn test_md5_option() {
1081 let sig = [1u8; 16];
1082 let opts = TcpOptionsBuilder::new().md5(sig).build();
1083
1084 let bytes = opts.to_bytes();
1085 let parsed = parse_options(&bytes).unwrap();
1086
1087 if let Some(TcpOption::Md5(parsed_sig)) = parsed.get(TcpOptionKind::Md5) {
1088 assert_eq!(*parsed_sig, sig);
1089 } else {
1090 panic!("Expected MD5 option");
1091 }
1092 }
1093}