1use std::fmt::{self, Formatter};
100
101use zerocopy::byteorder::{BigEndian, U16, U64};
102use zerocopy::{FromBytes, IntoBytes, Unaligned};
103
104use crate::packet::{HeaderParser, PacketHeader};
105
106pub const STT_PORT: u16 = 7471;
108
109#[inline]
111pub fn is_stt_port(port: u16) -> bool {
112 port == STT_PORT
113}
114
115pub const STT_VERSION: u8 = 0;
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub struct SttFlags(pub u8);
121
122impl SttFlags {
123 pub const CSUM_VERIFIED: u8 = 0x01;
125 pub const CSUM_PARTIAL: u8 = 0x02;
127 pub const IPV4: u8 = 0x04;
129 pub const TCP: u8 = 0x08;
131 pub const CSUM_PRESENT: u8 = 0x10;
133
134 #[inline]
136 pub const fn new(value: u8) -> Self {
137 Self(value)
138 }
139
140 #[inline]
142 pub fn is_csum_verified(&self) -> bool {
143 self.0 & Self::CSUM_VERIFIED != 0
144 }
145
146 #[inline]
148 pub fn is_csum_partial(&self) -> bool {
149 self.0 & Self::CSUM_PARTIAL != 0
150 }
151
152 #[inline]
154 pub fn is_ipv4(&self) -> bool {
155 self.0 & Self::IPV4 != 0
156 }
157
158 #[inline]
160 pub fn is_tcp(&self) -> bool {
161 self.0 & Self::TCP != 0
162 }
163
164 #[inline]
166 pub fn is_csum_present(&self) -> bool {
167 self.0 & Self::CSUM_PRESENT != 0
168 }
169}
170
171impl fmt::Display for SttFlags {
172 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
173 let mut flags = Vec::new();
174
175 if self.is_csum_verified() {
176 flags.push("CSUM_VERIFIED");
177 }
178 if self.is_csum_partial() {
179 flags.push("CSUM_PARTIAL");
180 }
181 if self.is_ipv4() {
182 flags.push("IPV4");
183 }
184 if self.is_tcp() {
185 flags.push("TCP");
186 }
187 if self.is_csum_present() {
188 flags.push("CSUM_PRESENT");
189 }
190
191 if flags.is_empty() {
192 write!(f, "none")
193 } else {
194 write!(f, "{}", flags.join(","))
195 }
196 }
197}
198
199#[repr(C, packed)]
204#[derive(
205 FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, zerocopy::KnownLayout, zerocopy::Immutable,
206)]
207pub struct SttHeader {
208 version: u8,
209 flags: u8,
210 l4_offset: u8,
211 reserved: u8,
212 mss: U16<BigEndian>,
213 vlan_tci: U16<BigEndian>,
214 context_id: U64<BigEndian>,
215 padding: U16<BigEndian>,
216}
217
218impl SttHeader {
219 pub const VLAN_PRESENT: u16 = 0x1000;
221
222 pub const VLAN_ID_MASK: u16 = 0x0FFF;
224
225 pub const PCP_MASK: u16 = 0xE000;
227 pub const PCP_SHIFT: u16 = 13;
228
229 pub const HEADER_SIZE: usize = 18;
231
232 #[allow(unused)]
233 const NAME: &'static str = "SttHeader";
234
235 #[inline]
237 pub fn version(&self) -> u8 {
238 self.version
239 }
240
241 #[inline]
243 pub fn flags_raw(&self) -> u8 {
244 self.flags
245 }
246
247 #[inline]
249 pub fn flags(&self) -> SttFlags {
250 SttFlags::new(self.flags)
251 }
252
253 #[inline]
258 pub fn l4_offset(&self) -> u8 {
259 self.l4_offset
260 }
261
262 #[inline]
264 pub fn l4_offset_bytes(&self) -> usize {
265 self.l4_offset as usize * 2
266 }
267
268 #[inline]
270 pub fn mss(&self) -> u16 {
271 self.mss.get()
272 }
273
274 #[inline]
276 pub fn vlan_tci_raw(&self) -> u16 {
277 self.vlan_tci.get()
278 }
279
280 #[inline]
282 pub fn has_vlan(&self) -> bool {
283 self.vlan_tci.get() & Self::VLAN_PRESENT != 0
284 }
285
286 #[inline]
288 pub fn vlan_id(&self) -> Option<u16> {
289 if self.has_vlan() {
290 Some(self.vlan_tci.get() & Self::VLAN_ID_MASK)
291 } else {
292 None
293 }
294 }
295
296 #[inline]
298 pub fn pcp(&self) -> u8 {
299 ((self.vlan_tci.get() & Self::PCP_MASK) >> Self::PCP_SHIFT) as u8
300 }
301
302 #[inline]
304 pub fn context_id(&self) -> u64 {
305 self.context_id.get()
306 }
307
308 #[inline]
310 fn is_valid(&self) -> bool {
311 self.version == STT_VERSION
313 }
314}
315
316impl PacketHeader for SttHeader {
317 const NAME: &'static str = "SttHeader";
318 type InnerType = u64;
320
321 #[inline]
322 fn inner_type(&self) -> Self::InnerType {
323 self.context_id()
324 }
325
326 #[inline]
328 fn total_len(&self, _buf: &[u8]) -> usize {
329 Self::HEADER_SIZE
330 }
331
332 #[inline]
334 fn is_valid(&self) -> bool {
335 self.is_valid()
336 }
337}
338
339impl HeaderParser for SttHeader {
340 type Output<'a> = &'a SttHeader;
341
342 #[inline]
343 fn into_view<'a>(header: &'a Self, _options: &'a [u8]) -> Self::Output<'a> {
344 header
345 }
346}
347
348impl fmt::Display for SttHeader {
349 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
350 write!(
351 f,
352 "STT v{} ctx=0x{:016x} mss={} flags=[{}]",
353 self.version(),
354 self.context_id(),
355 self.mss(),
356 self.flags()
357 )?;
358
359 if let Some(vid) = self.vlan_id() {
360 write!(f, " vlan={}", vid)?;
361 }
362
363 Ok(())
364 }
365}
366
367#[repr(C, packed)]
372#[derive(
373 FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, zerocopy::KnownLayout, zerocopy::Immutable,
374)]
375pub struct SttTcpHeader {
376 src_port: U16<BigEndian>,
377 dst_port: U16<BigEndian>,
378 seq: [u8; 4], ack: [u8; 4], data_offset_flags: U16<BigEndian>,
381 window: U16<BigEndian>,
382 checksum: U16<BigEndian>,
383 urgent: U16<BigEndian>,
384}
385
386impl SttTcpHeader {
387 pub const HEADER_SIZE: usize = 20;
389
390 #[inline]
392 pub fn src_port(&self) -> u16 {
393 self.src_port.get()
394 }
395
396 #[inline]
398 pub fn dst_port(&self) -> u16 {
399 self.dst_port.get()
400 }
401
402 #[inline]
406 pub fn sequence(&self) -> u32 {
407 u32::from_be_bytes(self.seq)
408 }
409
410 #[inline]
414 pub fn acknowledgment(&self) -> u32 {
415 u32::from_be_bytes(self.ack)
416 }
417
418 #[inline]
420 pub fn data_offset(&self) -> u8 {
421 ((self.data_offset_flags.get() >> 12) & 0x0F) as u8
422 }
423
424 #[inline]
426 pub fn data_offset_bytes(&self) -> usize {
427 self.data_offset() as usize * 4
428 }
429
430 #[inline]
432 pub fn tcp_flags(&self) -> u8 {
433 (self.data_offset_flags.get() & 0x003F) as u8
434 }
435
436 #[inline]
438 pub fn window(&self) -> u16 {
439 self.window.get()
440 }
441
442 #[inline]
444 pub fn checksum(&self) -> u16 {
445 self.checksum.get()
446 }
447
448 #[inline]
450 pub fn urgent(&self) -> u16 {
451 self.urgent.get()
452 }
453
454 #[inline]
456 pub fn is_stt(&self) -> bool {
457 self.dst_port() == STT_PORT
458 }
459
460 #[inline]
465 pub fn fragment_offset(&self) -> u32 {
466 self.sequence()
467 }
468
469 #[inline]
473 pub fn total_length(&self) -> u16 {
474 (self.acknowledgment() >> 16) as u16
475 }
476
477 #[inline]
481 pub fn fragment_id(&self) -> u16 {
482 (self.acknowledgment() & 0xFFFF) as u16
483 }
484}
485
486impl PacketHeader for SttTcpHeader {
487 const NAME: &'static str = "SttTcpHeader";
488 type InnerType = u16;
489
490 #[inline]
491 fn inner_type(&self) -> Self::InnerType {
492 self.dst_port()
493 }
494
495 #[inline]
496 fn total_len(&self, _buf: &[u8]) -> usize {
497 Self::HEADER_SIZE
498 }
499
500 #[inline]
501 fn is_valid(&self) -> bool {
502 self.data_offset() >= 5
504 }
505}
506
507impl HeaderParser for SttTcpHeader {
508 type Output<'a> = &'a SttTcpHeader;
509
510 #[inline]
511 fn into_view<'a>(header: &'a Self, _options: &'a [u8]) -> Self::Output<'a> {
512 header
513 }
514}
515
516impl fmt::Display for SttTcpHeader {
517 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
518 write!(
519 f,
520 "STT-TCP {}:{} frag_id={} frag_off={} total_len={}",
521 self.src_port(),
522 self.dst_port(),
523 self.fragment_id(),
524 self.fragment_offset(),
525 self.total_length()
526 )
527 }
528}
529
530#[derive(Debug, Clone)]
532pub struct SttPacket<'a> {
533 pub tcp_header: &'a SttTcpHeader,
535 pub stt_header: &'a SttHeader,
537 pub payload: &'a [u8],
539}
540
541impl<'a> SttPacket<'a> {
542 pub fn parse(buf: &'a [u8]) -> Option<Self> {
547 if buf.len() < SttTcpHeader::HEADER_SIZE + SttHeader::HEADER_SIZE {
548 return None;
549 }
550
551 let (tcp_ref, rest) = zerocopy::Ref::<_, SttTcpHeader>::from_prefix(buf).ok()?;
552 let tcp_header = zerocopy::Ref::into_ref(tcp_ref);
553
554 let (stt_ref, payload) = zerocopy::Ref::<_, SttHeader>::from_prefix(rest).ok()?;
555 let stt_header = zerocopy::Ref::into_ref(stt_ref);
556
557 if stt_header.version() != STT_VERSION {
559 return None;
560 }
561
562 Some(SttPacket {
563 tcp_header,
564 stt_header,
565 payload,
566 })
567 }
568
569 #[inline]
571 pub fn context_id(&self) -> u64 {
572 self.stt_header.context_id()
573 }
574
575 #[inline]
577 pub fn vlan_id(&self) -> Option<u16> {
578 self.stt_header.vlan_id()
579 }
580
581 #[inline]
583 pub fn is_fragmented(&self) -> bool {
584 self.tcp_header.fragment_offset() != 0
585 || self.payload.len() < self.tcp_header.total_length() as usize
586 }
587
588 #[inline]
590 pub fn header_length(&self) -> usize {
591 SttTcpHeader::HEADER_SIZE + SttHeader::HEADER_SIZE
592 }
593}
594
595impl fmt::Display for SttPacket<'_> {
596 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
597 write!(
598 f,
599 "STT ctx=0x{:016x} mss={} frag_id={}",
600 self.stt_header.context_id(),
601 self.stt_header.mss(),
602 self.tcp_header.fragment_id()
603 )?;
604
605 if let Some(vid) = self.vlan_id() {
606 write!(f, " vlan={}", vid)?;
607 }
608
609 if self.is_fragmented() {
610 write!(
611 f,
612 " frag_off={} total={}",
613 self.tcp_header.fragment_offset(),
614 self.tcp_header.total_length()
615 )?;
616 }
617
618 Ok(())
619 }
620}
621
622#[cfg(test)]
623mod tests {
624 use super::*;
625
626 #[test]
627 fn test_stt_header_size() {
628 assert_eq!(std::mem::size_of::<SttHeader>(), 18);
629 assert_eq!(SttHeader::FIXED_LEN, 18);
630 assert_eq!(std::mem::size_of::<SttTcpHeader>(), 20);
631 assert_eq!(SttTcpHeader::FIXED_LEN, 20);
632 }
633
634 #[test]
635 fn test_stt_header_basic() {
636 let packet = vec![
637 0x00, 0x00, 0x00, 0x00, 0x05, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
647 ];
648
649 let (header, payload) = SttHeader::from_bytes(&packet).unwrap();
650 assert_eq!(header.version(), 0);
651 assert_eq!(header.flags_raw(), 0);
652 assert_eq!(header.l4_offset(), 0);
653 assert_eq!(header.mss(), 1500);
654 assert!(!header.has_vlan());
655 assert_eq!(header.vlan_id(), None);
656 assert_eq!(header.context_id(), 1);
657 assert_eq!(payload.len(), 4);
658 }
659
660 #[test]
661 fn test_stt_header_with_vlan() {
662 let packet = vec![
663 0x00, 0x00, 0x00, 0x00, 0x05, 0xDC, 0x10, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, ];
672
673 let (header, _) = SttHeader::from_bytes(&packet).unwrap();
674 assert!(header.has_vlan());
675 assert_eq!(header.vlan_id(), Some(100));
676 assert_eq!(header.context_id(), 10);
677 }
678
679 #[test]
680 fn test_stt_header_with_pcp() {
681 let packet = vec![
682 0x00, 0x00, 0x00, 0x00, 0x05, 0xDC, 0xF0, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
684 ];
685
686 let (header, _) = SttHeader::from_bytes(&packet).unwrap();
687 assert_eq!(header.pcp(), 7);
688 assert!(header.has_vlan());
689 assert_eq!(header.vlan_id(), Some(100));
690 }
691
692 #[test]
693 fn test_stt_header_flags() {
694 let packet = vec![
695 0x00, 0x1F, 0x00, 0x00, 0x05, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
697 0x00, 0x00,
698 ];
699
700 let (header, _) = SttHeader::from_bytes(&packet).unwrap();
701 let flags = header.flags();
702 assert!(flags.is_csum_verified());
703 assert!(flags.is_csum_partial());
704 assert!(flags.is_ipv4());
705 assert!(flags.is_tcp());
706 assert!(flags.is_csum_present());
707 }
708
709 #[test]
710 fn test_stt_header_l4_offset() {
711 let packet = vec![
712 0x00, 0x00, 0x0A, 0x00, 0x05, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
714 0x00,
715 ];
716
717 let (header, _) = SttHeader::from_bytes(&packet).unwrap();
718 assert_eq!(header.l4_offset(), 10);
719 assert_eq!(header.l4_offset_bytes(), 20);
720 }
721
722 #[test]
723 fn test_stt_header_large_context_id() {
724 let packet = vec![
725 0x00, 0x00, 0x00, 0x00, 0x05, 0xDC, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
726 0xFF, 0xFF, 0x00, 0x00,
728 ];
729
730 let (header, _) = SttHeader::from_bytes(&packet).unwrap();
731 assert_eq!(header.context_id(), u64::MAX);
732 }
733
734 #[test]
735 fn test_stt_header_invalid_version() {
736 let packet = vec![
737 0x01, 0x00, 0x00, 0x00, 0x05, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
739 0x01, 0x00, 0x00,
740 ];
741
742 let result = SttHeader::from_bytes(&packet);
743 assert!(result.is_err());
744 }
745
746 #[test]
747 fn test_stt_header_too_small() {
748 let packet = vec![0x00, 0x00, 0x00, 0x00]; let result = SttHeader::from_bytes(&packet);
750 assert!(result.is_err());
751 }
752
753 #[test]
754 fn test_stt_header_display() {
755 let packet = vec![
756 0x00, 0x00, 0x00, 0x00, 0x05, 0xDC, 0x10, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
758 ];
759
760 let (header, _) = SttHeader::from_bytes(&packet).unwrap();
761 let display = format!("{}", header);
762 assert!(display.contains("STT"));
763 assert!(display.contains("ctx="));
764 assert!(display.contains("vlan=100"));
765 }
766
767 #[test]
768 fn test_stt_flags_display() {
769 let flags = SttFlags::new(0x1F);
770 let display = format!("{}", flags);
771 assert!(display.contains("CSUM_VERIFIED"));
772 assert!(display.contains("IPV4"));
773 assert!(display.contains("TCP"));
774
775 let empty_flags = SttFlags::new(0);
776 assert_eq!(format!("{}", empty_flags), "none");
777 }
778
779 #[test]
780 fn test_stt_tcp_header_basic() {
781 let packet = vec![
782 0x12, 0x34, 0x1D, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x05, 0xDC, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
791
792 let (header, _) = SttTcpHeader::from_bytes(&packet).unwrap();
793 assert_eq!(header.src_port(), 0x1234);
794 assert_eq!(header.dst_port(), STT_PORT);
795 assert!(header.is_stt());
796 assert_eq!(header.fragment_offset(), 0);
797 assert_eq!(header.total_length(), 1500);
798 assert_eq!(header.fragment_id(), 1);
799 assert_eq!(header.data_offset(), 5);
800 assert_eq!(header.data_offset_bytes(), 20);
801 }
802
803 #[test]
804 fn test_stt_tcp_header_with_fragment() {
805 let packet = vec![
806 0x12, 0x34, 0x1D, 0x2F, 0x00, 0x00, 0x10, 0x00, 0x05, 0xDC, 0x00, 0x02, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
809 ];
810
811 let (header, _) = SttTcpHeader::from_bytes(&packet).unwrap();
812 assert_eq!(header.fragment_offset(), 4096);
813 assert_eq!(header.fragment_id(), 2);
814 assert_eq!(header.total_length(), 1500);
815 }
816
817 #[test]
818 fn test_stt_tcp_header_display() {
819 let packet = vec![
820 0x12, 0x34, 0x1D, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x05, 0xDC, 0x00, 0x01, 0x50, 0x00,
821 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
822 ];
823
824 let (header, _) = SttTcpHeader::from_bytes(&packet).unwrap();
825 let display = format!("{}", header);
826 assert!(display.contains("STT-TCP"));
827 assert!(display.contains("7471"));
828 }
829
830 #[test]
831 fn test_stt_packet_parse() {
832 let packet = vec![
833 0x12, 0x34, 0x1D, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0xDC, 0x10, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ];
855
856 let stt_packet = SttPacket::parse(&packet).unwrap();
857 assert_eq!(stt_packet.context_id(), 10);
858 assert_eq!(stt_packet.vlan_id(), Some(100));
859 assert!(stt_packet.stt_header.flags().is_ipv4());
860 assert_eq!(stt_packet.header_length(), 38);
861 assert_eq!(stt_packet.payload.len(), 12);
862 }
863
864 #[test]
865 fn test_stt_packet_fragmented() {
866 let packet = vec![
867 0x12, 0x34, 0x1D, 0x2F, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
870 0x00, 0x00, 0x00, 0x00, 0x05, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
872 0x00, 0x01, 0x00, 0x00,
873 ];
874
875 let stt_packet = SttPacket::parse(&packet).unwrap();
876 assert!(stt_packet.is_fragmented());
877 }
878
879 #[test]
880 fn test_stt_packet_too_small() {
881 let packet = vec![0x00; 30]; let result = SttPacket::parse(&packet);
883 assert!(result.is_none());
884 }
885
886 #[test]
887 fn test_stt_packet_display() {
888 let packet = vec![
889 0x12, 0x34, 0x1D, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x01, 0x50, 0x00,
890 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xDC, 0x10, 0x64,
891 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00,
892 ];
893
894 let stt_packet = SttPacket::parse(&packet).unwrap();
895 let display = format!("{}", stt_packet);
896 assert!(display.contains("STT"));
897 assert!(display.contains("ctx="));
898 assert!(display.contains("vlan=100"));
899 }
900
901 #[test]
902 fn test_stt_port() {
903 assert!(is_stt_port(7471));
904 assert!(!is_stt_port(7472));
905 assert!(!is_stt_port(4789));
906 }
907
908 #[test]
909 fn test_stt_inner_type() {
910 let packet = vec![
911 0x00, 0x00, 0x00, 0x00, 0x05, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34,
912 0x56, 0x78, 0x00, 0x00,
914 ];
915
916 let (header, _) = SttHeader::from_bytes(&packet).unwrap();
917 assert_eq!(header.inner_type(), 0x12345678);
918 }
919}