LCOV - code coverage report
Current view: top level - cpp2ethernet/include/ip - IPAddress.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 131 131 100.0 %
Date: 2025-10-07 10:52:16 Functions: 23 23 100.0 %

          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

Generated by: LCOV version 1.14