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