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