/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 |