Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/extensions/filters/network/connection_limit/connection_limit.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/extensions/filters/network/connection_limit/connection_limit.h"
2
3
#include "envoy/extensions/filters/network/connection_limit/v3/connection_limit.pb.h"
4
5
#include "source/common/protobuf/utility.h"
6
7
namespace Envoy {
8
namespace Extensions {
9
namespace NetworkFilters {
10
namespace ConnectionLimitFilter {
11
12
Config::Config(
13
    const envoy::extensions::filters::network::connection_limit::v3::ConnectionLimit& proto_config,
14
    Stats::Scope& scope, Runtime::Loader& runtime)
15
    : enabled_(proto_config.runtime_enabled(), runtime),
16
      stats_(generateStats(proto_config.stat_prefix(), scope)),
17
      max_connections_(PROTOBUF_GET_WRAPPED_REQUIRED(proto_config, max_connections)),
18
0
      connections_(0), delay_(PROTOBUF_GET_OPTIONAL_MS(proto_config, delay)) {}
19
20
0
ConnectionLimitStats Config::generateStats(const std::string& prefix, Stats::Scope& scope) {
21
0
  const std::string final_prefix = "connection_limit." + prefix;
22
0
  return {ALL_CONNECTION_LIMIT_STATS(POOL_COUNTER_PREFIX(scope, final_prefix),
23
0
                                     POOL_GAUGE_PREFIX(scope, final_prefix))};
24
0
}
25
26
0
bool Config::incrementConnectionWithinLimit() {
27
0
  auto conns = connections_.load(std::memory_order_relaxed);
28
0
  while (conns < max_connections_) {
29
    // Testing hook.
30
0
    synchronizer_.syncPoint("increment_pre_cas");
31
32
0
    if (connections_.compare_exchange_weak(conns, conns + 1, std::memory_order_release,
33
0
                                           std::memory_order_relaxed)) {
34
0
      return true;
35
0
    }
36
0
  }
37
0
  return false;
38
0
}
39
40
0
void Config::incrementConnection() { connections_++; }
41
42
0
void Config::decrementConnection() {
43
0
  ASSERT(connections_ > 0);
44
0
  connections_--;
45
0
}
46
47
0
void Filter::resetTimerState() {
48
0
  if (delay_timer_) {
49
0
    delay_timer_->disableTimer();
50
0
    delay_timer_.reset();
51
0
  }
52
0
}
53
54
0
Network::FilterStatus Filter::onData(Buffer::Instance&, bool) {
55
0
  if (is_rejected_) {
56
0
    return Network::FilterStatus::StopIteration;
57
0
  }
58
0
  return Network::FilterStatus::Continue;
59
0
}
60
61
0
Network::FilterStatus Filter::onNewConnection() {
62
0
  if (!config_->enabled()) {
63
0
    ENVOY_CONN_LOG(trace, "connection_limit: runtime disabled", read_callbacks_->connection());
64
0
    return Network::FilterStatus::Continue;
65
0
  }
66
67
0
  config_->stats().active_connections_.inc();
68
69
0
  if (!config_->incrementConnectionWithinLimit()) {
70
0
    config_->stats().limited_connections_.inc();
71
0
    ENVOY_CONN_LOG(trace, "connection_limit: connection limiting connection",
72
0
                   read_callbacks_->connection());
73
74
    // Set is_rejected_ is true, so that onData() will return StopIteration during the delay time.
75
0
    is_rejected_ = true;
76
    // The close() will trigger onEvent() with close event, increment the active connections count.
77
0
    config_->incrementConnection();
78
79
    // Delay rejection provides a better DoS protection for Envoy.
80
0
    absl::optional<std::chrono::milliseconds> duration = config_->delay();
81
0
    if (duration.has_value() && duration.value() > std::chrono::milliseconds(0)) {
82
0
      delay_timer_ = read_callbacks_->connection().dispatcher().createTimer([this]() -> void {
83
0
        read_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush);
84
0
      });
85
0
      delay_timer_->enableTimer(duration.value());
86
0
    } else {
87
0
      read_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush);
88
0
    }
89
0
    return Network::FilterStatus::StopIteration;
90
0
  }
91
92
0
  return Network::FilterStatus::Continue;
93
0
}
94
95
0
void Filter::onEvent(Network::ConnectionEvent event) {
96
0
  if (event == Network::ConnectionEvent::RemoteClose ||
97
0
      event == Network::ConnectionEvent::LocalClose) {
98
0
    resetTimerState();
99
0
    config_->decrementConnection();
100
0
    config_->stats().active_connections_.dec();
101
0
  }
102
0
}
103
104
} // namespace ConnectionLimitFilter
105
} // namespace NetworkFilters
106
} // namespace Extensions
107
} // namespace Envoy