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 pub fn from_byte(b: u8) -> Self {
66 match b {
67 0 => Self::Eol,
68 1 => Self::Nop,
69 2 => Self::Mss,
70 3 => Self::WScale,
71 4 => Self::SackOk,
72 5 => Self::Sack,
73 8 => Self::Timestamp,
74 14 => Self::AltChkSum,
75 15 => Self::AltChkSumOpt,
76 19 => Self::Md5,
77 25 => Self::Mood,
78 28 => Self::Uto,
79 29 => Self::Ao,
80 34 => Self::Tfo,
81 x => Self::Unknown(x),
82 }
83 }
84
85 pub fn to_byte(self) -> u8 {
87 match self {
88 Self::Eol => 0,
89 Self::Nop => 1,
90 Self::Mss => 2,
91 Self::WScale => 3,
92 Self::SackOk => 4,
93 Self::Sack => 5,
94 Self::Timestamp => 8,
95 Self::AltChkSum => 14,
96 Self::AltChkSumOpt => 15,
97 Self::Md5 => 19,
98 Self::Mood => 25,
99 Self::Uto => 28,
100 Self::Ao => 29,
101 Self::Tfo => 34,
102 Self::Unknown(x) => x,
103 }
104 }
105
106 pub fn name(&self) -> &'static str {
108 match self {
109 Self::Eol => "EOL",
110 Self::Nop => "NOP",
111 Self::Mss => "MSS",
112 Self::WScale => "WScale",
113 Self::SackOk => "SAckOK",
114 Self::Sack => "SAck",
115 Self::Timestamp => "Timestamp",
116 Self::AltChkSum => "AltChkSum",
117 Self::AltChkSumOpt => "AltChkSumOpt",
118 Self::Md5 => "MD5",
119 Self::Mood => "Mood",
120 Self::Uto => "UTO",
121 Self::Ao => "AO",
122 Self::Tfo => "TFO",
123 Self::Unknown(_) => "Unknown",
124 }
125 }
126
127 #[inline]
129 pub fn is_single_byte(&self) -> bool {
130 matches!(self, Self::Eol | Self::Nop)
131 }
132
133 pub fn expected_len(&self) -> Option<usize> {
136 match self {
137 Self::Eol | Self::Nop => Some(1),
138 Self::Mss => Some(4),
139 Self::WScale => Some(3),
140 Self::SackOk => Some(2),
141 Self::Timestamp => Some(10),
142 Self::AltChkSum => Some(4), Self::AltChkSumOpt => Some(2),
144 Self::Md5 => Some(18),
145 Self::Uto => Some(4),
146 Self::Tfo => Some(10), Self::Sack | Self::Ao | Self::Mood | Self::Unknown(_) => None,
148 }
149 }
150}
151
152impl std::fmt::Display for TcpOptionKind {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 match self {
155 Self::Unknown(x) => write!(f, "Unknown({})", x),
156 _ => write!(f, "{}", self.name()),
157 }
158 }
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub struct TcpSackBlock {
164 pub left: u32,
166 pub right: u32,
168}
169
170impl TcpSackBlock {
171 pub fn new(left: u32, right: u32) -> Self {
173 Self { left, right }
174 }
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
179pub struct TcpTimestamp {
180 pub ts_val: u32,
182 pub ts_ecr: u32,
184}
185
186impl TcpTimestamp {
187 pub fn new(ts_val: u32, ts_ecr: u32) -> Self {
189 Self { ts_val, ts_ecr }
190 }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
195pub struct TcpAoValue {
196 pub key_id: u8,
198 pub rnext_key_id: u8,
200 pub mac: Vec<u8>,
202}
203
204impl TcpAoValue {
205 pub fn new(key_id: u8, rnext_key_id: u8, mac: Vec<u8>) -> Self {
207 Self {
208 key_id,
209 rnext_key_id,
210 mac,
211 }
212 }
213}
214
215#[derive(Debug, Clone, PartialEq, Eq)]
217pub enum TcpOption {
218 Eol,
220
221 Nop,
223
224 Mss(u16),
226
227 WScale(u8),
229
230 SackOk,
232
233 Sack(Vec<TcpSackBlock>),
235
236 Timestamp(TcpTimestamp),
238
239 AltChkSum { algorithm: u8, checksum: u16 },
241
242 AltChkSumOpt,
244
245 Md5([u8; 16]),
247
248 Mood(String),
250
251 Uto(u16),
253
254 Ao(TcpAoValue),
256
257 Tfo {
259 cookie: Option<Vec<u8>>,
261 },
262
263 Unknown { kind: u8, data: Vec<u8> },
265}
266
267impl TcpOption {
268 pub fn kind(&self) -> TcpOptionKind {
270 match self {
271 Self::Eol => TcpOptionKind::Eol,
272 Self::Nop => TcpOptionKind::Nop,
273 Self::Mss(_) => TcpOptionKind::Mss,
274 Self::WScale(_) => TcpOptionKind::WScale,
275 Self::SackOk => TcpOptionKind::SackOk,
276 Self::Sack(_) => TcpOptionKind::Sack,
277 Self::Timestamp(_) => TcpOptionKind::Timestamp,
278 Self::AltChkSum { .. } => TcpOptionKind::AltChkSum,
279 Self::AltChkSumOpt => TcpOptionKind::AltChkSumOpt,
280 Self::Md5(_) => TcpOptionKind::Md5,
281 Self::Mood(_) => TcpOptionKind::Mood,
282 Self::Uto(_) => TcpOptionKind::Uto,
283 Self::Ao(_) => TcpOptionKind::Ao,
284 Self::Tfo { .. } => TcpOptionKind::Tfo,
285 Self::Unknown { kind, .. } => TcpOptionKind::Unknown(*kind),
286 }
287 }
288
289 pub fn len(&self) -> usize {
291 match self {
292 Self::Eol => 1,
293 Self::Nop => 1,
294 Self::Mss(_) => 4,
295 Self::WScale(_) => 3,
296 Self::SackOk => 2,
297 Self::Sack(blocks) => 2 + blocks.len() * 8,
298 Self::Timestamp(_) => 10,
299 Self::AltChkSum { .. } => 4,
300 Self::AltChkSumOpt => 2,
301 Self::Md5(_) => 18,
302 Self::Mood(s) => 2 + s.len(),
303 Self::Uto(_) => 4,
304 Self::Ao(ao) => 4 + ao.mac.len(),
305 Self::Tfo { cookie: None } => 2,
306 Self::Tfo { cookie: Some(c) } => 2 + c.len(),
307 Self::Unknown { data, .. } => 2 + data.len(),
308 }
309 }
310
311 pub fn is_empty(&self) -> bool {
313 self.len() == 0
314 }
315
316 pub fn to_bytes(&self) -> Vec<u8> {
318 match self {
319 Self::Eol => vec![0],
320 Self::Nop => vec![1],
321
322 Self::Mss(mss) => {
323 let mut buf = vec![2, 4];
324 buf.extend_from_slice(&mss.to_be_bytes());
325 buf
326 }
327
328 Self::WScale(scale) => vec![3, 3, *scale],
329
330 Self::SackOk => vec![4, 2],
331
332 Self::Sack(blocks) => {
333 let mut buf = vec![5, (2 + blocks.len() * 8) as u8];
334 for block in blocks {
335 buf.extend_from_slice(&block.left.to_be_bytes());
336 buf.extend_from_slice(&block.right.to_be_bytes());
337 }
338 buf
339 }
340
341 Self::Timestamp(ts) => {
342 let mut buf = vec![8, 10];
343 buf.extend_from_slice(&ts.ts_val.to_be_bytes());
344 buf.extend_from_slice(&ts.ts_ecr.to_be_bytes());
345 buf
346 }
347
348 Self::AltChkSum {
349 algorithm,
350 checksum,
351 } => {
352 let mut buf = vec![14, 4, *algorithm];
353 buf.extend_from_slice(&checksum.to_be_bytes());
354 buf
355 }
356
357 Self::AltChkSumOpt => vec![15, 2],
358
359 Self::Md5(sig) => {
360 let mut buf = vec![19, 18];
361 buf.extend_from_slice(sig);
362 buf
363 }
364
365 Self::Mood(mood) => {
366 let mut buf = vec![25, (2 + mood.len()) as u8];
367 buf.extend_from_slice(mood.as_bytes());
368 buf
369 }
370
371 Self::Uto(timeout) => {
372 let mut buf = vec![28, 4];
373 buf.extend_from_slice(&timeout.to_be_bytes());
374 buf
375 }
376
377 Self::Ao(ao) => {
378 let len = 4 + ao.mac.len();
379 let mut buf = vec![29, len as u8, ao.key_id, ao.rnext_key_id];
380 buf.extend_from_slice(&ao.mac);
381 buf
382 }
383
384 Self::Tfo { cookie: None } => vec![34, 2],
385
386 Self::Tfo { cookie: Some(c) } => {
387 let mut buf = vec![34, (2 + c.len()) as u8];
388 buf.extend_from_slice(c);
389 buf
390 }
391
392 Self::Unknown { kind, data } => {
393 let mut buf = vec![*kind, (2 + data.len()) as u8];
394 buf.extend_from_slice(data);
395 buf
396 }
397 }
398 }
399
400 pub fn mss(mss: u16) -> Self {
402 Self::Mss(mss)
403 }
404
405 pub fn wscale(scale: u8) -> Self {
407 Self::WScale(scale)
408 }
409
410 pub fn timestamp(ts_val: u32, ts_ecr: u32) -> Self {
412 Self::Timestamp(TcpTimestamp::new(ts_val, ts_ecr))
413 }
414
415 pub fn sack(blocks: Vec<TcpSackBlock>) -> Self {
417 Self::Sack(blocks)
418 }
419}
420
421#[derive(Debug, Clone, Default, PartialEq, Eq)]
423pub struct TcpOptions {
424 pub options: Vec<TcpOption>,
425}
426
427impl TcpOptions {
428 pub fn new() -> Self {
430 Self::default()
431 }
432
433 pub fn from_vec(options: Vec<TcpOption>) -> Self {
435 Self { options }
436 }
437
438 pub fn is_empty(&self) -> bool {
440 self.options.is_empty()
441 }
442
443 pub fn len(&self) -> usize {
445 self.options.len()
446 }
447
448 pub fn byte_len(&self) -> usize {
450 self.options.iter().map(|o| o.len()).sum()
451 }
452
453 pub fn padded_len(&self) -> usize {
455 let len = self.byte_len();
456 (len + 3) & !3
457 }
458
459 pub fn push(&mut self, option: TcpOption) {
461 self.options.push(option);
462 }
463
464 pub fn get(&self, kind: TcpOptionKind) -> Option<&TcpOption> {
466 self.options.iter().find(|o| o.kind() == kind)
467 }
468
469 pub fn mss(&self) -> Option<u16> {
471 self.options.iter().find_map(|o| match o {
472 TcpOption::Mss(mss) => Some(*mss),
473 _ => None,
474 })
475 }
476
477 pub fn wscale(&self) -> Option<u8> {
479 self.options.iter().find_map(|o| match o {
480 TcpOption::WScale(scale) => Some(*scale),
481 _ => None,
482 })
483 }
484
485 pub fn timestamp(&self) -> Option<TcpTimestamp> {
487 self.options.iter().find_map(|o| match o {
488 TcpOption::Timestamp(ts) => Some(*ts),
489 _ => None,
490 })
491 }
492
493 pub fn sack_permitted(&self) -> bool {
495 self.options.iter().any(|o| matches!(o, TcpOption::SackOk))
496 }
497
498 pub fn sack_blocks(&self) -> Option<&[TcpSackBlock]> {
500 self.options.iter().find_map(|o| match o {
501 TcpOption::Sack(blocks) => Some(blocks.as_slice()),
502 _ => None,
503 })
504 }
505
506 pub fn ao(&self) -> Option<&TcpAoValue> {
508 self.options.iter().find_map(|o| match o {
509 TcpOption::Ao(ao) => Some(ao),
510 _ => None,
511 })
512 }
513
514 pub fn tfo_cookie(&self) -> Option<&[u8]> {
516 self.options.iter().find_map(|o| match o {
517 TcpOption::Tfo { cookie: Some(c) } => Some(c.as_slice()),
518 _ => None,
519 })
520 }
521
522 pub fn to_bytes(&self) -> Vec<u8> {
524 let mut buf = Vec::new();
525 for opt in &self.options {
526 buf.extend_from_slice(&opt.to_bytes());
527 }
528
529 let pad = (4 - (buf.len() % 4)) % 4;
531 buf.extend(std::iter::repeat(0u8).take(pad));
532
533 buf
534 }
535
536 pub fn to_bytes_unpadded(&self) -> Vec<u8> {
538 let mut buf = Vec::new();
539 for opt in &self.options {
540 buf.extend_from_slice(&opt.to_bytes());
541 }
542 buf
543 }
544}
545
546impl IntoIterator for TcpOptions {
547 type Item = TcpOption;
548 type IntoIter = std::vec::IntoIter<TcpOption>;
549
550 fn into_iter(self) -> Self::IntoIter {
551 self.options.into_iter()
552 }
553}
554
555impl<'a> IntoIterator for &'a TcpOptions {
556 type Item = &'a TcpOption;
557 type IntoIter = std::slice::Iter<'a, TcpOption>;
558
559 fn into_iter(self) -> Self::IntoIter {
560 self.options.iter()
561 }
562}
563
564pub fn parse_options(data: &[u8]) -> Result<TcpOptions, FieldError> {
566 let mut options = Vec::new();
567 let mut offset = 0;
568
569 while offset < data.len() {
570 let kind = data[offset];
571
572 match kind {
573 0 => {
575 options.push(TcpOption::Eol);
576 break;
577 }
578
579 1 => {
581 options.push(TcpOption::Nop);
582 offset += 1;
583 }
584
585 _ => {
587 if offset + 1 >= data.len() {
588 return Err(FieldError::InvalidValue(
589 "option length field missing".to_string(),
590 ));
591 }
592
593 let length = data[offset + 1] as usize;
594 if length < 2 {
595 return Err(FieldError::InvalidValue(format!(
596 "option length {} is less than minimum (2)",
597 length
598 )));
599 }
600
601 if offset + length > data.len() {
602 return Err(FieldError::BufferTooShort {
603 offset,
604 need: length,
605 have: data.len() - offset,
606 });
607 }
608
609 let opt_data = &data[offset..offset + length];
610 let opt = parse_single_option(kind, opt_data)?;
611 options.push(opt);
612
613 offset += length;
614 }
615 }
616 }
617
618 Ok(TcpOptions { options })
619}
620
621fn parse_single_option(kind: u8, data: &[u8]) -> Result<TcpOption, FieldError> {
623 let length = data[1] as usize;
624 let value = &data[2..length];
625
626 match kind {
627 2 => {
629 if length != 4 {
630 return Err(FieldError::InvalidValue(format!(
631 "MSS option length {} != 4",
632 length
633 )));
634 }
635 let mss = u16::from_be_bytes([value[0], value[1]]);
636 Ok(TcpOption::Mss(mss))
637 }
638
639 3 => {
641 if length != 3 {
642 return Err(FieldError::InvalidValue(format!(
643 "WScale option length {} != 3",
644 length
645 )));
646 }
647 Ok(TcpOption::WScale(value[0]))
648 }
649
650 4 => {
652 if length != 2 {
653 return Err(FieldError::InvalidValue(format!(
654 "SAckOK option length {} != 2",
655 length
656 )));
657 }
658 Ok(TcpOption::SackOk)
659 }
660
661 5 => {
663 let block_count = value.len() / 8;
664 let mut blocks = Vec::with_capacity(block_count);
665
666 for chunk in value.chunks_exact(8) {
667 let left = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
668 let right = u32::from_be_bytes([chunk[4], chunk[5], chunk[6], chunk[7]]);
669 blocks.push(TcpSackBlock::new(left, right));
670 }
671
672 Ok(TcpOption::Sack(blocks))
673 }
674
675 8 => {
677 if length != 10 {
678 return Err(FieldError::InvalidValue(format!(
679 "Timestamp option length {} != 10",
680 length
681 )));
682 }
683 let ts_val = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
684 let ts_ecr = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);
685 Ok(TcpOption::Timestamp(TcpTimestamp::new(ts_val, ts_ecr)))
686 }
687
688 14 => {
690 if length < 3 {
691 return Err(FieldError::InvalidValue(format!(
692 "AltChkSum option length {} < 3",
693 length
694 )));
695 }
696 let algorithm = value[0];
697 let checksum = if value.len() >= 3 {
698 u16::from_be_bytes([value[1], value[2]])
699 } else {
700 0
701 };
702 Ok(TcpOption::AltChkSum {
703 algorithm,
704 checksum,
705 })
706 }
707
708 15 => Ok(TcpOption::AltChkSumOpt),
710
711 19 => {
713 if length != 18 {
714 return Err(FieldError::InvalidValue(format!(
715 "MD5 option length {} != 18",
716 length
717 )));
718 }
719 let mut sig = [0u8; 16];
720 sig.copy_from_slice(value);
721 Ok(TcpOption::Md5(sig))
722 }
723
724 25 => {
726 let mood = String::from_utf8_lossy(value).to_string();
727 Ok(TcpOption::Mood(mood))
728 }
729
730 28 => {
732 if length != 4 {
733 return Err(FieldError::InvalidValue(format!(
734 "UTO option length {} != 4",
735 length
736 )));
737 }
738 let timeout = u16::from_be_bytes([value[0], value[1]]);
739 Ok(TcpOption::Uto(timeout))
740 }
741
742 29 => {
744 if length < 4 {
745 return Err(FieldError::InvalidValue(format!(
746 "AO option length {} < 4",
747 length
748 )));
749 }
750 let key_id = value[0];
751 let rnext_key_id = value[1];
752 let mac = value[2..].to_vec();
753 Ok(TcpOption::Ao(TcpAoValue::new(key_id, rnext_key_id, mac)))
754 }
755
756 34 => {
758 let cookie = if value.is_empty() {
759 None
760 } else {
761 Some(value.to_vec())
762 };
763 Ok(TcpOption::Tfo { cookie })
764 }
765
766 _ => Ok(TcpOption::Unknown {
768 kind,
769 data: value.to_vec(),
770 }),
771 }
772}
773
774#[derive(Debug, Clone, Default)]
776pub struct TcpOptionsBuilder {
777 options: Vec<TcpOption>,
778}
779
780impl TcpOptionsBuilder {
781 pub fn new() -> Self {
783 Self::default()
784 }
785
786 pub fn nop(mut self) -> Self {
788 self.options.push(TcpOption::Nop);
789 self
790 }
791
792 pub fn eol(mut self) -> Self {
794 self.options.push(TcpOption::Eol);
795 self
796 }
797
798 pub fn mss(mut self, mss: u16) -> Self {
800 self.options.push(TcpOption::Mss(mss));
801 self
802 }
803
804 pub fn wscale(mut self, scale: u8) -> Self {
806 self.options.push(TcpOption::WScale(scale));
807 self
808 }
809
810 pub fn sack_ok(mut self) -> Self {
812 self.options.push(TcpOption::SackOk);
813 self
814 }
815
816 pub fn sack(mut self, blocks: Vec<TcpSackBlock>) -> Self {
818 self.options.push(TcpOption::Sack(blocks));
819 self
820 }
821
822 pub fn timestamp(mut self, ts_val: u32, ts_ecr: u32) -> Self {
824 self.options
825 .push(TcpOption::Timestamp(TcpTimestamp::new(ts_val, ts_ecr)));
826 self
827 }
828
829 pub fn tfo(mut self, cookie: Option<Vec<u8>>) -> Self {
831 self.options.push(TcpOption::Tfo { cookie });
832 self
833 }
834
835 pub fn ao(mut self, key_id: u8, rnext_key_id: u8, mac: Vec<u8>) -> Self {
837 self.options
838 .push(TcpOption::Ao(TcpAoValue::new(key_id, rnext_key_id, mac)));
839 self
840 }
841
842 pub fn md5(mut self, signature: [u8; 16]) -> Self {
844 self.options.push(TcpOption::Md5(signature));
845 self
846 }
847
848 pub fn option(mut self, option: TcpOption) -> Self {
850 self.options.push(option);
851 self
852 }
853
854 pub fn build(self) -> TcpOptions {
856 TcpOptions {
857 options: self.options,
858 }
859 }
860}
861
862pub fn get_tcp_ao(options: &TcpOptions) -> Option<&TcpAoValue> {
864 options.ao()
865}
866
867#[cfg(test)]
868mod tests {
869 use super::*;
870
871 #[test]
872 fn test_parse_nop_eol() {
873 let data = [1, 1, 1, 0]; let opts = parse_options(&data).unwrap();
875
876 assert_eq!(opts.len(), 4);
877 assert!(matches!(opts.options[0], TcpOption::Nop));
878 assert!(matches!(opts.options[1], TcpOption::Nop));
879 assert!(matches!(opts.options[2], TcpOption::Nop));
880 assert!(matches!(opts.options[3], TcpOption::Eol));
881 }
882
883 #[test]
884 fn test_parse_mss() {
885 let data = [
886 2, 4, 0x05, 0xB4, ];
889
890 let opts = parse_options(&data).unwrap();
891 assert_eq!(opts.len(), 1);
892 assert_eq!(opts.mss(), Some(1460));
893 }
894
895 #[test]
896 fn test_parse_wscale() {
897 let data = [
898 3, 3, 7, ];
901
902 let opts = parse_options(&data).unwrap();
903 assert_eq!(opts.len(), 1);
904 assert_eq!(opts.wscale(), Some(7));
905 }
906
907 #[test]
908 fn test_parse_timestamp() {
909 let data = [
910 8, 10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, ];
914
915 let opts = parse_options(&data).unwrap();
916 assert_eq!(opts.len(), 1);
917
918 let ts = opts.timestamp().unwrap();
919 assert_eq!(ts.ts_val, 0x1000);
920 assert_eq!(ts.ts_ecr, 0x2000);
921 }
922
923 #[test]
924 fn test_parse_sack() {
925 let data = [
926 5, 18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x40, 0x00, ];
932
933 let opts = parse_options(&data).unwrap();
934 assert_eq!(opts.len(), 1);
935
936 let blocks = opts.sack_blocks().unwrap();
937 assert_eq!(blocks.len(), 2);
938 assert_eq!(blocks[0].left, 0x1000);
939 assert_eq!(blocks[0].right, 0x2000);
940 assert_eq!(blocks[1].left, 0x3000);
941 assert_eq!(blocks[1].right, 0x4000);
942 }
943
944 #[test]
945 fn test_parse_sack_ok() {
946 let data = [4, 2]; let opts = parse_options(&data).unwrap();
949 assert_eq!(opts.len(), 1);
950 assert!(opts.sack_permitted());
951 }
952
953 #[test]
954 fn test_parse_ao() {
955 let data = [
956 29, 6, 1, 2, 0xAB, 0xCD, ];
961
962 let opts = parse_options(&data).unwrap();
963 assert_eq!(opts.len(), 1);
964
965 let ao = opts.ao().unwrap();
966 assert_eq!(ao.key_id, 1);
967 assert_eq!(ao.rnext_key_id, 2);
968 assert_eq!(ao.mac, vec![0xAB, 0xCD]);
969 }
970
971 #[test]
972 fn test_serialize_options() {
973 let opts = TcpOptionsBuilder::new()
974 .mss(1460)
975 .wscale(7)
976 .sack_ok()
977 .timestamp(1000, 2000)
978 .build();
979
980 let bytes = opts.to_bytes();
981
982 assert_eq!(bytes.len() % 4, 0);
984
985 let parsed = parse_options(&bytes).unwrap();
987 assert_eq!(parsed.mss(), Some(1460));
988 assert_eq!(parsed.wscale(), Some(7));
989 assert!(parsed.sack_permitted());
990
991 let ts = parsed.timestamp().unwrap();
992 assert_eq!(ts.ts_val, 1000);
993 assert_eq!(ts.ts_ecr, 2000);
994 }
995
996 #[test]
997 fn test_option_kind_properties() {
998 assert!(TcpOptionKind::Eol.is_single_byte());
999 assert!(TcpOptionKind::Nop.is_single_byte());
1000 assert!(!TcpOptionKind::Mss.is_single_byte());
1001
1002 assert_eq!(TcpOptionKind::Mss.expected_len(), Some(4));
1003 assert_eq!(TcpOptionKind::Timestamp.expected_len(), Some(10));
1004 assert_eq!(TcpOptionKind::Sack.expected_len(), None);
1005 }
1006
1007 #[test]
1008 fn test_typical_syn_options() {
1009 let opts = TcpOptionsBuilder::new()
1011 .mss(1460)
1012 .sack_ok()
1013 .timestamp(12345, 0)
1014 .nop()
1015 .wscale(7)
1016 .build();
1017
1018 let bytes = opts.to_bytes();
1019
1020 let parsed = parse_options(&bytes).unwrap();
1022 assert_eq!(parsed.mss(), Some(1460));
1023 assert!(parsed.sack_permitted());
1024 assert_eq!(parsed.wscale(), Some(7));
1025
1026 let ts = parsed.timestamp().unwrap();
1027 assert_eq!(ts.ts_val, 12345);
1028 assert_eq!(ts.ts_ecr, 0);
1029 }
1030
1031 #[test]
1032 fn test_tfo_option() {
1033 let cookie = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
1034 let opts = TcpOptionsBuilder::new().tfo(Some(cookie.clone())).build();
1035
1036 let bytes = opts.to_bytes();
1037 let parsed = parse_options(&bytes).unwrap();
1038
1039 assert_eq!(parsed.tfo_cookie(), Some(cookie.as_slice()));
1040 }
1041
1042 #[test]
1043 fn test_md5_option() {
1044 let sig = [1u8; 16];
1045 let opts = TcpOptionsBuilder::new().md5(sig).build();
1046
1047 let bytes = opts.to_bytes();
1048 let parsed = parse_options(&bytes).unwrap();
1049
1050 if let Some(TcpOption::Md5(parsed_sig)) = parsed.get(TcpOptionKind::Md5) {
1051 assert_eq!(*parsed_sig, sig);
1052 } else {
1053 panic!("Expected MD5 option");
1054 }
1055 }
1056}