From a4f4c29a8a7153b16a769b199acb5c6a7f4a3e0e Mon Sep 17 00:00:00 2001 From: reverse Date: Wed, 19 Aug 2026 00:34:53 +0000 Subject: [PATCH] =?UTF-8?q?connectban:=20clamp=20the=20IPv6=20keep-hextets?= =?UTF-8?q?=20to=20>=3D1=20=E2=80=94=20connectban=5Fipv6cidr=20between=201?= =?UTF-8?q?=20and=2015=20made=20keep=3D=3D0,=20so=20the=20z-line=20glob=20?= =?UTF-8?q?became=20"*"=20and=20banned=20every=20IPv6=20address=20on=20the?= =?UTF-8?q?=20internet=20(and=20bucketed=20all=20v6=20clients=20into=20one?= =?UTF-8?q?=20key);=20mirrors=20the=20v4=20clamp(1,4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/connectban.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/connectban.rs b/src/modules/connectban.rs index 6db86df..db6b780 100644 --- a/src/modules/connectban.rs +++ b/src/modules/connectban.rs @@ -49,7 +49,9 @@ fn range_of(ip: IpAddr, v4cidr: u8, v6cidr: u8) -> (String, String) { } IpAddr::V6(a) => { let segs = a.segments(); - let keep = (v6cidr / 16).min(8) as usize; + // keep at least one hextet, so a sub-/16 config can't collapse the ban + // mask to "*" and z-line every IPv6 address (mirrors the v4 clamp above). + let keep = (v6cidr / 16).clamp(1, 8) as usize; if keep >= 8 { (format!("v6:{}", a), a.to_string()) } else { @@ -169,5 +171,8 @@ mod tests { let ip: IpAddr = "2001:db8::1".parse().unwrap(); assert_eq!(range_of(ip, 32, 128).1, "2001:db8::1"); assert_eq!(range_of(ip, 32, 32).1, "2001:db8:*"); + // a sub-/16 v6 prefix must keep at least one hextet, never collapse to "*" + assert_eq!(range_of(ip, 32, 8).1, "2001:*"); + assert_ne!(range_of(ip, 32, 1).1, "*"); } }