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