Skip to main content

iris_core/conntrack/
pdu.rs

1//! Transport-layer protocol data unit.
2//! Directly exposed to users as a primitive data type.
3
4use crate::memory::mbuf::Mbuf;
5use crate::protocols::packet::ethernet::Ethernet;
6use crate::protocols::packet::ipv4::Ipv4;
7use crate::protocols::packet::ipv6::Ipv6;
8use crate::protocols::packet::tcp::{Tcp, TCP_PROTOCOL};
9use crate::protocols::packet::udp::{Udp, UDP_PROTOCOL};
10use crate::protocols::packet::{Packet, PacketParseError};
11
12use std::time::Instant;
13
14use std::net::{IpAddr, SocketAddr};
15
16/// Transport-layer protocol data unit for stream reassembly and application-layer protocol parsing.
17/// As a primitive Iris data type, this can be reassembled (InL4Stream) or not reassembled (InL4Conn).
18#[derive(Debug, Clone)]
19pub struct L4Pdu {
20    /// Internal packet buffer containing frame data.
21    pub mbuf: Mbuf,
22    /// Transport layer context.
23    pub ctxt: L4Context,
24    /// `true` if segment is in the direction of orig -> resp.
25    pub dir: bool,
26    /// Time observed from timerwheel.
27    pub ts: Instant,
28}
29
30impl L4Pdu {
31    pub(crate) fn new(mbuf: Mbuf, ctxt: L4Context, dir: bool, ts: Instant) -> Self {
32        L4Pdu {
33            mbuf,
34            ctxt,
35            dir,
36            ts,
37        }
38    }
39
40    #[inline]
41    pub fn mbuf_own(self) -> Mbuf {
42        self.mbuf
43    }
44
45    #[inline]
46    pub fn mbuf_ref(&self) -> &Mbuf {
47        &self.mbuf
48    }
49
50    #[inline]
51    pub fn offset(&self) -> usize {
52        self.ctxt.offset
53    }
54
55    #[inline]
56    pub fn app_body_offset(&self) -> Option<usize> {
57        self.ctxt.app_offset
58    }
59
60    #[inline]
61    pub(crate) fn mark_no_payload(&mut self) {
62        self.ctxt.offset = self.mbuf.data_len();
63        self.ctxt.length = 0;
64    }
65
66    #[inline]
67    pub fn length(&self) -> usize {
68        self.ctxt.length
69    }
70
71    #[inline]
72    pub fn seq_no(&self) -> u32 {
73        self.ctxt.seq_no
74    }
75
76    #[inline]
77    pub fn ack_no(&self) -> u32 {
78        self.ctxt.ack_no
79    }
80
81    #[inline]
82    pub fn flags(&self) -> u8 {
83        self.ctxt.flags
84    }
85}
86
87/// Parsed transport-layer context from the packet used for connection tracking.
88#[derive(Debug, Clone, Copy)]
89pub struct L4Context {
90    /// Source socket address.
91    pub src: SocketAddr,
92    /// Destination socket address.
93    pub dst: SocketAddr,
94    /// L4 protocol.
95    pub proto: usize,
96    /// Offset into mbuf where L4 payload begins.
97    /// If this segment is reassembled, this is the offset where
98    /// *new* payload begins. None indicates that no new data.
99    /// If segment has not been reassembled, this is offset after
100    /// TCP header.
101    pub offset: usize,
102    /// Length of the payload in bytes.
103    pub length: usize,
104    /// Raw sequence number of segment.
105    pub seq_no: u32,
106    /// Raw acknowledgment number of segment.
107    pub ack_no: u32,
108    /// TCP flags.
109    pub flags: u8,
110    /// True if packet has been reassembled, with corresponding
111    /// possible updates to `offset`.
112    pub reassembled: bool,
113    /// If segment contains application-layer body, its offset
114    /// into the payload (after `offset`, i.e. L4 headers).
115    /// None indicates no application-layer body.
116    pub app_offset: Option<usize>,
117}
118
119impl L4Context {
120    pub fn new(mbuf: &Mbuf) -> Result<Self, PacketParseError> {
121        if let Ok(eth) = mbuf.parse_to::<Ethernet>() {
122            if let Ok(ipv4) = eth.parse_to::<Ipv4>() {
123                if let Ok(tcp) = ipv4.parse_to::<Tcp>() {
124                    if let Some(payload_size) = (ipv4.total_length() as usize)
125                        .checked_sub(ipv4.header_len() + tcp.header_len())
126                    {
127                        Ok(L4Context {
128                            src: SocketAddr::new(IpAddr::V4(ipv4.src_addr()), tcp.src_port()),
129                            dst: SocketAddr::new(IpAddr::V4(ipv4.dst_addr()), tcp.dst_port()),
130                            proto: TCP_PROTOCOL,
131                            offset: tcp.next_header_offset(),
132                            length: payload_size,
133                            seq_no: tcp.seq_no(),
134                            ack_no: tcp.ack_no(),
135                            flags: tcp.flags(),
136                            reassembled: false,
137                            app_offset: None,
138                        })
139                    } else {
140                        Err(PacketParseError::InvalidRead)
141                    }
142                } else if let Ok(udp) = ipv4.parse_to::<Udp>() {
143                    if let Some(payload_size) = (ipv4.total_length() as usize)
144                        .checked_sub(ipv4.header_len() + udp.header_len())
145                    {
146                        Ok(L4Context {
147                            src: SocketAddr::new(IpAddr::V4(ipv4.src_addr()), udp.src_port()),
148                            dst: SocketAddr::new(IpAddr::V4(ipv4.dst_addr()), udp.dst_port()),
149                            proto: UDP_PROTOCOL,
150                            offset: udp.next_header_offset(),
151                            length: payload_size,
152                            seq_no: 0,
153                            ack_no: 0,
154                            flags: 0,
155                            reassembled: false,
156                            app_offset: None,
157                        })
158                    } else {
159                        Err(PacketParseError::InvalidRead)
160                    }
161                } else {
162                    Err(PacketParseError::InvalidProtocol)
163                }
164            } else if let Ok(ipv6) = eth.parse_to::<Ipv6>() {
165                if let Ok(tcp) = ipv6.parse_to::<Tcp>() {
166                    if let Some(payload_size) =
167                        (ipv6.payload_length() as usize).checked_sub(tcp.header_len())
168                    {
169                        Ok(L4Context {
170                            src: SocketAddr::new(IpAddr::V6(ipv6.src_addr()), tcp.src_port()),
171                            dst: SocketAddr::new(IpAddr::V6(ipv6.dst_addr()), tcp.dst_port()),
172                            proto: TCP_PROTOCOL,
173                            offset: tcp.next_header_offset(),
174                            length: payload_size,
175                            seq_no: tcp.seq_no(),
176                            ack_no: tcp.ack_no(),
177                            flags: tcp.flags(),
178                            reassembled: false,
179                            app_offset: None,
180                        })
181                    } else {
182                        Err(PacketParseError::InvalidRead)
183                    }
184                } else if let Ok(udp) = ipv6.parse_to::<Udp>() {
185                    if let Some(payload_size) =
186                        (ipv6.payload_length() as usize).checked_sub(udp.header_len())
187                    {
188                        Ok(L4Context {
189                            src: SocketAddr::new(IpAddr::V6(ipv6.src_addr()), udp.src_port()),
190                            dst: SocketAddr::new(IpAddr::V6(ipv6.dst_addr()), udp.dst_port()),
191                            proto: UDP_PROTOCOL,
192                            offset: udp.next_header_offset(),
193                            length: payload_size,
194                            seq_no: 0,
195                            ack_no: 0,
196                            flags: 0,
197                            reassembled: false,
198                            app_offset: None,
199                        })
200                    } else {
201                        Err(PacketParseError::InvalidRead)
202                    }
203                } else {
204                    Err(PacketParseError::InvalidProtocol)
205                }
206            } else {
207                Err(PacketParseError::InvalidProtocol)
208            }
209        } else {
210            Err(PacketParseError::InvalidProtocol)
211        }
212    }
213}