http_acl/utils/ip/global_ip.rs
1// Extracted and adapted from https://github.com/libp2p/rust-libp2p/blob/master/core/src/transport/global_only.rs under the MIT license
2//
3// Copyright 2023 Protocol Labs
4//
5// Permission is hereby granted, free of charge, to any person obtaining a
6// copy of this software and associated documentation files (the "Software"),
7// to deal in the Software without restriction, including without limitation
8// the rights to use, copy, modify, merge, publish, distribute, sublicense,
9// and/or sell copies of the Software, and to permit persons to whom the
10// Software is furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21// DEALINGS IN THE SOFTWARE.
22
23use std::net::IpAddr;
24
25/// This module contains an implementation of the `is_global` IPv4 address space.
26///
27/// Credit for this implementation goes to the Rust standard library team.
28///
29/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709)
30mod ipv4_global {
31 use std::net::Ipv4Addr;
32
33 /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112]
34 /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the
35 /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since
36 /// it is obviously not reserved for future use.
37 ///
38 /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112
39 ///
40 /// # Warning
41 ///
42 /// As IANA assigns new addresses, this method will be
43 /// updated. This may result in non-reserved addresses being
44 /// treated as reserved in code that relies on an outdated version
45 /// of this method.
46 #[must_use]
47 #[inline]
48 pub(crate) const fn is_reserved(a: Ipv4Addr) -> bool {
49 a.octets()[0] & 240 == 240 && !a.is_broadcast()
50 }
51
52 /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for
53 /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0`
54 /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`.
55 ///
56 /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544
57 /// [errata 423]: https://www.rfc-editor.org/errata/eid423
58 #[must_use]
59 #[inline]
60 pub(crate) const fn is_benchmarking(a: Ipv4Addr) -> bool {
61 a.octets()[0] == 198 && (a.octets()[1] & 0xfe) == 18
62 }
63
64 /// Returns [`true`] if this address is part of the Shared Address Space defined in
65 /// [IETF RFC 6598] (`100.64.0.0/10`).
66 ///
67 /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598
68 #[must_use]
69 #[inline]
70 pub(crate) const fn is_shared(a: Ipv4Addr) -> bool {
71 a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000)
72 }
73
74 /// Returns [`true`] if this is a private address.
75 ///
76 /// The private address ranges are defined in [IETF RFC 1918] and include:
77 ///
78 /// - `10.0.0.0/8`
79 /// - `172.16.0.0/12`
80 /// - `192.168.0.0/16`
81 ///
82 /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918
83 #[must_use]
84 #[inline]
85 pub(crate) const fn is_private(a: Ipv4Addr) -> bool {
86 match a.octets() {
87 [10, ..] => true,
88 [172, b, ..] if b >= 16 && b <= 31 => true,
89 [192, 168, ..] => true,
90 _ => false,
91 }
92 }
93
94 /// Returns [`true`] if the address appears to be globally reachable
95 /// as specified by the [IANA IPv4 Special-Purpose Address Registry].
96 /// Whether or not an address is practically reachable will depend on your network configuration.
97 ///
98 /// Most IPv4 addresses are globally reachable;
99 /// unless they are specifically defined as *not* globally reachable.
100 ///
101 /// Non-exhaustive list of notable addresses that are not globally reachable:
102 ///
103 /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified))
104 /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private))
105 /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared))
106 /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback))
107 /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local))
108 /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation))
109 /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking))
110 /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved))
111 /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast))
112 ///
113 /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry].
114 ///
115 /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
116 /// [unspecified address]: Ipv4Addr::UNSPECIFIED
117 /// [broadcast address]: Ipv4Addr::BROADCAST
118 #[must_use]
119 #[inline]
120 pub(crate) const fn is_global(a: Ipv4Addr) -> bool {
121 !(a.octets()[0] == 0 // "This network"
122 || is_private(a)
123 || is_shared(a)
124 || a.is_loopback()
125 || a.is_link_local()
126 // addresses reserved for future protocols (`192.0.0.0/24`)
127 ||(a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0)
128 || a.is_documentation()
129 || is_benchmarking(a)
130 || is_reserved(a)
131 || a.is_broadcast())
132 }
133}
134
135/// This module contains an implementation of the `is_global` IPv6 address space.
136///
137/// Credit for this implementation goes to the Rust standard library team.
138///
139/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709)
140mod ipv6_global {
141 use std::net::Ipv6Addr;
142
143 /// Returns `true` if the address is a unicast address with link-local scope,
144 /// as defined in [RFC 4291].
145 ///
146 /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4].
147 /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6],
148 /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format:
149 ///
150 /// ```text
151 /// | 10 bits | 54 bits | 64 bits |
152 /// +----------+-------------------------+----------------------------+
153 /// |1111111010| 0 | interface ID |
154 /// +----------+-------------------------+----------------------------+
155 /// ```
156 /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`,
157 /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated,
158 /// and those addresses will have link-local scope.
159 ///
160 /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope",
161 /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it.
162 ///
163 /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
164 /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4
165 /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
166 /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6
167 /// [loopback address]: Ipv6Addr::LOCALhost
168 #[must_use]
169 #[inline]
170 pub(crate) const fn is_unicast_link_local(a: Ipv6Addr) -> bool {
171 (a.segments()[0] & 0xffc0) == 0xfe80
172 }
173
174 /// Returns [`true`] if this is a unique local address (`fc00::/7`).
175 ///
176 /// This property is defined in [IETF RFC 4193].
177 ///
178 /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193
179 #[must_use]
180 #[inline]
181 pub(crate) const fn is_unique_local(a: Ipv6Addr) -> bool {
182 (a.segments()[0] & 0xfe00) == 0xfc00
183 }
184
185 /// Returns [`true`] if this is an address reserved for documentation
186 /// (`2001:db8::/32`).
187 ///
188 /// This property is defined in [IETF RFC 3849].
189 ///
190 /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849
191 #[must_use]
192 #[inline]
193 pub(crate) const fn is_documentation(a: Ipv6Addr) -> bool {
194 (a.segments()[0] == 0x2001) && (a.segments()[1] == 0xdb8)
195 }
196
197 /// Returns [`true`] if the address appears to be globally reachable
198 /// as specified by the [IANA IPv6 Special-Purpose Address Registry].
199 /// Whether or not an address is practically reachable will depend on your network configuration.
200 ///
201 /// Most IPv6 addresses are globally reachable;
202 /// unless they are specifically defined as *not* globally reachable.
203 ///
204 /// Non-exhaustive list of notable addresses that are not globally reachable:
205 /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified))
206 /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback))
207 /// - IPv4-mapped addresses
208 /// - Addresses reserved for benchmarking
209 /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation))
210 /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local))
211 /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local))
212 ///
213 /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry].
214 ///
215 /// Note that an address having global scope is not the same as being globally reachable,
216 /// and there is no direct relation between the two concepts: There exist addresses with global scope
217 /// that are not globally reachable (for example unique local addresses),
218 /// and addresses that are globally reachable without having global scope
219 /// (multicast addresses with non-global scope).
220 ///
221 /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
222 /// [unspecified address]: Ipv6Addr::UNSPECIFIED
223 /// [loopback address]: Ipv6Addr::LOCALhost
224 #[must_use]
225 #[inline]
226 pub(crate) const fn is_global(a: Ipv6Addr) -> bool {
227 !(a.is_unspecified()
228 || a.is_loopback()
229 // IPv4-mapped Address (`::ffff:0:0/96`)
230 || matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _])
231 // IPv4-IPv6 Translat. (`64:ff9b:1::/48`)
232 || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _])
233 // Discard-Only Address Block (`100::/64`)
234 || matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _])
235 // IETF Protocol Assignments (`2001::/23`)
236 || (matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200)
237 && !(
238 // Port Control Protocol Anycast (`2001:1::1`)
239 u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001
240 // Traversal Using Relays around NAT Anycast (`2001:1::2`)
241 || u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002
242 // AMT (`2001:3::/32`)
243 || matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _])
244 // AS112-v6 (`2001:4:112::/48`)
245 || matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _])
246 // ORCHIDv2 (`2001:20::/28`)
247 || matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F)
248 ))
249 || is_documentation(a)
250 || is_unique_local(a)
251 || is_unicast_link_local(a))
252 }
253}
254
255pub(crate) fn is_global_ip(ip: &IpAddr) -> bool {
256 match ip {
257 IpAddr::V4(ipv4) => ipv4_global::is_global(*ipv4),
258 IpAddr::V6(ipv6) => ipv6_global::is_global(*ipv6),
259 }
260}
261
262#[allow(dead_code)]
263pub(crate) fn is_private_ip(ip: &IpAddr) -> bool {
264 match ip {
265 IpAddr::V4(ipv4) => ipv4_global::is_private(*ipv4),
266 IpAddr::V6(ipv6) => ipv6_global::is_unique_local(*ipv6),
267 }
268}