Line data Source code
1 : /********************************************************************************
2 : * Copyright (c) 2025 Accenture
3 : *
4 : * This program and the accompanying materials are made available under the
5 : * terms of the Apache License Version 2.0 which is available at
6 : * https://www.apache.org/licenses/LICENSE-2.0
7 : *
8 : * SPDX-License-Identifier: Apache-2.0
9 : ********************************************************************************/
10 :
11 : #pragma once
12 :
13 : #include <etl/array.h>
14 : #include <etl/error_handler.h>
15 : #include <etl/span.h>
16 : #include <etl/unaligned_type.h>
17 :
18 : #include <cstdint>
19 : #include <cstring>
20 :
21 : namespace ip
22 : {
23 : struct IPAddress
24 : {
25 : enum Family
26 : {
27 : FAMILY_UNKNOWN = 0U, ///< \deprecated FAMILY_UNKNOWN will be removed in future
28 : IPV4 = 4U,
29 : IPV6 = 6U
30 : };
31 :
32 : static constexpr uint8_t IP4LENGTH = 4U;
33 : static constexpr uint8_t IP6LENGTH = 16U;
34 :
35 : #ifdef PLATFORM_SUPPORT_IPV6
36 : static constexpr size_t MAX_IP_LENGTH = IP6LENGTH;
37 : #else
38 : static constexpr size_t MAX_IP_LENGTH = IP4LENGTH;
39 : #endif
40 :
41 398121 : uint32_t be_uint32_at(size_t index) const
42 : {
43 398121 : return ::etl::be_uint32_t(&raw[index * sizeof(uint32_t)]);
44 : }
45 :
46 : ::etl::be_uint32_ext_t be_uint32_at(size_t index)
47 : {
48 : return ::etl::be_uint32_ext_t(&raw[index * sizeof(uint32_t)]);
49 : }
50 :
51 : ::etl::array<uint8_t, MAX_IP_LENGTH> raw = {};
52 : };
53 :
54 : constexpr IPAddress make_ip4(uint32_t ip4addr);
55 :
56 : IPAddress make_ip4(::etl::span<uint8_t const> const& ip4addr);
57 :
58 : #ifdef PLATFORM_SUPPORT_IPV6
59 : constexpr IPAddress make_ip6(uint32_t addr0, uint32_t addr1, uint32_t addr2, uint32_t addr3);
60 :
61 : constexpr IPAddress
62 : make_ip6(::etl::span<uint32_t const, IPAddress::IP6LENGTH / sizeof(uint32_t)> ip6addr);
63 :
64 : IPAddress make_ip6(::etl::span<uint8_t const> const& ip6addr);
65 : #endif
66 :
67 : /** \deprecated Use one of the other make_ip4 factory functions instead. */
68 : constexpr IPAddress make_ip4(uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3);
69 :
70 : ::etl::span<uint8_t const> packed(IPAddress const& ipAddr);
71 :
72 : uint32_t ip4_to_u32(IPAddress const& ipAddr);
73 :
74 : #ifdef PLATFORM_SUPPORT_IPV6
75 : uint32_t ip6_to_u32(IPAddress const& ipAddr, size_t offset);
76 : #endif
77 :
78 : bool isUnspecified(IPAddress const& ipAddr);
79 :
80 : /** \deprecated Test (addressFamilyOf(ipAddr) != IPAddress::IPV4) instead. */
81 : bool isIp4Address(IPAddress const& ipAddr);
82 :
83 : /** \deprecated Test (addressFamilyOf(ipAddr) != IPAddress::IPV6) instead. */
84 : bool isIp6Address(IPAddress const& ipAddr);
85 :
86 : bool isMulticastAddress(IPAddress const& ipAddr);
87 :
88 : bool isLinkLocalAddress(IPAddress const& ipAddr);
89 :
90 : bool isLoopbackAddress(IPAddress const& ipAddr);
91 :
92 : bool isNetworkLocal(IPAddress const& ipAddr1, IPAddress const& ipAddr2, uint8_t networkId);
93 :
94 : IPAddress::Family addressFamilyOf(IPAddress const& ipAddr);
95 :
96 : bool operator==(IPAddress const& ip1, IPAddress const& ip2);
97 :
98 : bool operator!=(IPAddress const& ip1, IPAddress const& ip2);
99 :
100 : /** \deprecated Implement your own comparator logic if needed */
101 : struct IPAddressCompareLess
102 : {
103 : bool operator()(IPAddress const& ipAddr1, IPAddress const& ipAddr2) const;
104 : };
105 :
106 : namespace internal
107 : {
108 : static constexpr size_t RAW_IP4_IDX = IPAddress::MAX_IP_LENGTH - 1U * sizeof(uint32_t);
109 : static constexpr size_t IP4_IDX = IPAddress::MAX_IP_LENGTH / sizeof(uint32_t) - 1U;
110 : } // namespace internal
111 :
112 619 : inline constexpr IPAddress make_ip4(uint32_t const ip4addr)
113 : {
114 : // clang-format off
115 : return {{
116 : #ifdef PLATFORM_SUPPORT_IPV6
117 : 0x00U, 0x00U, 0x00U, 0x00U,
118 : 0x00U, 0x00U, 0x00U, 0x00U,
119 : 0x00U, 0x00U, 0xFFU, 0xFFU,
120 : #endif
121 619 : static_cast<uint8_t>(ip4addr >> 24),
122 619 : static_cast<uint8_t>(ip4addr >> 16),
123 619 : static_cast<uint8_t>(ip4addr >> 8),
124 619 : static_cast<uint8_t>(ip4addr)}};
125 : // clang-format on
126 : }
127 :
128 : inline constexpr IPAddress
129 635 : make_ip4(uint8_t const byte0, uint8_t const byte1, uint8_t const byte2, uint8_t const byte3)
130 : {
131 : // clang-format off
132 : return {{
133 : #ifdef PLATFORM_SUPPORT_IPV6
134 : 0x00U, 0x00U, 0x00U, 0x00U,
135 : 0x00U, 0x00U, 0x00U, 0x00U,
136 : 0x00U, 0x00U, 0xFFU, 0xFFU,
137 : #endif
138 635 : byte0, byte1, byte2, byte3}};
139 : // clang-format on
140 : }
141 :
142 576 : inline IPAddress make_ip4(::etl::span<uint8_t const> const& ip4addr)
143 : {
144 576 : ETL_ASSERT(
145 : ip4addr.size() == IPAddress::IP4LENGTH,
146 : ETL_ERROR_GENERIC("ipv4 addres must be of correct length"));
147 :
148 : // clang-format off
149 : IPAddress const newAddr = {{
150 : #ifdef PLATFORM_SUPPORT_IPV6
151 : 0x00U, 0x00U, 0x00U, 0x00U,
152 : 0x00U, 0x00U, 0x00U, 0x00U,
153 : 0x00U, 0x00U, 0xFFU, 0xFFU,
154 : #endif
155 575 : ip4addr[0U], ip4addr[1U], ip4addr[2U], ip4addr[3U]
156 575 : }};
157 : // clang-format on
158 :
159 575 : return newAddr;
160 : }
161 :
162 : #ifdef PLATFORM_SUPPORT_IPV6
163 : inline constexpr IPAddress
164 : make_ip6(uint32_t const addr0, uint32_t const addr1, uint32_t const addr2, uint32_t const addr3)
165 : {
166 : return {
167 : {static_cast<uint8_t>(addr0 >> 24),
168 : static_cast<uint8_t>(addr0 >> 16),
169 : static_cast<uint8_t>(addr0 >> 8),
170 : static_cast<uint8_t>(addr0),
171 : static_cast<uint8_t>(addr1 >> 24),
172 : static_cast<uint8_t>(addr1 >> 16),
173 : static_cast<uint8_t>(addr1 >> 8),
174 : static_cast<uint8_t>(addr1),
175 : static_cast<uint8_t>(addr2 >> 24),
176 : static_cast<uint8_t>(addr2 >> 16),
177 : static_cast<uint8_t>(addr2 >> 8),
178 : static_cast<uint8_t>(addr2),
179 : static_cast<uint8_t>(addr3 >> 24),
180 : static_cast<uint8_t>(addr3 >> 16),
181 : static_cast<uint8_t>(addr3 >> 8),
182 : static_cast<uint8_t>(addr3)}};
183 : }
184 :
185 : inline constexpr IPAddress
186 : make_ip6(::etl::span<uint32_t const, IPAddress::IP6LENGTH / sizeof(uint32_t)> const ip6addr)
187 : {
188 : return {
189 : {static_cast<uint8_t>(ip6addr[0] >> 24),
190 : static_cast<uint8_t>(ip6addr[0] >> 16),
191 : static_cast<uint8_t>(ip6addr[0] >> 8),
192 : static_cast<uint8_t>(ip6addr[0]),
193 : static_cast<uint8_t>(ip6addr[1] >> 24),
194 : static_cast<uint8_t>(ip6addr[1] >> 16),
195 : static_cast<uint8_t>(ip6addr[1] >> 8),
196 : static_cast<uint8_t>(ip6addr[1]),
197 : static_cast<uint8_t>(ip6addr[2] >> 24),
198 : static_cast<uint8_t>(ip6addr[2] >> 16),
199 : static_cast<uint8_t>(ip6addr[2] >> 8),
200 : static_cast<uint8_t>(ip6addr[2]),
201 : static_cast<uint8_t>(ip6addr[3] >> 24),
202 : static_cast<uint8_t>(ip6addr[3] >> 16),
203 : static_cast<uint8_t>(ip6addr[3] >> 8),
204 : static_cast<uint8_t>(ip6addr[3])}};
205 : }
206 :
207 : inline IPAddress make_ip6(::etl::span<uint8_t const> const& ip6addr)
208 : {
209 : ETL_ASSERT(
210 : ip6addr.size() == IPAddress::IP6LENGTH,
211 : ETL_ERROR_GENERIC("ipv6 address must be of correct length"));
212 :
213 : IPAddress newAddr;
214 : (void)memcpy(&newAddr.raw[0U], ip6addr.data(), IPAddress::IP6LENGTH);
215 : return newAddr;
216 : }
217 : #endif
218 :
219 34 : inline ::etl::span<uint8_t const, IPAddress::IP4LENGTH> ip4_bytes(IPAddress const& ipAddr)
220 : {
221 : return ::etl::span<uint8_t const, IPAddress::IP4LENGTH>(
222 34 : &ipAddr.raw[internal::RAW_IP4_IDX], IPAddress::IP4LENGTH);
223 : }
224 :
225 0 : inline ::etl::span<uint8_t const, IPAddress::IP6LENGTH> ip6_bytes(IPAddress const& ipAddr)
226 : {
227 0 : return ::etl::span<uint8_t const, IPAddress::IP6LENGTH>(&ipAddr.raw[0U], IPAddress::IP6LENGTH);
228 : }
229 :
230 6 : inline ::etl::span<uint8_t const> packed(IPAddress const& ipAddr)
231 : {
232 6 : if (addressFamilyOf(ipAddr) == IPAddress::IPV4)
233 : {
234 6 : return ip4_bytes(ipAddr);
235 : }
236 0 : return ip6_bytes(ipAddr);
237 : }
238 :
239 13 : inline uint32_t ip4_to_u32(IPAddress const& ipAddr)
240 : {
241 13 : return ipAddr.be_uint32_at(internal::IP4_IDX);
242 : }
243 :
244 : #ifdef PLATFORM_SUPPORT_IPV6
245 : inline uint32_t ip6_to_u32(IPAddress const& ipAddr, size_t const offset)
246 : {
247 : ETL_ASSERT(offset <= 3U, ETL_ERROR_GENERIC("offset must be smaller than 3"));
248 :
249 : return ipAddr.be_uint32_at(offset);
250 : }
251 : #endif
252 :
253 400 : inline bool isUnspecified(IPAddress const& ipAddr)
254 : {
255 400 : if (addressFamilyOf(ipAddr) == IPAddress::IPV4)
256 : {
257 400 : return ipAddr.be_uint32_at(internal::IP4_IDX) == 0U;
258 : }
259 :
260 0 : for (auto const i : ipAddr.raw)
261 : {
262 0 : if (0U != i)
263 : {
264 0 : return false;
265 : }
266 : }
267 0 : return true;
268 : }
269 :
270 70 : inline bool isIp4Address(IPAddress const& ipAddr)
271 : {
272 70 : return (IPAddress::IPV4 == addressFamilyOf(ipAddr));
273 : }
274 :
275 70 : inline bool isIp6Address(IPAddress const& ipAddr)
276 : {
277 70 : return (IPAddress::IPV6 == addressFamilyOf(ipAddr));
278 : }
279 :
280 171 : inline bool isMulticastAddress(IPAddress const& ipAddr)
281 : {
282 171 : IPAddress::Family const family = addressFamilyOf(ipAddr);
283 :
284 171 : if (IPAddress::IPV6 == family)
285 : {
286 0 : uint8_t const IP6_MULTICAST_PREFIX = 0xFFU;
287 0 : return (ipAddr.raw[0U] == IP6_MULTICAST_PREFIX);
288 : }
289 :
290 : // IPAddress::IPv4
291 171 : uint32_t const IP4_MULTICAST_MASK = 0xF0000000U;
292 171 : uint32_t const IP4_MULTICAST_PREFIX = 0xE0000000U;
293 171 : uint32_t const addressPrefix = (ipAddr.be_uint32_at(internal::IP4_IDX) & IP4_MULTICAST_MASK);
294 171 : return (addressPrefix == IP4_MULTICAST_PREFIX);
295 : }
296 :
297 3 : inline bool isLinkLocalAddress(IPAddress const& ipAddr)
298 : {
299 3 : IPAddress::Family const family = addressFamilyOf(ipAddr);
300 :
301 3 : if (IPAddress::IPV6 == family)
302 : {
303 0 : uint32_t const IP6_LINK_LOCAL_MASK = 0xFFC00000U;
304 0 : uint32_t const IP6_LINK_LOCAL_PREFIX = 0xFE800000U;
305 0 : uint32_t const addressPrefix = (ipAddr.be_uint32_at(0U) & IP6_LINK_LOCAL_MASK);
306 0 : return (addressPrefix == IP6_LINK_LOCAL_PREFIX);
307 : }
308 :
309 : // IPAddress::IPv4
310 3 : uint32_t const IP4_LINK_LOCAL_MASK = 0xFFFF0000U;
311 3 : uint32_t const IP4_LINK_LOCAL_PREFIX = 0xA9FE0000U;
312 3 : uint32_t const addressPrefix = (ipAddr.be_uint32_at(internal::IP4_IDX) & IP4_LINK_LOCAL_MASK);
313 3 : return (addressPrefix == IP4_LINK_LOCAL_PREFIX);
314 : }
315 :
316 257 : inline bool isLoopbackAddress(IPAddress const& ipAddr)
317 : {
318 257 : IPAddress::Family const family = addressFamilyOf(ipAddr);
319 :
320 257 : if (IPAddress::IPV6 == family)
321 : {
322 0 : return (ipAddr.be_uint32_at(0U) == 0U) && (ipAddr.be_uint32_at(1U) == 0U)
323 0 : && (ipAddr.be_uint32_at(2U) == 0U) && (ipAddr.be_uint32_at(3U) == 1U);
324 : }
325 :
326 : // IPAddress::IPv4
327 257 : uint32_t const IP4_LOOPBACK_PREFIX = 0x7F000000U;
328 257 : uint32_t const IP4_LOOPBACK_MASK = 0xFFFFFF00U;
329 257 : uint32_t const addressPrefix = (ipAddr.be_uint32_at(internal::IP4_IDX) & IP4_LOOPBACK_MASK);
330 257 : bool const hasLastByteSet = (ipAddr.raw[internal::RAW_IP4_IDX + 3U] != 0U);
331 257 : return ((addressPrefix == IP4_LOOPBACK_PREFIX) && hasLastByteSet);
332 : }
333 :
334 : inline bool
335 49 : isNetworkLocal(IPAddress const& ipAddr1, IPAddress const& ipAddr2, uint8_t const networkId)
336 : {
337 49 : IPAddress::Family const family1 = addressFamilyOf(ipAddr1);
338 49 : IPAddress::Family const family2 = addressFamilyOf(ipAddr2);
339 :
340 49 : if ((family1 != family2) || isUnspecified(ipAddr1) || isUnspecified(ipAddr2))
341 : {
342 2 : return false; // invalid IPs or IPv4/6 mix
343 : }
344 :
345 47 : if (networkId == 0U)
346 : {
347 2 : return true; // don't care
348 : }
349 :
350 45 : size_t const ip4PrefixLengthInBits = internal::IP4_IDX * sizeof(uint32_t) * 8U;
351 45 : size_t const netMaskBits
352 : = (IPAddress::IPV6 == family1) ? networkId : (networkId + ip4PrefixLengthInBits);
353 :
354 45 : if (netMaskBits > (IPAddress::MAX_IP_LENGTH * 8U))
355 : {
356 1 : return false; // invalid networkId
357 : }
358 :
359 44 : size_t const netMaskFullBytes = netMaskBits / 8U;
360 44 : if (0 != memcmp(&ipAddr1.raw[0U], &ipAddr2.raw[0U], netMaskFullBytes))
361 : {
362 2 : return false;
363 : }
364 :
365 42 : uint8_t const mask = ~(0xFFU >> (netMaskBits % 8U));
366 42 : if (mask == 0U)
367 : {
368 4 : return true;
369 : }
370 :
371 38 : return ((ipAddr1.raw[netMaskFullBytes] & mask) == (ipAddr2.raw[netMaskFullBytes] & mask));
372 : }
373 :
374 395677 : inline IPAddress::Family addressFamilyOf([[maybe_unused]] IPAddress const& ipAddr)
375 : {
376 : #ifdef PLATFORM_SUPPORT_IPV6
377 : uint32_t const IP4_PREFIX[] = {0U, 0U, 0xFFFFU};
378 :
379 : bool const isIp4MappedIp6 = (IP4_PREFIX[0U] == ipAddr.be_uint32_at(0U))
380 : && (IP4_PREFIX[1U] == ipAddr.be_uint32_at(1U))
381 : && (IP4_PREFIX[2U] == ipAddr.be_uint32_at(2U));
382 :
383 : return isIp4MappedIp6 ? IPAddress::IPV4 : IPAddress::IPV6;
384 : #else
385 395677 : return IPAddress::IPV4;
386 : #endif
387 : }
388 :
389 1102 : inline bool operator==(IPAddress const& ip1, IPAddress const& ip2)
390 : {
391 : #ifdef PLATFORM_SUPPORT_IPV6
392 : IPAddress::Family const family1 = addressFamilyOf(ip1);
393 : IPAddress::Family const family2 = addressFamilyOf(ip2);
394 : if (family1 != family2)
395 : {
396 : return false;
397 : }
398 :
399 : if (IPAddress::IPV4 == family1)
400 : {
401 : // Only the last 32 bits carry the IPv4 payload; the IPv4-mapped prefix was already
402 : // verified equal by addressFamilyOf() returning the same family for both operands.
403 : return (ip1.be_uint32_at(internal::IP4_IDX) == ip2.be_uint32_at(internal::IP4_IDX));
404 : }
405 : return (
406 : (ip1.be_uint32_at(3) == ip2.be_uint32_at(3)) && (ip1.be_uint32_at(2) == ip2.be_uint32_at(2))
407 : && (ip1.be_uint32_at(1) == ip2.be_uint32_at(1))
408 : && (ip1.be_uint32_at(0) == ip2.be_uint32_at(0)));
409 : #else
410 1102 : return (ip1.be_uint32_at(0) == ip2.be_uint32_at(0));
411 : #endif
412 : }
413 :
414 37 : inline bool operator!=(IPAddress const& ip1, IPAddress const& ip2) { return !(ip1 == ip2); }
415 :
416 : inline bool
417 197284 : IPAddressCompareLess::operator()(IPAddress const& ipAddr1, IPAddress const& ipAddr2) const
418 : {
419 197284 : IPAddress::Family const family1 = addressFamilyOf(ipAddr1);
420 197284 : IPAddress::Family const family2 = addressFamilyOf(ipAddr2);
421 :
422 197284 : if (family1 != family2)
423 : {
424 0 : return (static_cast<uint8_t>(family1) < static_cast<uint8_t>(family2));
425 : }
426 :
427 394316 : for (uint8_t i = 0U; i < (IPAddress::MAX_IP_LENGTH / sizeof(uint32_t)); ++i)
428 : {
429 197284 : if (ipAddr1.be_uint32_at(i) != ipAddr2.be_uint32_at(i))
430 : {
431 252 : return ipAddr1.be_uint32_at(i) < ipAddr2.be_uint32_at(i);
432 : }
433 : }
434 :
435 197032 : return false;
436 : }
437 :
438 : } // namespace ip
|