Skip to main content

iris_core/conntrack/
conn_id.rs

1//! Bidirectional connection identifiers.
2//!
3//! Provides endpoint-specific (distinguishes originator and responder) and generic identifiers for bi-directional connections.
4//! Iris defines a "connection" by five tuple (source/destination addresses, ports, and transport protocol).
5
6use crate::conntrack::L4Context;
7
8use crate::protocols::packet::tcp::TCP_PROTOCOL;
9use crate::protocols::packet::udp::UDP_PROTOCOL;
10use std::cmp;
11use std::fmt;
12use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddr::V4, SocketAddr::V6};
13
14use serde::Serialize;
15
16/// Connection 5-tuple.
17///
18/// The sender of the first observed packet in the connection becomes the originator `orig`, and the
19/// recipient becomes the responder `resp`.
20#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize)]
21pub struct FiveTuple {
22    /// The originator connection endpoint.
23    pub orig: SocketAddr,
24    /// The responder connection endpoint.
25    pub resp: SocketAddr,
26    /// The layer-4 protocol.
27    pub proto: usize,
28}
29
30impl FiveTuple {
31    /// Creates a new 5-tuple from `ctxt`.
32    pub fn from_ctxt(ctxt: &L4Context) -> Self {
33        FiveTuple {
34            orig: ctxt.src,
35            resp: ctxt.dst,
36            proto: ctxt.proto,
37        }
38    }
39
40    /// Converts a 5-tuple to a non-directional connection identifier.
41    pub fn conn_id(&self) -> ConnId {
42        ConnId::new(self.orig, self.resp, self.proto)
43    }
44
45    /// Utility for returning a string representation of the dst. subnet
46    /// /24 for IPv4, /64 for IPv6; no mask for broadcast
47    pub fn dst_subnet_str(&self) -> String {
48        if let V4(_) = self.orig {
49            if let V4(dst) = self.resp {
50                if dst.ip().is_broadcast() || dst.ip().is_multicast() {
51                    return dst.ip().to_string();
52                } else {
53                    let mask = !0u32 << (32 - 24); // Convert to a /24
54                    return Ipv4Addr::from(dst.ip().to_bits() & mask).to_string();
55                }
56            }
57        } else if let V6(_) = self.orig {
58            if let V6(dst) = self.resp {
59                let mask = !0u128 << (128 - 64); // Convert to a /64
60                return Ipv6Addr::from(dst.ip().to_bits() & mask).to_string();
61            }
62        }
63        String::new()
64    }
65
66    /// Utility for returning a string representation of the dst. IP
67    pub fn dst_ip_str(&self) -> String {
68        if let V4(dst) = self.resp {
69            return dst.ip().to_string();
70        }
71        if let V6(dst) = self.resp {
72            return dst.ip().to_string();
73        }
74        String::new()
75    }
76
77    pub fn src_ip_str(&self) -> String {
78        if let V4(src) = self.orig {
79            return src.ip().to_string();
80        } else if let V6(src) = self.orig {
81            return src.ip().to_string();
82        }
83        String::new()
84    }
85
86    /// Utility for returning a string representation of the transport
87    /// protocol and source/destination ports
88    pub fn transp_proto_str(&self) -> String {
89        let src_port = self.orig.port();
90        let dst_port = self.resp.port();
91        let proto = match self.proto {
92            UDP_PROTOCOL => "udp",
93            TCP_PROTOCOL => "tcp",
94            _ => "none",
95        };
96        format!(
97            "{{ \"proto\": \"{}\", \"src\": \"{}\", \"dst\": \"{}\" }}",
98            proto, src_port, dst_port
99        )
100    }
101}
102
103impl fmt::Display for FiveTuple {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        write!(f, "{} -> ", self.orig)?;
106        write!(f, "{}", self.resp)?;
107        write!(f, " protocol {}", self.proto)?;
108        Ok(())
109    }
110}
111
112/// A generic connection identifier.
113///
114/// Identifies a connection independent of the source and destination socket address order. Does not
115/// distinguish between the originator and responder of the connection.
116#[derive(Debug, Clone, Hash, Eq, PartialEq)]
117pub struct ConnId(SocketAddr, SocketAddr, usize);
118
119impl ConnId {
120    /// Returns the connection ID of a packet with `src` and `dst` IP/port pairs.
121    pub(super) fn new(src: SocketAddr, dst: SocketAddr, protocol: usize) -> Self {
122        ConnId(cmp::max(src, dst), cmp::min(src, dst), protocol)
123    }
124}
125
126impl fmt::Display for ConnId {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        write!(f, "{} <> ", self.0)?;
129        write!(f, "{}", self.1)?;
130        write!(f, " protocol {}", self.2)?;
131        Ok(())
132    }
133}