LCOV - code coverage report
Current view: top level - libs/bsw/cpp2ethernet/include/ip - IPAddress.h (source / functions) Coverage Total Hit
Test: coverage.info Lines: 83.5 % 97 81
Test Date: 2026-10-01 16:17:37 Functions: 94.7 % 19 18

            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
        

Generated by: LCOV version 2.0-1