LCOV - code coverage report
Current view: top level - source/common/network - cidr_range.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 52 164 31.7 %
Date: 2024-01-05 06:35:25 Functions: 7 17 41.2 %

          Line data    Source code
       1             : #include "source/common/network/cidr_range.h"
       2             : 
       3             : #include <array>
       4             : #include <cstdint>
       5             : #include <string>
       6             : #include <vector>
       7             : 
       8             : #include "envoy/common/exception.h"
       9             : #include "envoy/common/platform.h"
      10             : 
      11             : #include "source/common/common/assert.h"
      12             : #include "source/common/common/fmt.h"
      13             : #include "source/common/common/safe_memcpy.h"
      14             : #include "source/common/common/utility.h"
      15             : #include "source/common/network/address_impl.h"
      16             : #include "source/common/network/utility.h"
      17             : 
      18             : namespace Envoy {
      19             : namespace Network {
      20             : namespace Address {
      21             : 
      22           0 : CidrRange::CidrRange() : length_(-1) {}
      23             : 
      24             : CidrRange::CidrRange(InstanceConstSharedPtr address, int length)
      25         846 :     : address_(std::move(address)), length_(length) {
      26             :   // This is a private ctor, so only checking these asserts in debug builds.
      27         846 :   if (address_ == nullptr) {
      28           0 :     ASSERT(length_ == -1);
      29         846 :   } else {
      30         846 :     ASSERT(address_->type() == Type::Ip);
      31         846 :     ASSERT(length_ >= 0);
      32         846 :   }
      33         846 : }
      34             : 
      35           0 : CidrRange& CidrRange::operator=(const CidrRange& other) = default;
      36             : 
      37           0 : bool CidrRange::operator==(const CidrRange& other) const {
      38             :   // Lengths must be the same, and must be valid (i.e. not -1).
      39           0 :   if (length_ != other.length_ || length_ == -1) {
      40           0 :     return false;
      41           0 :   }
      42             : 
      43           0 :   if (address_ == nullptr || other.address_ == nullptr) {
      44           0 :     return false;
      45           0 :   }
      46             : 
      47           0 :   if (address_->ip()->version() == IpVersion::v4) {
      48           0 :     return other.address_->ip()->version() == IpVersion::v4 &&
      49           0 :            address_->ip()->ipv4()->address() == other.address_->ip()->ipv4()->address();
      50           0 :   } else {
      51           0 :     return other.address_->ip()->version() == IpVersion::v6 &&
      52           0 :            address_->ip()->ipv6()->address() == other.address_->ip()->ipv6()->address();
      53           0 :   }
      54           0 : }
      55             : 
      56        1692 : const Ip* CidrRange::ip() const {
      57        1692 :   if (address_ != nullptr) {
      58        1692 :     return address_->ip();
      59        1692 :   }
      60           0 :   return nullptr;
      61        1692 : }
      62             : 
      63         846 : int CidrRange::length() const { return length_; }
      64             : 
      65           0 : bool CidrRange::isInRange(const Instance& address) const {
      66           0 :   if (address_ == nullptr || !isValid() || address.type() != Type::Ip ||
      67           0 :       address_->ip()->version() != address.ip()->version()) {
      68           0 :     return false;
      69           0 :   }
      70             : 
      71             :   // All addresses in range.
      72           0 :   if (length_ == 0) {
      73           0 :     return true;
      74           0 :   }
      75             : 
      76           0 :   switch (address.ip()->version()) {
      77           0 :   case IpVersion::v4:
      78           0 :     if (ntohl(address.ip()->ipv4()->address()) >> (32 - length_) ==
      79           0 :         ntohl(address_->ip()->ipv4()->address()) >> (32 - length_)) {
      80           0 :       return true;
      81           0 :     }
      82           0 :     break;
      83           0 :   case IpVersion::v6:
      84           0 :     if ((Utility::Ip6ntohl(address_->ip()->ipv6()->address()) >> (128 - length_)) ==
      85           0 :         (Utility::Ip6ntohl(address.ip()->ipv6()->address()) >> (128 - length_))) {
      86           0 :       return true;
      87           0 :     }
      88           0 :     break;
      89           0 :   }
      90           0 :   return false;
      91           0 : }
      92             : 
      93           0 : std::string CidrRange::asString() const {
      94           0 :   if (address_ == nullptr) {
      95           0 :     return "/-1";
      96           0 :   } else {
      97           0 :     return fmt::format("{}/{}", address_->ip()->addressAsString(), length_);
      98           0 :   }
      99           0 : }
     100             : 
     101             : // static
     102         846 : CidrRange CidrRange::create(InstanceConstSharedPtr address, int length) {
     103         846 :   InstanceConstSharedPtr ptr = truncateIpAddressAndLength(std::move(address), &length);
     104         846 :   return {std::move(ptr), length};
     105         846 : }
     106             : 
     107             : // static
     108           0 : CidrRange CidrRange::create(const std::string& address, int length) {
     109           0 :   return create(Utility::parseInternetAddress(address), length);
     110           0 : }
     111             : 
     112           1 : CidrRange CidrRange::create(const envoy::config::core::v3::CidrRange& cidr) {
     113           1 :   return create(Utility::parseInternetAddress(cidr.address_prefix()), cidr.prefix_len().value());
     114           1 : }
     115             : 
     116           0 : CidrRange CidrRange::create(const xds::core::v3::CidrRange& cidr) {
     117           0 :   return create(Utility::parseInternetAddress(cidr.address_prefix()), cidr.prefix_len().value());
     118           0 : }
     119             : 
     120             : // static
     121         846 : CidrRange CidrRange::create(const std::string& range) {
     122         846 :   const auto parts = StringUtil::splitToken(range, "/");
     123         846 :   if (parts.size() == 2) {
     124         846 :     InstanceConstSharedPtr ptr = Utility::parseInternetAddress(std::string{parts[0]});
     125         846 :     if (ptr->type() == Type::Ip) {
     126         846 :       uint64_t length64;
     127         846 :       if (absl::SimpleAtoi(parts[1], &length64)) {
     128         846 :         if ((ptr->ip()->version() == IpVersion::v6 && length64 <= 128) ||
     129         846 :             (ptr->ip()->version() == IpVersion::v4 && length64 <= 32)) {
     130         846 :           return create(std::move(ptr), static_cast<uint32_t>(length64));
     131         846 :         }
     132         846 :       }
     133         846 :     }
     134         846 :   }
     135           0 :   return {nullptr, -1};
     136         846 : }
     137             : 
     138             : // static
     139             : InstanceConstSharedPtr CidrRange::truncateIpAddressAndLength(InstanceConstSharedPtr address,
     140         846 :                                                              int* length_io) {
     141         846 :   int length = *length_io;
     142         846 :   if (address == nullptr || length < 0 || address->type() != Type::Ip) {
     143           0 :     *length_io = -1;
     144           0 :     return nullptr;
     145           0 :   }
     146         846 :   switch (address->ip()->version()) {
     147         423 :   case IpVersion::v4: {
     148         423 :     if (length >= 32) {
     149             :       // We're using all of the bits, so don't need to create a new address instance.
     150           0 :       *length_io = 32;
     151           0 :       return address;
     152         423 :     } else if (length == 0) {
     153             :       // Create an Ipv4Instance with only a port, which will thus have the any address.
     154         423 :       return std::make_shared<Ipv4Instance>(uint32_t(0));
     155         423 :     }
     156             :     // Need to mask out the unused bits, and create an Ipv4Instance with this address.
     157           0 :     uint32_t ip4 = ntohl(address->ip()->ipv4()->address());
     158           0 :     ip4 &= ~0U << (32 - length);
     159           0 :     sockaddr_in sa4;
     160           0 :     memset(&sa4, 0, sizeof(sa4));
     161           0 :     sa4.sin_family = AF_INET;
     162           0 :     sa4.sin_port = htons(0);
     163           0 :     sa4.sin_addr.s_addr = htonl(ip4);
     164           0 :     return std::make_shared<Ipv4Instance>(&sa4);
     165         423 :   }
     166             : 
     167         423 :   case IpVersion::v6: {
     168         423 :     if (length >= 128) {
     169             :       // We're using all of the bits, so don't need to create a new address instance.
     170           0 :       *length_io = 128;
     171           0 :       return address;
     172         423 :     } else if (length == 0) {
     173             :       // Create an Ipv6Instance with only a port, which will thus have the any address.
     174         423 :       return std::make_shared<Ipv6Instance>(uint32_t(0));
     175         423 :     }
     176           0 :     sockaddr_in6 sa6;
     177           0 :     memset(&sa6, 0, sizeof(sa6));
     178           0 :     sa6.sin6_family = AF_INET6;
     179           0 :     sa6.sin6_port = htons(0);
     180             : 
     181             :     // The maximum number stored in absl::uint128 has every bit set to 1.
     182           0 :     absl::uint128 mask = absl::Uint128Max();
     183             :     // Shifting the value to the left sets all bits between 128-length and 128 to zero.
     184           0 :     mask <<= (128 - length);
     185             :     // This will mask out the unused bits of the address.
     186           0 :     absl::uint128 ip6 = Utility::Ip6ntohl(address->ip()->ipv6()->address()) & mask;
     187             : 
     188           0 :     absl::uint128 ip6_htonl = Utility::Ip6htonl(ip6);
     189           0 :     static_assert(sizeof(absl::uint128) == 16, "The size of asbl::uint128 is not 16.");
     190           0 :     safeMemcpy(&sa6.sin6_addr.s6_addr, &ip6_htonl);
     191           0 :     return std::make_shared<Ipv6Instance>(sa6);
     192         423 :   }
     193         846 :   }
     194           0 :   PANIC_DUE_TO_CORRUPT_ENUM;
     195           0 : }
     196             : 
     197             : absl::StatusOr<std::unique_ptr<IpList>>
     198           0 : IpList::create(const Protobuf::RepeatedPtrField<envoy::config::core::v3::CidrRange>& cidrs) {
     199           0 :   std::unique_ptr<IpList> ret = std::unique_ptr<IpList>(new IpList(cidrs));
     200           0 :   if (!ret->error_.empty()) {
     201           0 :     return absl::InvalidArgumentError(ret->error_);
     202           0 :   }
     203           0 :   return ret;
     204           0 : }
     205           0 : IpList::IpList(const Protobuf::RepeatedPtrField<envoy::config::core::v3::CidrRange>& cidrs) {
     206           0 :   ip_list_.reserve(cidrs.size());
     207           0 :   for (const envoy::config::core::v3::CidrRange& entry : cidrs) {
     208           0 :     CidrRange list_entry = CidrRange::create(entry);
     209           0 :     if (list_entry.isValid()) {
     210           0 :       ip_list_.push_back(std::move(list_entry));
     211           0 :     } else {
     212           0 :       error_ = fmt::format("invalid ip/mask combo '{}/{}' (format is <ip>/<# mask bits>)",
     213           0 :                            entry.address_prefix(), entry.prefix_len().value());
     214           0 :     }
     215           0 :   }
     216           0 : }
     217             : 
     218           0 : bool IpList::contains(const Instance& address) const {
     219           0 :   for (const CidrRange& entry : ip_list_) {
     220           0 :     if (entry.isInRange(address)) {
     221           0 :       return true;
     222           0 :     }
     223           0 :   }
     224           0 :   return false;
     225           0 : }
     226             : 
     227             : } // namespace Address
     228             : } // namespace Network
     229             : } // namespace Envoy

Generated by: LCOV version 1.15