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
2
CidrRange::CidrRange() : length_(-1) {}
23

            
24
CidrRange::CidrRange(InstanceConstSharedPtr address, int length)
25
96695
    : address_(std::move(address)), length_(length) {
26
  // This is a private ctor, so only checking these asserts in debug builds.
27
96695
  if (address_ == nullptr) {
28
    ASSERT(length_ == -1);
29
96695
  } else {
30
96695
    ASSERT(address_->type() == Type::Ip);
31
96695
    ASSERT(length_ >= 0);
32
96695
  }
33
96695
}
34

            
35
34797
CidrRange& CidrRange::operator=(const CidrRange& other) = default;
36

            
37
6
bool CidrRange::operator==(const CidrRange& other) const {
38
  // Lengths must be the same, and must be valid (i.e. not -1).
39
6
  if (length_ != other.length_ || length_ == -1) {
40
1
    return false;
41
1
  }
42

            
43
5
  if (address_ == nullptr || other.address_ == nullptr) {
44
    return false;
45
  }
46

            
47
5
  if (address_->ip()->version() == IpVersion::v4) {
48
2
    return other.address_->ip()->version() == IpVersion::v4 &&
49
2
           address_->ip()->ipv4()->address() == other.address_->ip()->ipv4()->address();
50
3
  } else {
51
3
    return other.address_->ip()->version() == IpVersion::v6 &&
52
3
           address_->ip()->ipv6()->address() == other.address_->ip()->ipv6()->address();
53
3
  }
54
5
}
55

            
56
139772
const Ip* CidrRange::ip() const {
57
139772
  if (address_ != nullptr) {
58
139772
    return address_->ip();
59
139772
  }
60
  return nullptr;
61
139772
}
62

            
63
69893
int CidrRange::length() const { return length_; }
64

            
65
2250
bool CidrRange::isInRange(const Instance& address) const {
66
2250
  if (address_ == nullptr || !isValid() || address.type() != Type::Ip ||
67
2250
      address_->ip()->version() != address.ip()->version()) {
68
81
    return false;
69
81
  }
70

            
71
  // All addresses in range.
72
2169
  if (length_ == 0) {
73
35
    return true;
74
35
  }
75

            
76
2134
  switch (address.ip()->version()) {
77
2054
  case IpVersion::v4:
78
2054
    if (ntohl(address.ip()->ipv4()->address()) >> (32 - length_) ==
79
2054
        ntohl(address_->ip()->ipv4()->address()) >> (32 - length_)) {
80
1943
      return true;
81
1943
    }
82
111
    break;
83
147
  case IpVersion::v6:
84
80
    if ((Utility::Ip6ntohl(address_->ip()->ipv6()->address()) >> (128 - length_)) ==
85
80
        (Utility::Ip6ntohl(address.ip()->ipv6()->address()) >> (128 - length_))) {
86
52
      return true;
87
52
    }
88
28
    break;
89
2134
  }
90
139
  return false;
91
2134
}
92

            
93
84
std::string CidrRange::asString() const {
94
84
  if (address_ == nullptr) {
95
    return "/-1";
96
84
  } else {
97
84
    return fmt::format("{}/{}", address_->ip()->addressAsString(), length_);
98
84
  }
99
84
}
100

            
101
// static
102
absl::StatusOr<CidrRange>
103
CidrRange::create(InstanceConstSharedPtr address, int length,
104
96713
                  absl::optional<absl::string_view> original_address_str) {
105
96713
  InstanceConstSharedPtr ptr = truncateIpAddressAndLength(std::move(address), &length);
106
96713
  if (!ptr) {
107
18
    return absl::InvalidArgumentError(absl::StrCat(
108
18
        "malformed IP address: ", original_address_str.has_value() ? *original_address_str : ""));
109
18
  }
110
96695
  CidrRange ret = CidrRange(std::move(ptr), length);
111
96695
  if (ret.isValid()) {
112
96695
    return ret;
113
96695
  }
114
  return absl::InvalidArgumentError("Invalid CIDR range");
115
96695
}
116

            
117
// static
118
27
absl::StatusOr<CidrRange> CidrRange::create(const std::string& address, int length) {
119
27
  return create(Utility::parseInternetAddressNoThrow(address), length, address);
120
27
}
121

            
122
18588
absl::StatusOr<CidrRange> CidrRange::create(const envoy::config::core::v3::CidrRange& cidr) {
123
18588
  return create(Utility::parseInternetAddressNoThrow(cidr.address_prefix()),
124
18588
                cidr.prefix_len().value(), cidr.address_prefix());
125
18588
}
126

            
127
116
absl::StatusOr<CidrRange> CidrRange::create(const xds::core::v3::CidrRange& cidr) {
128
116
  return create(Utility::parseInternetAddressNoThrow(cidr.address_prefix()),
129
116
                cidr.prefix_len().value(), cidr.address_prefix());
130
116
}
131

            
132
// static
133
77963
absl::StatusOr<CidrRange> CidrRange::create(const std::string& range) {
134
77963
  const auto parts = StringUtil::splitToken(range, "/");
135
77963
  if (parts.size() == 2) {
136
77957
    InstanceConstSharedPtr ptr = Utility::parseInternetAddressNoThrow(std::string{parts[0]});
137
77957
    if (ptr && ptr->type() == Type::Ip) {
138
77955
      uint64_t length64;
139
77955
      if (absl::SimpleAtoi(parts[1], &length64)) {
140
77953
        if ((ptr->ip()->version() == IpVersion::v6 && length64 <= 128) ||
141
77953
            (ptr->ip()->version() == IpVersion::v4 && length64 <= 32)) {
142
77953
          return create(std::move(ptr), static_cast<uint32_t>(length64));
143
77953
        }
144
77953
      }
145
77955
    }
146
77957
  }
147
10
  return absl::InvalidArgumentError("Invalid CIDR range");
148
77963
}
149

            
150
// static
151
InstanceConstSharedPtr CidrRange::truncateIpAddressAndLength(InstanceConstSharedPtr address,
152
96746
                                                             int* length_io) {
153
96746
  int length = *length_io;
154
96746
  if (address == nullptr || length < 0 || address->type() != Type::Ip) {
155
20
    *length_io = -1;
156
20
    return nullptr;
157
20
  }
158
96726
  switch (address->ip()->version()) {
159
52601
  case IpVersion::v4: {
160
52601
    if (length >= 32) {
161
      // We're using all of the bits, so don't need to create a new address instance.
162
93
      *length_io = 32;
163
93
      return address;
164
52536
    } else if (length == 0) {
165
      // Create an Ipv4Instance with only a port, which will thus have the any address.
166
34850
      return std::make_shared<Ipv4Instance>(uint32_t(0));
167
34850
    }
168
    // Need to mask out the unused bits, and create an Ipv4Instance with this address.
169
17658
    uint32_t ip4 = ntohl(address->ip()->ipv4()->address());
170
17658
    ip4 &= ~0U << (32 - length);
171
17658
    sockaddr_in sa4;
172
17658
    memset(&sa4, 0, sizeof(sa4));
173
17658
    sa4.sin_family = AF_INET;
174
17658
    sa4.sin_port = htons(0);
175
17658
    sa4.sin_addr.s_addr = htonl(ip4);
176
17658
    return std::make_shared<Ipv4Instance>(&sa4);
177
52601
  }
178

            
179
44125
  case IpVersion::v6: {
180
44125
    if (length >= 128) {
181
      // We're using all of the bits, so don't need to create a new address instance.
182
9243
      *length_io = 128;
183
9243
      return address;
184
34882
    } else if (length == 0) {
185
      // Create an Ipv6Instance with only a port, which will thus have the any address.
186
34808
      return std::make_shared<Ipv6Instance>(uint32_t(0));
187
34808
    }
188
74
    sockaddr_in6 sa6;
189
74
    memset(&sa6, 0, sizeof(sa6));
190
74
    sa6.sin6_family = AF_INET6;
191
74
    sa6.sin6_port = htons(0);
192

            
193
    // The maximum number stored in absl::uint128 has every bit set to 1.
194
74
    absl::uint128 mask = absl::Uint128Max();
195
    // Shifting the value to the left sets all bits between 128-length and 128 to zero.
196
74
    mask <<= (128 - length);
197
    // This will mask out the unused bits of the address.
198
74
    absl::uint128 ip6 = Utility::Ip6ntohl(address->ip()->ipv6()->address()) & mask;
199

            
200
74
    absl::uint128 ip6_htonl = Utility::Ip6htonl(ip6);
201
74
    static_assert(sizeof(absl::uint128) == 16, "The size of asbl::uint128 is not 16.");
202
74
    safeMemcpy(&sa6.sin6_addr.s6_addr, &ip6_htonl);
203
74
    return std::make_shared<Ipv6Instance>(sa6);
204
44125
  }
205
96726
  }
206
  PANIC_DUE_TO_CORRUPT_ENUM;
207
}
208

            
209
absl::StatusOr<std::unique_ptr<IpList>>
210
23417
IpList::create(const Protobuf::RepeatedPtrField<envoy::config::core::v3::CidrRange>& cidrs) {
211
23417
  std::unique_ptr<IpList> ret = std::unique_ptr<IpList>(new IpList(cidrs));
212
23417
  if (!ret->error_.empty()) {
213
1
    return absl::InvalidArgumentError(ret->error_);
214
1
  }
215
23416
  return ret;
216
23417
}
217

            
218
absl::StatusOr<std::unique_ptr<IpList>>
219
19
IpList::create(const Protobuf::RepeatedPtrField<xds::core::v3::CidrRange>& cidrs) {
220
19
  std::unique_ptr<IpList> ret = std::unique_ptr<IpList>(new IpList(cidrs));
221
19
  if (!ret->error_.empty()) {
222
    return absl::InvalidArgumentError(ret->error_);
223
  }
224
19
  return ret;
225
19
}
226

            
227
23417
IpList::IpList(const Protobuf::RepeatedPtrField<envoy::config::core::v3::CidrRange>& cidrs) {
228
23417
  ip_list_.reserve(cidrs.size());
229
28760
  for (const envoy::config::core::v3::CidrRange& entry : cidrs) {
230
18452
    absl::StatusOr<CidrRange> range_or_error = CidrRange::create(entry);
231
18452
    if (range_or_error.status().ok()) {
232
18451
      ip_list_.push_back(std::move(range_or_error.value()));
233
18451
    } else {
234
1
      error_ = fmt::format("invalid ip/mask combo '{}/{}' (format is <ip>/<# mask bits>)",
235
1
                           entry.address_prefix(), entry.prefix_len().value());
236
1
    }
237
18452
  }
238
23417
}
239

            
240
19
IpList::IpList(const Protobuf::RepeatedPtrField<xds::core::v3::CidrRange>& cidrs) {
241
19
  ip_list_.reserve(cidrs.size());
242
26
  for (const xds::core::v3::CidrRange& entry : cidrs) {
243
26
    absl::StatusOr<CidrRange> range_or_error = CidrRange::create(entry);
244
26
    if (range_or_error.status().ok()) {
245
26
      ip_list_.push_back(std::move(range_or_error.value()));
246
26
    } else {
247
      error_ = fmt::format("invalid ip/mask combo '{}/{}' (format is <ip>/<# mask bits>)",
248
                           entry.address_prefix(), entry.prefix_len().value());
249
    }
250
26
  }
251
19
}
252

            
253
1496
bool IpList::contains(const Instance& address) const {
254
1585
  for (const CidrRange& entry : ip_list_) {
255
1585
    if (entry.isInRange(address)) {
256
1429
      return true;
257
1429
    }
258
1585
  }
259
67
  return false;
260
1496
}
261

            
262
} // namespace Address
263
} // namespace Network
264
} // namespace Envoy