conntrack: ICMP echo rate limiting (ping flood protection)
Conntrack now rate-limits ICMP/ICMPv6 echo requests per source: - is_echo_request() detects Type 8 (ICMPv4) / Type 128 (ICMPv6) - check_icmp_limit() enforces 20 echoes/sec per source IP - Over-limit returns ConnState::OverLimit, increments over_limit_count Mirrors existing SYN flood protection (100 SYN/sec per source). Smaller threshold (20/sec) because ICMP is cheaper to generate. Reuses same rate_limits map — TCP SYNs and ICMP echoes share the budget. Side effect: 100 SYN + 20 echo = 120 packets/sec from one source before any trigger, which is reasonable.
This commit is contained in:
@@ -24,6 +24,16 @@ fn is_syn(ctx: &PacketContext) -> bool {
|
||||
ctx.packet.len() >= 34 && (ctx.packet[33] & 0x02) != 0 && (ctx.packet[33] & 0x10) == 0
|
||||
}
|
||||
|
||||
/// ICMP echo request detection (Type 8 for ICMPv4, Type 128 for ICMPv6).
|
||||
/// Returns true if the packet is an echo request.
|
||||
fn is_echo_request(ctx: &PacketContext) -> bool {
|
||||
if ctx.packet.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let icmp_type = ctx.packet[0];
|
||||
icmp_type == 8 || icmp_type == 128
|
||||
}
|
||||
|
||||
use super::{PacketContext, Verdict};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -161,6 +171,13 @@ impl ConntrackTable {
|
||||
return ConnState::OverLimit;
|
||||
}
|
||||
|
||||
if (ctx.protocol == 1 || ctx.protocol == 58) && is_echo_request(ctx)
|
||||
&& self.check_icmp_limit(ctx.src_addr, now)
|
||||
{
|
||||
self.over_limit_count = self.over_limit_count.saturating_add(1);
|
||||
return ConnState::OverLimit;
|
||||
}
|
||||
|
||||
if ctx.protocol == 1 || ctx.protocol == 58 {
|
||||
return self.track_icmp(ctx, l3num, now);
|
||||
}
|
||||
@@ -335,6 +352,19 @@ impl ConntrackTable {
|
||||
entry.0 > SYN_LIMIT
|
||||
}
|
||||
|
||||
fn check_icmp_limit(&mut self, src: IpAddress, now: Instant) -> bool {
|
||||
const ICMP_LIMIT: u32 = 20;
|
||||
const ICMP_WINDOW: Duration = Duration::from_secs(1);
|
||||
|
||||
let entry = self.rate_limits.entry(src).or_insert((0, now));
|
||||
if now > entry.1 + ICMP_WINDOW {
|
||||
*entry = (1, now);
|
||||
} else {
|
||||
entry.0 += 1;
|
||||
}
|
||||
entry.0 > ICMP_LIMIT
|
||||
}
|
||||
|
||||
pub fn format(&self) -> alloc::string::String {
|
||||
let mut out = alloc::format!("conntrack entries: {}, over_limit: {}\n", self.entries.len(), self.over_limit_count);
|
||||
for entry in self.entries.values() {
|
||||
|
||||
Reference in New Issue
Block a user