1use std::net::IpAddr;
7
8#[must_use]
18pub fn is_private_ip(addr: IpAddr) -> bool {
19 match addr {
20 IpAddr::V4(ip) => {
21 let n = u32::from(ip);
22 ip.is_loopback()
23 || ip.is_private()
24 || ip.is_link_local()
25 || ip.is_unspecified()
26 || ip.is_broadcast()
27 || (n & 0xFFC0_0000 == 0x6440_0000)
29 }
30 IpAddr::V6(ip) => {
31 ip.is_loopback()
32 || ip.is_unspecified()
33 || ip.to_ipv4_mapped().is_some_and(|v4| {
34 let n = u32::from(v4);
35 v4.is_loopback()
36 || v4.is_private()
37 || v4.is_link_local()
38 || v4.is_unspecified()
39 || v4.is_broadcast()
40 || (n & 0xFFC0_0000 == 0x6440_0000)
41 })
42 || (ip.segments()[0] & 0xfe00) == 0xfc00 || (ip.segments()[0] & 0xffc0) == 0xfe80 }
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use std::net::{Ipv4Addr, Ipv6Addr};
52
53 #[test]
54 fn loopback_is_private() {
55 assert!(is_private_ip(IpAddr::V4(Ipv4Addr::LOCALHOST)));
56 assert!(is_private_ip(IpAddr::V6(Ipv6Addr::LOCALHOST)));
57 }
58
59 #[test]
60 fn private_ranges() {
61 assert!(is_private_ip("10.0.0.1".parse().unwrap()));
62 assert!(is_private_ip("172.16.0.1".parse().unwrap()));
63 assert!(is_private_ip("192.168.1.1".parse().unwrap()));
64 }
65
66 #[test]
67 fn link_local() {
68 assert!(is_private_ip("169.254.0.1".parse().unwrap()));
69 }
70
71 #[test]
72 fn unspecified() {
73 assert!(is_private_ip("0.0.0.0".parse().unwrap()));
74 assert!(is_private_ip("::".parse().unwrap()));
75 }
76
77 #[test]
78 fn broadcast() {
79 assert!(is_private_ip("255.255.255.255".parse().unwrap()));
80 }
81
82 #[test]
83 fn cgnat() {
84 assert!(is_private_ip("100.64.0.1".parse().unwrap()));
85 assert!(is_private_ip("100.127.255.255".parse().unwrap()));
86 assert!(!is_private_ip("100.128.0.1".parse().unwrap()));
87 }
88
89 #[test]
90 fn public_ipv4() {
91 assert!(!is_private_ip("8.8.8.8".parse().unwrap()));
92 assert!(!is_private_ip("1.1.1.1".parse().unwrap()));
93 assert!(!is_private_ip("93.184.216.34".parse().unwrap()));
94 }
95
96 #[test]
97 fn ipv6_unique_local() {
98 assert!(is_private_ip("fc00::1".parse().unwrap()));
99 assert!(is_private_ip("fd00::1".parse().unwrap()));
100 }
101
102 #[test]
103 fn ipv6_link_local() {
104 assert!(is_private_ip("fe80::1".parse().unwrap()));
105 }
106
107 #[test]
108 fn ipv6_public() {
109 assert!(!is_private_ip("2001:4860:4860::8888".parse().unwrap()));
110 }
111}