Skip to content

Commit 8b85aa1

Browse files
committed
proxy.config.http.per_client.connection.exempt_list
This implements proxy.config.http.per_client.connection.exempt_list, a configuration for the user to be able to provide a set of IP addresses that are not counted against proxy.config.net.per_client.max_connections_in. This also adds the following TS APIs to modify this list via a plugin: TSConnectionLimitExemptListSet TSConnectionLimitExemptListAdd TSConnectionLimitExemptListClear
1 parent 64d4769 commit 8b85aa1

File tree

12 files changed

+523
-55
lines changed

12 files changed

+523
-55
lines changed

doc/admin-guide/files/records.yaml.en.rst

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,37 @@ Network
524524
below this limit. A value of 0 disables the per client concurrent connection
525525
limit.
526526

527+
See :ts:cv:`proxy.config.http.per_client.connection.exempt_list` for a way to
528+
allow (not count) certain client IP addresses when applying this limit.
529+
530+
.. ts:cv:: CONFIG proxy.config.http.per_client.connection.exempt_list STRING NULL
531+
532+
A comma-separated list of IP addresses or CIDR ranges to exempt when
533+
counting incoming client connections for per client connection
534+
throttling. Incoming addresses in this specified set will not count
535+
against :ts:cv:`proxy.config.net.per_client.max_connections_in` and
536+
thus will not be blocked by that configuration. This may be useful,
537+
for example, to allow any number of incoming connections from within
538+
an organization's network without blocking them due to the per client
539+
connection max feature.
540+
541+
This configuration takes a comma-separated list of IP addresses, CIDR
542+
networks, or ranges separated by a dash.
543+
544+
============================== ===========================================================
545+
Example Effect
546+
============================== ===========================================================
547+
``10.0.2.123`` Exempt a single IP Address.
548+
``10.0.3.1-10.0.3.254`` Exempt a range of IP address.
549+
``10.0.4.0/24`` Exempt a range of IP address specified by CIDR notation.
550+
``10.0.2.123,172.16.0.0/20`` Exempt multiple addresses/ranges.
551+
============================== ===========================================================
552+
553+
Here is an example configuration value::
554+
555+
10.0.2.123,172.16.0.0/20,192.168.1.0/24
556+
557+
527558
.. ts:cv:: CONFIG proxy.config.http.per_client.connection.alert_delay INT 60
528559
:reloadable:
529560
:units: seconds
@@ -2041,7 +2072,7 @@ Proxy User Variables
20412072
by a dash or by using CIDR notation.
20422073

20432074
======================= ===========================================================
2044-
Example Effect
2075+
Example Effect
20452076
======================= ===========================================================
20462077
``10.0.2.123`` A single IP Address.
20472078
``10.0.3.1-10.0.3.254`` A range of IP address.
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
.. Licensed to the Apache Software Foundation (ASF) under one or more
2+
contributor license agreements. See the NOTICE file distributed
3+
with this work for additional information regarding copyright
4+
ownership. The ASF licenses this file to you under the Apache
5+
License, Version 2.0 (the "License"); you may not use this file
6+
except in compliance with the License. You may obtain a copy of
7+
the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14+
implied. See the License for the specific language governing
15+
permissions and limitations under the License.
16+
17+
.. default-domain:: cpp
18+
19+
TSConnectionLimitExemptList
20+
===========================
21+
22+
Synopsis
23+
--------
24+
25+
.. code-block:: cpp
26+
27+
#include <ts/ts.h>
28+
29+
.. function:: TSReturnCode TSConnectionLimitExemptListAdd(std::string_view ip_ranges)
30+
.. function:: TSReturnCode TSConnectionLimitExemptListRemove(std::string_view ip_ranges)
31+
.. function:: void TSConnectionLimitExemptListClear()
32+
33+
Description
34+
-----------
35+
36+
These functions manage the per-client connection limit exempt list, which contains IP addresses
37+
and ranges that are exempt from the connection limits enforced by
38+
:ts:cv:`proxy.config.net.per_client.max_connections_in`.
39+
40+
:func:`TSConnectionLimitExemptListAdd` adds one or more IP addresses or CIDR ranges specified in
41+
:arg:`ip_ranges` to the existing exempt list. The :arg:`ip_ranges` parameter can be a single
42+
IP address or CIDR range, or a comma-separated string of multiple ranges (e.g.,
43+
"192.168.1.10,10.0.0.0/8,172.16.0.0/12"). The ranges are added without removing any existing
44+
entries. Returns :enumerator:`TS_SUCCESS` if all ranges were successfully added, :enumerator:`TS_ERROR` if
45+
any of the IP ranges are invalid or if the operation fails.
46+
47+
:func:`TSConnectionLimitExemptListRemove` removes one or more IP addresses or CIDR ranges specified in
48+
:arg:`ip_ranges` from the existing exempt list. The :arg:`ip_ranges` parameter can be a single
49+
IP address or CIDR range, or a comma-separated string of multiple ranges. If a range is not present
50+
in the list, it is silently ignored. Returns :enumerator:`TS_SUCCESS` if all ranges were successfully
51+
processed, :enumerator:`TS_ERROR` if any of the IP ranges are invalid or if the operation fails.
52+
53+
:func:`TSConnectionLimitExemptListClear` removes all entries from the per-client connection
54+
limit exempt list. After calling this function, all clients will be subject to connection
55+
limits. This function does not return a value and never fails.
56+
57+
All functions are thread-safe and can be called from any plugin context. Changes made through
58+
these functions will override any configuration set via
59+
:ts:cv:`proxy.config.http.per_client.connection.exempt_list`.
60+
61+
Return Values
62+
-------------
63+
64+
:func:`TSConnectionLimitExemptListAdd` and :func:`TSConnectionLimitExemptListRemove` return
65+
:enumerator:`TS_SUCCESS` if the operation completed successfully, or :enumerator:`TS_ERROR` if the
66+
operation failed due to invalid input or system errors.
67+
68+
Examples
69+
--------
70+
71+
.. code-block:: cpp
72+
73+
#include <ts/ts.h>
74+
#include <fstream>
75+
#include <string>
76+
77+
void load_exempt_list_from_file(const char *filename) {
78+
std::ifstream file(filename);
79+
if (!file.is_open()) {
80+
TSError("Failed to open exempt list file: %s", filename);
81+
return;
82+
}
83+
84+
// Clear existing exempt list before loading from file
85+
TSConnectionLimitExemptListClear();
86+
87+
std::string line;
88+
int line_num = 0;
89+
while (std::getline(file, line)) {
90+
line_num++;
91+
92+
// Skip empty lines and comments
93+
if (line.empty() || line[0] == '#') {
94+
continue;
95+
}
96+
97+
// Add each IP range to the exempt list
98+
TSReturnCode result = TSConnectionLimitExemptListAdd(line.c_str());
99+
if (result != TS_SUCCESS) {
100+
TSError("Failed to add IP range '%s' from line %d in %s", line.c_str(), line_num, filename);
101+
} else {
102+
TSDebug("exempt_list", "Added IP range: %s", line.c_str());
103+
}
104+
}
105+
file.close();
106+
}
107+
108+
void TSPluginInit(int argc, const char *argv[]) {
109+
const char *exempt_file = "exempt_ips.txt";
110+
111+
// Check if custom file specified in plugin arguments
112+
if (argc > 1) {
113+
exempt_file = argv[1];
114+
}
115+
116+
// Load exempt list from file
117+
load_exempt_list_from_file(exempt_file);
118+
}
119+
120+
121+
See Also
122+
--------
123+
124+
:ts:cv:`proxy.config.net.per_client.max_connections_in`,
125+
:ts:cv:`proxy.config.http.per_client.connection.exempt_list`

include/iocore/net/ConnectionTracker.h

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,22 @@ class ConnectionTracker
8282

8383
/** Static configuration values. */
8484
struct GlobalConfig {
85+
GlobalConfig() = default;
86+
GlobalConfig(GlobalConfig const &);
87+
GlobalConfig &operator=(GlobalConfig const &);
88+
8589
std::chrono::seconds client_alert_delay{60}; ///< Alert delay in seconds.
8690
std::chrono::seconds server_alert_delay{60}; ///< Alert delay in seconds.
8791
bool metric_enabled{false}; ///< Enabling per server metrics.
8892
std::string metric_prefix; ///< Per server metric prefix.
93+
swoc::IPRangeSet client_exempt_list; ///< The set of IP addresses to not block due client connection counting.
8994
};
9095

9196
// The names of the configuration values.
9297
// Unfortunately these are not used in RecordsConfig.cc so that must be made consistent by hand.
9398
// Note: These need to be @c constexpr or there are static initialization ordering risks.
9499
static constexpr std::string_view CONFIG_CLIENT_VAR_ALERT_DELAY{"proxy.config.http.per_client.connection.alert_delay"};
100+
static constexpr std::string_view CONFIG_CLIENT_VAR_EXEMPT_LIST{"proxy.config.http.per_client.connection.exempt_list"};
95101
static constexpr std::string_view CONFIG_SERVER_VAR_MAX{"proxy.config.http.per_server.connection.max"};
96102
static constexpr std::string_view CONFIG_SERVER_VAR_MIN{"proxy.config.http.per_server.connection.min"};
97103
static constexpr std::string_view CONFIG_SERVER_VAR_MATCH{"proxy.config.http.per_server.connection.match"};
@@ -172,11 +178,18 @@ class ConnectionTracker
172178
std::shared_ptr<Group> _g; ///< Active group for this transaction.
173179
bool _reserved_p{false}; ///< Set if a connection slot has been reserved.
174180
bool _queued_p{false}; ///< Set if the connection is delayed / queued.
181+
bool _exempt_p{false}; ///< Set if the peer is in the connection exempt list.
175182

176183
/// Check if tracking is active.
177-
bool is_active();
184+
bool is_active() const;
185+
186+
/// Whether this group is in the connection max exempt list.
187+
/// @return @c true if this group should not be blocked due to
188+
/// proxy.config.net.per_client.max_connections_in.
189+
bool is_exempt() const;
178190

179191
/// Reserve a connection.
192+
/// @return the number of tracked connections.
180193
int reserve();
181194
/// Release a connection reservation.
182195
void release();
@@ -272,6 +285,42 @@ class ConnectionTracker
272285
*/
273286
static void config_init(GlobalConfig *global, TxnConfig *txn, RecConfigUpdateCb const &config_cb);
274287

288+
/** Set the client connection exempt list programmatically.
289+
*
290+
* This allows plugins to override the per-client connection exempt list with their own
291+
* IPRangeSet. This will replace the existing exempt list entirely.
292+
*
293+
* @param ip_ranges The IPRangeSet containing the addresses that should be exempt from per-client connection limits.
294+
* @return true if the exempt list was successfully updated, false otherwise.
295+
*/
296+
static bool set_client_exempt_list(swoc::IPRangeSet const &ip_ranges);
297+
298+
/** Add an IP range to the client connection exempt list.
299+
*
300+
* This allows plugins to add an additional IP range to the existing per-client connection exempt list.
301+
* The new range will be added to any existing ranges in the list.
302+
*
303+
* @param ip_range The IPRange containing the addresses to add to the exempt list.
304+
* @return true if the range was successfully added, false otherwise.
305+
*/
306+
static bool add_client_exempt_range(swoc::IPRange const &ip_range);
307+
308+
/** Remove an IP range from the client connection exempt list.
309+
*
310+
* This allows plugins to remove an IP range from the existing per-client connection exempt list.
311+
* If the range is not present in the list, the operation succeeds without error.
312+
*
313+
* @param ip_range The IPRange containing the addresses to remove from the exempt list.
314+
* @return true if the operation completed successfully, false otherwise.
315+
*/
316+
static bool remove_client_exempt_range(swoc::IPRange const &ip_range);
317+
318+
/** Clear all IP ranges from the client connection exempt list.
319+
*
320+
* This allows plugins to remove all entries from the per-client connection exempt list.
321+
*/
322+
static void clear_client_exempt_list();
323+
275324
/// Debug control used for debugging output.
276325
static inline DbgCtl dbg_ctl{"conn_track"};
277326

@@ -382,11 +431,17 @@ ConnectionTracker::Group::metric_name(const Key &key, std::string_view fqdn, std
382431
}
383432

384433
inline bool
385-
ConnectionTracker::TxnState::is_active()
434+
ConnectionTracker::TxnState::is_active() const
386435
{
387436
return nullptr != _g;
388437
}
389438

439+
inline bool
440+
ConnectionTracker::TxnState::is_exempt() const
441+
{
442+
return _exempt_p;
443+
}
444+
390445
inline int
391446
ConnectionTracker::TxnState::reserve()
392447
{

include/ts/ts.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,6 +2866,36 @@ TSReturnCode TSHostStatusGet(const char *hostname, const size_t hostname_len, TS
28662866
void TSHostStatusSet(const char *hostname, const size_t hostname_len, TSHostStatus status, const unsigned int down_time,
28672867
const unsigned int reason);
28682868

2869+
/*
2870+
* Add one or more IP addresses or CIDR ranges to the per-client connection limit exempt list.
2871+
* This function allows plugins to programmatically add to the list of IP addresses
2872+
* that should be exempt from per-client connection limits (see
2873+
* proxy.config.net.per_client.max_connections_in).
2874+
*
2875+
* @param ip_ranges The IP address or CIDR range to exempt, or a comma-separated list of ranges.
2876+
* @return TS_SUCCESS if the exempt list was successfully updated, TS_ERROR otherwise.
2877+
*/
2878+
TSReturnCode TSConnectionLimitExemptListAdd(std::string_view ip_ranges);
2879+
2880+
/*
2881+
* Remove one or more IP addresses or CIDR ranges from the per-client connection limit exempt list.
2882+
* This function allows plugins to programmatically remove from the list of IP addresses
2883+
* that should be exempt from per-client connection limits (see
2884+
* proxy.config.net.per_client.max_connections_in).
2885+
*
2886+
* @param ip_ranges The IP address or CIDR range to remove, or a comma-separated list of ranges.
2887+
* @return TS_SUCCESS if the exempt list was successfully updated, TS_ERROR otherwise.
2888+
*/
2889+
TSReturnCode TSConnectionLimitExemptListRemove(std::string_view ip_ranges);
2890+
2891+
/*
2892+
* Clear the per-client connection limit exempt list.
2893+
* This function allows plugins to programmatically clear the list of IP addresses
2894+
* that should be exempt from per-client connection limits (see
2895+
* proxy.config.net.per_client.max_connections_in).
2896+
*/
2897+
void TSConnectionLimitExemptListClear();
2898+
28692899
/*
28702900
* Set or get various HTTP Transaction control settings.
28712901
*/

lib/swoc/include/swoc/IPRange.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,13 @@ class IPRangeSet {
15701570
*/
15711571
self_type &fill(swoc::IPRange const &r);
15721572

1573+
/** Erase addresses from the set.
1574+
*
1575+
* @param r Range of addresses to erase.
1576+
* @return @a this
1577+
*/
1578+
self_type &erase(swoc::IPRange const &r);
1579+
15731580
/// @return @c true if @a addr is in the set.
15741581
bool contains(swoc::IPAddr const &addr) const;
15751582

@@ -1675,6 +1682,12 @@ IPRangeSet::fill(swoc::IPRange const &r) -> self_type & {
16751682
return *this;
16761683
}
16771684

1685+
inline auto
1686+
IPRangeSet::erase(swoc::IPRange const &r) -> self_type & {
1687+
_addrs.erase(r);
1688+
return *this;
1689+
}
1690+
16781691
inline bool
16791692
IPRangeSet::contains(swoc::IPAddr const &addr) const {
16801693
return _addrs.find(addr) != _addrs.end();

src/api/InkAPI.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989

9090
#include "mgmt/rpc/jsonrpc/JsonRPC.h"
9191
#include <swoc/bwf_base.h>
92+
#include <swoc/IPRange.h>
9293
#include "ts/ts.h"
9394

9495
/****************************************************************
@@ -9145,3 +9146,43 @@ TSHttpTxnTypeGet(TSHttpTxn txnp)
91459146
}
91469147
return retval;
91479148
}
9149+
9150+
TSReturnCode
9151+
TSConnectionLimitExemptListAdd(std::string_view ip_ranges)
9152+
{
9153+
swoc::TextView ip_ranges_tv{ip_ranges};
9154+
while (auto ip_range_tv = ip_ranges_tv.take_prefix_at(',')) {
9155+
swoc::IPRange ip_range;
9156+
if (!ip_range.load(ip_range_tv)) {
9157+
return TS_ERROR;
9158+
}
9159+
bool success = ConnectionTracker::add_client_exempt_range(ip_range);
9160+
if (!success) {
9161+
return TS_ERROR;
9162+
}
9163+
}
9164+
return TS_SUCCESS;
9165+
}
9166+
9167+
TSReturnCode
9168+
TSConnectionLimitExemptListRemove(std::string_view ip_ranges)
9169+
{
9170+
swoc::TextView ip_ranges_tv{ip_ranges};
9171+
while (auto ip_range_tv = ip_ranges_tv.take_prefix_at(',')) {
9172+
swoc::IPRange ip_range;
9173+
if (!ip_range.load(ip_range_tv)) {
9174+
return TS_ERROR;
9175+
}
9176+
bool success = ConnectionTracker::remove_client_exempt_range(ip_range);
9177+
if (!success) {
9178+
return TS_ERROR;
9179+
}
9180+
}
9181+
return TS_SUCCESS;
9182+
}
9183+
9184+
void
9185+
TSConnectionLimitExemptListClear()
9186+
{
9187+
ConnectionTracker::clear_client_exempt_list();
9188+
}

0 commit comments

Comments
 (0)