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