use smallvec::SmallVec;
#[derive(Debug, Clone)]
pub struct ProtocolExpectation {
pub tcp_allowed_ports: SmallVec<[u16; 16]>,
pub udp_allowed_ports: SmallVec<[u16; 16]>,
pub drop_stray_tcp: bool,
pub drop_stray_udp: bool,
pub allowed_protocols: u64,
pub fragment_policy: u8,
pub min_ttl: u8,
}
impl Default for ProtocolExpectation {
fn default() -> Self {
Self {
tcp_allowed_ports: SmallVec::new(),
udp_allowed_ports: SmallVec::new(),
drop_stray_tcp: false,
drop_stray_udp: false,
allowed_protocols: zenith_foundation::net::DEFAULT_PROTO_WHITELIST,
fragment_policy: 0,
min_ttl: 0,
}
}
}
impl ProtocolExpectation {
pub fn is_packet_expected(&self, proto: u8, dst_port: u16, ttl: u8, is_fragment: bool) -> bool {
if proto < 64 {
let bit = 1u64 << proto;
if (self.allowed_protocols & bit) == 0 {
return false;
}
} else {
return false;
}
if !self.tcp_allowed_ports.is_empty() && proto == 6 && !self.tcp_allowed_ports.contains(&dst_port) {
return false;
}
if !self.udp_allowed_ports.is_empty() && proto == 17 && !self.udp_allowed_ports.contains(&dst_port) {
return false;
}
if self.min_ttl > 0 && ttl < self.min_ttl {
return false;
}
if is_fragment && (self.fragment_policy == 1 || self.fragment_policy == 2) {
return false;
}
true
}
pub fn validate(&self) -> std::result::Result<(), crate::NetError> {
if self.fragment_policy == 1 {
return Err(crate::NetError::InvalidOperation {
reason: "fragment_policy=1 (丢弃重叠分片) 未实现,fail-closed 拒绝。\
请使用 0(不限制)或 2(丢弃所有分片)"
.to_string(),
});
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_allows_tcp_udp() {
let exp = ProtocolExpectation::default();
assert!(exp.is_packet_expected(6, 443, 64, false));
assert!(exp.is_packet_expected(17, 53, 64, false));
assert!(exp.is_packet_expected(1, 0, 64, false));
}
#[test]
fn test_tcp_port_whitelist() {
let mut exp = ProtocolExpectation::default();
exp.tcp_allowed_ports.push(80);
exp.tcp_allowed_ports.push(443);
assert!(exp.is_packet_expected(6, 80, 64, false));
assert!(exp.is_packet_expected(6, 443, 64, false));
assert!(!exp.is_packet_expected(6, 8080, 64, false));
}
#[test]
fn test_udp_port_whitelist() {
let mut exp = ProtocolExpectation::default();
exp.udp_allowed_ports.push(53);
assert!(exp.is_packet_expected(17, 53, 64, false));
assert!(!exp.is_packet_expected(17, 123, 64, false));
}
#[test]
fn test_min_ttl() {
let mut exp = ProtocolExpectation::default();
exp.min_ttl = 10;
assert!(exp.is_packet_expected(6, 80, 64, false));
assert!(exp.is_packet_expected(6, 80, 10, false));
assert!(!exp.is_packet_expected(6, 80, 5, false));
}
#[test]
fn test_fragment_policy_drop_all() {
let mut exp = ProtocolExpectation::default();
exp.fragment_policy = 2;
assert!(exp.is_packet_expected(6, 80, 64, false));
assert!(!exp.is_packet_expected(6, 80, 64, true));
}
#[test]
fn test_fragment_policy_allow() {
let exp = ProtocolExpectation::default();
assert!(exp.is_packet_expected(6, 80, 64, true));
assert!(exp.is_packet_expected(6, 80, 64, false));
}
#[test]
fn test_protocol_not_in_bitmap() {
let exp = ProtocolExpectation::default();
assert!(!exp.is_packet_expected(2, 0, 64, false));
assert!(!exp.is_packet_expected(47, 0, 64, false));
}
#[test]
fn test_empty_port_lists_allow_all() {
let exp = ProtocolExpectation::default();
assert!(exp.is_packet_expected(6, 1, 64, false));
assert!(exp.is_packet_expected(6, 65535, 64, false));
assert!(exp.is_packet_expected(17, 1, 64, false));
assert!(exp.is_packet_expected(17, 65535, 64, false));
}
#[test]
fn test_fragment_policy_1_fail_closed_runtime() {
let mut exp = ProtocolExpectation::default();
exp.fragment_policy = 1;
assert!(exp.is_packet_expected(6, 80, 64, false));
assert!(!exp.is_packet_expected(6, 80, 64, true));
}
#[test]
fn test_fragment_policy_1_rejected_by_validate() {
let mut exp = ProtocolExpectation::default();
exp.fragment_policy = 1;
assert!(exp.validate().is_err());
exp.fragment_policy = 0;
assert!(exp.validate().is_ok());
exp.fragment_policy = 2;
assert!(exp.validate().is_ok());
}
}