1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
2
3pub fn is_private_ip(ip: IpAddr) -> bool {
6 match ip {
7 IpAddr::V4(v4) => is_private_ipv4(v4),
8 IpAddr::V6(v6) => is_private_ipv6(v6),
9 }
10}
11
12fn is_private_ipv4(ip: Ipv4Addr) -> bool {
13 let octets = ip.octets();
14
15 if ip.is_unspecified() {
16 return true;
17 }
18 if ip.is_loopback() {
19 return true;
20 }
21 if octets[0] == 10 {
23 return true;
24 }
25 if octets[0] == 172 && (16..=31).contains(&octets[1]) {
27 return true;
28 }
29 if octets[0] == 192 && octets[1] == 168 {
31 return true;
32 }
33 if octets[0] == 169 && octets[1] == 254 {
35 return true;
36 }
37 if ip.is_broadcast() {
38 return true;
39 }
40 if (octets[0] == 192 && octets[1] == 0 && octets[2] == 2)
42 || (octets[0] == 198 && octets[1] == 51 && octets[2] == 100)
43 || (octets[0] == 203 && octets[1] == 0 && octets[2] == 113)
44 {
45 return true;
46 }
47 if octets[0] == 100 && (64..=127).contains(&octets[1]) {
49 return true;
50 }
51 if octets[0] >= 224 && octets[0] <= 239 {
53 return true;
54 }
55 if octets[0] >= 240 {
57 return true;
58 }
59 false
60}
61
62fn is_private_ipv6(ip: Ipv6Addr) -> bool {
63 if ip.is_loopback() {
64 return true;
65 }
66 if ip.is_unspecified() {
67 return true;
68 }
69
70 let segments = ip.segments();
71
72 if segments[0] & 0xffc0 == 0xfe80 {
74 return true;
75 }
76 if segments[0] & 0xfe00 == 0xfc00 {
78 return true;
79 }
80 if segments[0] & 0xff00 == 0xff00 {
82 return true;
83 }
84
85 if let Some(v4) = ip.to_ipv4_mapped() {
86 return is_private_ipv4(v4);
87 }
88
89 false
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn loopback_v4() {
98 assert!(is_private_ip("127.0.0.1".parse().unwrap()));
99 assert!(is_private_ip("127.255.255.255".parse().unwrap()));
100 }
101
102 #[test]
103 fn private_ranges_v4() {
104 assert!(is_private_ip("10.0.0.1".parse().unwrap()));
105 assert!(is_private_ip("10.255.255.255".parse().unwrap()));
106 assert!(is_private_ip("172.16.0.1".parse().unwrap()));
107 assert!(is_private_ip("172.31.255.255".parse().unwrap()));
108 assert!(is_private_ip("192.168.0.1".parse().unwrap()));
109 assert!(is_private_ip("192.168.255.255".parse().unwrap()));
110 }
111
112 #[test]
113 fn link_local_v4() {
114 assert!(is_private_ip("169.254.0.1".parse().unwrap()));
115 assert!(is_private_ip("169.254.169.254".parse().unwrap())); }
117
118 #[test]
119 fn unspecified_and_broadcast() {
120 assert!(is_private_ip("0.0.0.0".parse().unwrap()));
121 assert!(is_private_ip("255.255.255.255".parse().unwrap()));
122 }
123
124 #[test]
125 fn multicast_v4() {
126 assert!(is_private_ip("224.0.0.1".parse().unwrap()));
127 assert!(is_private_ip("239.255.255.255".parse().unwrap()));
128 }
129
130 #[test]
131 fn reserved_v4() {
132 assert!(is_private_ip("240.0.0.1".parse().unwrap()));
133 assert!(is_private_ip("100.64.0.1".parse().unwrap()));
134 }
135
136 #[test]
137 fn public_v4_allowed() {
138 assert!(!is_private_ip("8.8.8.8".parse().unwrap()));
139 assert!(!is_private_ip("1.1.1.1".parse().unwrap()));
140 assert!(!is_private_ip("93.184.216.34".parse().unwrap()));
141 }
142
143 #[test]
144 fn loopback_v6() {
145 assert!(is_private_ip("::1".parse().unwrap()));
146 }
147
148 #[test]
149 fn unspecified_v6() {
150 assert!(is_private_ip("::".parse().unwrap()));
151 }
152
153 #[test]
154 fn link_local_v6() {
155 assert!(is_private_ip("fe80::1".parse().unwrap()));
156 }
157
158 #[test]
159 fn unique_local_v6() {
160 assert!(is_private_ip("fc00::1".parse().unwrap()));
161 assert!(is_private_ip("fd00::1".parse().unwrap()));
162 }
163
164 #[test]
165 fn ec2_metadata_v6() {
166 assert!(is_private_ip("fd00:ec2::254".parse().unwrap()));
167 }
168
169 #[test]
170 fn multicast_v6() {
171 assert!(is_private_ip("ff02::1".parse().unwrap()));
172 }
173
174 #[test]
175 fn ipv4_mapped_v6_private() {
176 assert!(is_private_ip("::ffff:127.0.0.1".parse().unwrap()));
177 assert!(is_private_ip("::ffff:10.0.0.1".parse().unwrap()));
178 assert!(is_private_ip("::ffff:192.168.1.1".parse().unwrap()));
179 assert!(is_private_ip("::ffff:169.254.169.254".parse().unwrap()));
180 }
181
182 #[test]
183 fn ipv4_mapped_v6_public() {
184 assert!(!is_private_ip("::ffff:8.8.8.8".parse().unwrap()));
185 }
186
187 #[test]
188 fn public_v6_allowed() {
189 assert!(!is_private_ip("2607:f8b0:4004:800::200e".parse().unwrap()));
190 }
191}