mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-04 00:25:29 +00:00
[master] fixed several RRL issues
3554. [bug] RRL failed to correctly rate-limit upward referrals and failed to count dropped error responses in the statistics. [RT #33225]
This commit is contained in:
4
CHANGES
4
CHANGES
@@ -1,3 +1,7 @@
|
|||||||
|
3554. [bug] RRL failed to correctly rate-limit upward
|
||||||
|
referrals and failed to count dropped error
|
||||||
|
responses in the statistics. [RT #33225]
|
||||||
|
|
||||||
3553. [bug] Address suspected double free in acache. [RT #33252]
|
3553. [bug] Address suspected double free in acache. [RT #33252]
|
||||||
|
|
||||||
3552. [bug] Wrong getopt option string for 'nsupdate -r'.
|
3552. [bug] Wrong getopt option string for 'nsupdate -r'.
|
||||||
|
@@ -1257,11 +1257,13 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
|
|||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Some error responses cannot be 'slipped',
|
* Some error responses cannot be 'slipped',
|
||||||
* so don't try.
|
* so don't try to slip any error responses.
|
||||||
* This will counted with dropped queries in the
|
|
||||||
* QryDropped counter.
|
|
||||||
*/
|
*/
|
||||||
if (!client->view->rrl->log_only) {
|
if (!client->view->rrl->log_only) {
|
||||||
|
isc_stats_increment(ns_g_server->nsstats,
|
||||||
|
dns_nsstatscounter_ratedropped);
|
||||||
|
isc_stats_increment(ns_g_server->nsstats,
|
||||||
|
dns_nsstatscounter_dropped);
|
||||||
ns_client_next(client, DNS_R_DROP);
|
ns_client_next(client, DNS_R_DROP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -191,7 +191,7 @@ inc_stats(ns_client_t *client, isc_statscounter_t counter) {
|
|||||||
|
|
||||||
/* Do query type statistics
|
/* Do query type statistics
|
||||||
*
|
*
|
||||||
* We only increment per-type if we're using the authoriative
|
* We only increment per-type if we're using the authoritative
|
||||||
* answer counter, preventing double-counting.
|
* answer counter, preventing double-counting.
|
||||||
*/
|
*/
|
||||||
if (counter == dns_nsstatscounter_authans) {
|
if (counter == dns_nsstatscounter_authans) {
|
||||||
@@ -6246,7 +6246,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
|
|||||||
* Rate limit these responses to this client.
|
* Rate limit these responses to this client.
|
||||||
*/
|
*/
|
||||||
if (client->view->rrl != NULL &&
|
if (client->view->rrl != NULL &&
|
||||||
fname != NULL && dns_name_isabsolute(fname) &&
|
((fname != NULL && dns_name_isabsolute(fname)) ||
|
||||||
|
(result == ISC_R_NOTFOUND && !RECURSIONOK(client))) &&
|
||||||
(client->query.attributes & NS_QUERYATTR_RRL_CHECKED) == 0) {
|
(client->query.attributes & NS_QUERYATTR_RRL_CHECKED) == 0) {
|
||||||
dns_rdataset_t nc_rdataset;
|
dns_rdataset_t nc_rdataset;
|
||||||
isc_boolean_t wouldlog;
|
isc_boolean_t wouldlog;
|
||||||
@@ -6289,8 +6290,19 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
|
|||||||
dns_rdataset_disassociate(&nc_rdataset);
|
dns_rdataset_disassociate(&nc_rdataset);
|
||||||
}
|
}
|
||||||
rrl_result = DNS_R_NXDOMAIN;
|
rrl_result = DNS_R_NXDOMAIN;
|
||||||
|
} else if (result == DNS_R_NXRRSET ||
|
||||||
|
result == DNS_R_EMPTYNAME) {
|
||||||
|
rrl_result = DNS_R_NXRRSET;
|
||||||
} else if (result == DNS_R_DELEGATION) {
|
} else if (result == DNS_R_DELEGATION) {
|
||||||
rrl_result = result;
|
rrl_result = result;
|
||||||
|
} else if (result == ISC_R_NOTFOUND) {
|
||||||
|
/*
|
||||||
|
* Handle referral to ".", including when recursion
|
||||||
|
* is off or not requested and the hints have not
|
||||||
|
* been loaded or we have "additional-from-cache no".
|
||||||
|
*/
|
||||||
|
tname = dns_rootname;
|
||||||
|
rrl_result = DNS_R_DELEGATION;
|
||||||
} else {
|
} else {
|
||||||
rrl_result = ISC_R_SUCCESS;
|
rrl_result = ISC_R_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@@ -1835,7 +1835,7 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
|
|||||||
return (ISC_R_SUCCESS);
|
return (ISC_R_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_RRL(obj, cond, pat, val1, val2) \
|
#define CHECK_RRL(cond, pat, val1, val2) \
|
||||||
do { \
|
do { \
|
||||||
if (!(cond)) { \
|
if (!(cond)) { \
|
||||||
cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, \
|
cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, \
|
||||||
@@ -1845,6 +1845,22 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define CHECK_RRL_RATE(rate, def, max_rate, name) \
|
||||||
|
do { \
|
||||||
|
obj = NULL; \
|
||||||
|
rrl->rate.str = name; \
|
||||||
|
result = cfg_map_get(map, name, &obj); \
|
||||||
|
if (result == ISC_R_SUCCESS) { \
|
||||||
|
rrl->rate.r = cfg_obj_asuint32(obj); \
|
||||||
|
CHECK_RRL(rrl->rate.r <= max_rate, \
|
||||||
|
name" %d > %d", \
|
||||||
|
rrl->rate.r, max_rate); \
|
||||||
|
} else { \
|
||||||
|
rrl->rate.r = def; \
|
||||||
|
} \
|
||||||
|
rrl->rate.scaled = rrl->rate.r; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
|
configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
|
||||||
const cfg_obj_t *obj;
|
const cfg_obj_t *obj;
|
||||||
@@ -1875,86 +1891,39 @@ configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
|
|||||||
result = cfg_map_get(map, "max-table-size", &obj);
|
result = cfg_map_get(map, "max-table-size", &obj);
|
||||||
if (result == ISC_R_SUCCESS) {
|
if (result == ISC_R_SUCCESS) {
|
||||||
i = cfg_obj_asuint32(obj);
|
i = cfg_obj_asuint32(obj);
|
||||||
CHECK_RRL(obj, i >= min_entries,
|
CHECK_RRL(i >= min_entries,
|
||||||
"max-table-size %d < min-table-size %d",
|
"max-table-size %d < min-table-size %d",
|
||||||
i, min_entries);
|
i, min_entries);
|
||||||
}
|
}
|
||||||
rrl->max_entries = i;
|
rrl->max_entries = i;
|
||||||
|
|
||||||
i = 0;
|
CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE,
|
||||||
obj = NULL;
|
"responses-per-second");
|
||||||
result = cfg_map_get(map, "responses-per-second", &obj);
|
CHECK_RRL_RATE(referrals_per_second,
|
||||||
if (result == ISC_R_SUCCESS) {
|
rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
|
||||||
i = cfg_obj_asuint32(obj);
|
"referrals-per-second");
|
||||||
CHECK_RRL(obj, i <= DNS_RRL_MAX_RATE,
|
CHECK_RRL_RATE(nodata_per_second,
|
||||||
"responses-per-second %d > %d",
|
rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
|
||||||
i, DNS_RRL_MAX_RATE);
|
"nodata-per-second");
|
||||||
}
|
CHECK_RRL_RATE(nxdomains_per_second,
|
||||||
rrl->responses_per_second = i;
|
rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
|
||||||
rrl->scaled_responses_per_second = rrl->responses_per_second;
|
"nxdomains-per-second");
|
||||||
|
CHECK_RRL_RATE(errors_per_second,
|
||||||
|
rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
|
||||||
|
"errors-per-second");
|
||||||
|
|
||||||
/*
|
CHECK_RRL_RATE(all_per_second, 0, DNS_RRL_MAX_RATE,
|
||||||
* The default error rate is the response rate,
|
"all-per-second");
|
||||||
* and so off by default.
|
|
||||||
*/
|
|
||||||
i = rrl->responses_per_second;
|
|
||||||
obj = NULL;
|
|
||||||
result = cfg_map_get(map, "errors-per-second", &obj);
|
|
||||||
if (result == ISC_R_SUCCESS) {
|
|
||||||
i = cfg_obj_asuint32(obj);
|
|
||||||
CHECK_RRL(obj, i <= DNS_RRL_MAX_RATE,
|
|
||||||
"errors-per-second %d > %d",
|
|
||||||
i, DNS_RRL_MAX_RATE);
|
|
||||||
}
|
|
||||||
rrl->errors_per_second = i;
|
|
||||||
rrl->scaled_errors_per_second = rrl->errors_per_second;
|
|
||||||
/*
|
|
||||||
* The default NXDOMAIN rate is the response rate,
|
|
||||||
* and so off by default.
|
|
||||||
*/
|
|
||||||
i = rrl->responses_per_second;
|
|
||||||
obj = NULL;
|
|
||||||
result = cfg_map_get(map, "nxdomains-per-second", &obj);
|
|
||||||
if (result == ISC_R_SUCCESS) {
|
|
||||||
i = cfg_obj_asuint32(obj);
|
|
||||||
CHECK_RRL(obj, i <= DNS_RRL_MAX_RATE,
|
|
||||||
"nxdomains-per-second %d > %d",
|
|
||||||
i, DNS_RRL_MAX_RATE);
|
|
||||||
}
|
|
||||||
rrl->nxdomains_per_second = i;
|
|
||||||
rrl->scaled_nxdomains_per_second = rrl->nxdomains_per_second;
|
|
||||||
|
|
||||||
/*
|
CHECK_RRL_RATE(slip, 2, DNS_RRL_MAX_SLIP,
|
||||||
* The all-per-second rate is off by default.
|
"slip");
|
||||||
*/
|
|
||||||
i = 0;
|
|
||||||
obj = NULL;
|
|
||||||
result = cfg_map_get(map, "all-per-second", &obj);
|
|
||||||
if (result == ISC_R_SUCCESS) {
|
|
||||||
i = cfg_obj_asuint32(obj);
|
|
||||||
CHECK_RRL(obj, i <= DNS_RRL_MAX_RATE, "all-per-second %d > %d",
|
|
||||||
i, DNS_RRL_MAX_RATE);
|
|
||||||
}
|
|
||||||
rrl->all_per_second = i;
|
|
||||||
rrl->scaled_all_per_second = rrl->all_per_second;
|
|
||||||
|
|
||||||
i = 2;
|
|
||||||
obj = NULL;
|
|
||||||
result = cfg_map_get(map, "slip", &obj);
|
|
||||||
if (result == ISC_R_SUCCESS) {
|
|
||||||
i = cfg_obj_asuint32(obj);
|
|
||||||
CHECK_RRL(obj, i <= DNS_RRL_MAX_SLIP,
|
|
||||||
"slip %d > %d", i, DNS_RRL_MAX_SLIP);
|
|
||||||
}
|
|
||||||
rrl->slip = i;
|
|
||||||
rrl->scaled_slip = rrl->slip;
|
|
||||||
|
|
||||||
i = 15;
|
i = 15;
|
||||||
obj = NULL;
|
obj = NULL;
|
||||||
result = cfg_map_get(map, "window", &obj);
|
result = cfg_map_get(map, "window", &obj);
|
||||||
if (result == ISC_R_SUCCESS) {
|
if (result == ISC_R_SUCCESS) {
|
||||||
i = cfg_obj_asuint32(obj);
|
i = cfg_obj_asuint32(obj);
|
||||||
CHECK_RRL(obj, i >= 1 && i <= DNS_RRL_MAX_WINDOW,
|
CHECK_RRL(i >= 1 && i <= DNS_RRL_MAX_WINDOW,
|
||||||
"window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW);
|
"window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW);
|
||||||
}
|
}
|
||||||
rrl->window = i;
|
rrl->window = i;
|
||||||
@@ -1964,18 +1933,18 @@ configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
|
|||||||
result = cfg_map_get(map, "qps-scale", &obj);
|
result = cfg_map_get(map, "qps-scale", &obj);
|
||||||
if (result == ISC_R_SUCCESS) {
|
if (result == ISC_R_SUCCESS) {
|
||||||
i = cfg_obj_asuint32(obj);
|
i = cfg_obj_asuint32(obj);
|
||||||
CHECK_RRL(obj, i >= 1, "invalid 'qps-scale %d'%s", i, "");
|
CHECK_RRL(i >= 1, "invalid 'qps-scale %d'%s", i, "");
|
||||||
}
|
}
|
||||||
rrl->qps_scale = i;
|
rrl->qps_scale = i;
|
||||||
rrl->qps = 1.0;
|
rrl->qps = 1.0;
|
||||||
|
|
||||||
i = 24;
|
i = 24;
|
||||||
obj = NULL;
|
obj = NULL;
|
||||||
result = cfg_map_get(map, "IPv4-prefix-length", &obj);
|
result = cfg_map_get(map, "ipv4-prefix-length", &obj);
|
||||||
if (result == ISC_R_SUCCESS) {
|
if (result == ISC_R_SUCCESS) {
|
||||||
i = cfg_obj_asuint32(obj);
|
i = cfg_obj_asuint32(obj);
|
||||||
CHECK_RRL(obj, i >= 8 && i <= 32,
|
CHECK_RRL(i >= 8 && i <= 32,
|
||||||
"invalid 'IPv4-prefix-length %d'%s", i, "");
|
"invalid 'ipv4-prefix-length %d'%s", i, "");
|
||||||
}
|
}
|
||||||
rrl->ipv4_prefixlen = i;
|
rrl->ipv4_prefixlen = i;
|
||||||
if (i == 32)
|
if (i == 32)
|
||||||
@@ -1985,11 +1954,11 @@ configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
|
|||||||
|
|
||||||
i = 56;
|
i = 56;
|
||||||
obj = NULL;
|
obj = NULL;
|
||||||
result = cfg_map_get(map, "IPv6-prefix-length", &obj);
|
result = cfg_map_get(map, "ipv6-prefix-length", &obj);
|
||||||
if (result == ISC_R_SUCCESS) {
|
if (result == ISC_R_SUCCESS) {
|
||||||
i = cfg_obj_asuint32(obj);
|
i = cfg_obj_asuint32(obj);
|
||||||
CHECK_RRL(obj, i >= 16 && i <= DNS_RRL_MAX_PREFIX,
|
CHECK_RRL(i >= 16 && i <= DNS_RRL_MAX_PREFIX,
|
||||||
"IPv6-prefix-length %d < 16 or > %d",
|
"ipv6-prefix-length %d < 16 or > %d",
|
||||||
i, DNS_RRL_MAX_PREFIX);
|
i, DNS_RRL_MAX_PREFIX);
|
||||||
}
|
}
|
||||||
rrl->ipv6_prefixlen = i;
|
rrl->ipv6_prefixlen = i;
|
||||||
@@ -2010,7 +1979,7 @@ configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
|
|||||||
result = cfg_acl_fromconfig(obj, config, ns_g_lctx,
|
result = cfg_acl_fromconfig(obj, config, ns_g_lctx,
|
||||||
ns_g_aclconfctx, ns_g_mctx,
|
ns_g_aclconfctx, ns_g_mctx,
|
||||||
0, &rrl->exempt);
|
0, &rrl->exempt);
|
||||||
CHECK_RRL(obj, result == ISC_R_SUCCESS,
|
CHECK_RRL(result == ISC_R_SUCCESS,
|
||||||
"invalid %s%s", "address match list", "");
|
"invalid %s%s", "address match list", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,5 +17,5 @@
|
|||||||
# Clean up after rrl tests.
|
# Clean up after rrl tests.
|
||||||
|
|
||||||
rm -f dig.out*
|
rm -f dig.out*
|
||||||
rm -f */named.memstats */named.run */named.stats */log */session.key
|
rm -f */named.memstats */named.run */named.stats */log-* */session.key
|
||||||
rm -f ns3/bl*.db */*.jnl */*.core */*.pid
|
rm -f ns3/bl*.db */*.jnl */*.core */*.pid
|
||||||
|
@@ -32,15 +32,14 @@ options {
|
|||||||
rate-limit {
|
rate-limit {
|
||||||
responses-per-second 2;
|
responses-per-second 2;
|
||||||
all-per-second 70;
|
all-per-second 70;
|
||||||
IPv4-prefix-length 24;
|
|
||||||
IPv6-prefix-length 64;
|
|
||||||
slip 3;
|
slip 3;
|
||||||
/* qps-scale 2; */
|
|
||||||
exempt-clients { 10.53.0.7; };
|
exempt-clients { 10.53.0.7; };
|
||||||
window 1;
|
|
||||||
max-table-size 100;
|
// small enough to force a table expansion
|
||||||
min-table-size 2;
|
min-table-size 75;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
additional-from-cache no;
|
||||||
};
|
};
|
||||||
|
|
||||||
key rndc_key {
|
key rndc_key {
|
||||||
|
@@ -22,8 +22,10 @@ $TTL 120
|
|||||||
NS .
|
NS .
|
||||||
ns A 10.53.0.2
|
ns A 10.53.0.2
|
||||||
|
|
||||||
|
; basic rate limiting
|
||||||
a1 A 192.0.2.1
|
a1 A 192.0.2.1
|
||||||
|
|
||||||
|
; wildcards
|
||||||
*.a2 A 192.0.2.2
|
*.a2 A 192.0.2.2
|
||||||
|
|
||||||
; a3 is in tld3
|
; a3 is in tld3
|
||||||
@@ -38,5 +40,8 @@ a6 A 192.0.2.6
|
|||||||
|
|
||||||
; a7 for SERVFAIL
|
; a7 for SERVFAIL
|
||||||
|
|
||||||
; a8 for all-per-second limit
|
; a8 for NODATA
|
||||||
$GENERATE 101-180 all$.a8 A 192.0.2.8
|
a8 A 192.0.2.8
|
||||||
|
|
||||||
|
; a9 for all-per-second limit
|
||||||
|
$GENERATE 101-180 all$.a9 A 192.0.2.8
|
||||||
|
@@ -27,6 +27,22 @@ options {
|
|||||||
listen-on { 10.53.0.3; };
|
listen-on { 10.53.0.3; };
|
||||||
listen-on-v6 { none; };
|
listen-on-v6 { none; };
|
||||||
notify no;
|
notify no;
|
||||||
|
|
||||||
|
// check that all of the options are parsed without limiting anything
|
||||||
|
rate-limit {
|
||||||
|
responses-per-second 200;
|
||||||
|
referrals-per-second 220;
|
||||||
|
nodata-per-second 230;
|
||||||
|
nxdomains-per-second 240;
|
||||||
|
errors-per-second 250;
|
||||||
|
all-per-second 700;
|
||||||
|
ipv4-prefix-length 24;
|
||||||
|
ipv6-prefix-length 64;
|
||||||
|
qps-scale 10;
|
||||||
|
window 1;
|
||||||
|
max-table-size 1000;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
zone "." { type hint; file "hints"; };
|
zone "." { type hint; file "hints"; };
|
||||||
|
@@ -64,15 +64,20 @@ sec_start () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# turn off ${HOME}/.digrc
|
||||||
|
HOME=/dev/null; export HOME
|
||||||
|
|
||||||
# $1=result name $2=domain name $3=dig options
|
# $1=result name $2=domain name $3=dig options
|
||||||
digcmd () {
|
digcmd () {
|
||||||
OFILE=$1; shift
|
OFILE=$1; shift
|
||||||
DIG_DOM=$1; shift
|
DIG_DOM=$1; shift
|
||||||
ARGS="+noadd +noauth +nosearch +time=1 +tries=1 +ignore $* -p 5300 $DIG_DOM @$ns2"
|
ARGS="+nosearch +time=1 +tries=1 +ignore -p 5300 $* $DIG_DOM @$ns2"
|
||||||
#echo I:dig $ARGS 1>&2
|
#echo I:dig $ARGS 1>&2
|
||||||
START=`date +%y%m%d%H%M.%S`
|
START=`date +%y%m%d%H%M.%S`
|
||||||
RESULT=`$DIG $ARGS 2>&1 | tee $OFILE=TEMP \
|
RESULT=`$DIG $ARGS 2>&1 | tee $OFILE=TEMP \
|
||||||
| sed -n -e 's/^[^;].* \([^ ]\{1,\}\)$/\1/p' \
|
| sed -n -e '/^;; AUTHORITY/,/^$/d' \
|
||||||
|
-e '/^;; ADDITIONAL/,/^$/d' \
|
||||||
|
-e 's/^[^;].* \([^ ]\{1,\}\)$/\1/p' \
|
||||||
-e 's/;; flags.* tc .*/TC/p' \
|
-e 's/;; flags.* tc .*/TC/p' \
|
||||||
-e 's/;; .* status: NXDOMAIN.*/NXDOMAIN/p' \
|
-e 's/;; .* status: NXDOMAIN.*/NXDOMAIN/p' \
|
||||||
-e 's/;; .* status: SERVFAIL.*/SERVFAIL/p' \
|
-e 's/;; .* status: SERVFAIL.*/SERVFAIL/p' \
|
||||||
@@ -117,7 +122,7 @@ ck_result() {
|
|||||||
NXDOMAIN=`ls dig.out-$1-*=NXDOMAIN 2>/dev/null | wc -l | tr -d ' '`
|
NXDOMAIN=`ls dig.out-$1-*=NXDOMAIN 2>/dev/null | wc -l | tr -d ' '`
|
||||||
SERVFAIL=`ls dig.out-$1-*=SERVFAIL 2>/dev/null | wc -l | tr -d ' '`
|
SERVFAIL=`ls dig.out-$1-*=SERVFAIL 2>/dev/null | wc -l | tr -d ' '`
|
||||||
if test $ADDRS -ne "$3"; then
|
if test $ADDRS -ne "$3"; then
|
||||||
setret "I:$ADDRS instead of $3 $2 responses for $1"
|
setret "I:$ADDRS instead of $3 '$2' responses for $1"
|
||||||
BAD=yes
|
BAD=yes
|
||||||
fi
|
fi
|
||||||
if test $TC -ne "$4"; then
|
if test $TC -ne "$4"; then
|
||||||
@@ -142,26 +147,47 @@ ck_result() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ckstats () {
|
||||||
|
LABEL="$1"; shift
|
||||||
|
TYPE="$1"; shift
|
||||||
|
EXPECTED="$1"; shift
|
||||||
|
CNT=`sed -n -e "s/[ ]*\([0-9]*\).responses $TYPE for rate limits.*/\1/p" \
|
||||||
|
ns2/named.stats | tail -1`
|
||||||
|
CNT=`expr 0$CNT + 0`
|
||||||
|
if test "$CNT" -ne $EXPECTED; then
|
||||||
|
setret "I:wrong $LABEL $TYPE statistics of $CNT instead of $EXPECTED"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#########
|
#########
|
||||||
sec_start
|
sec_start
|
||||||
|
|
||||||
|
# Tests of referrals to "." must be done before the hints are loaded
|
||||||
|
# or with "additional-from-cache no"
|
||||||
|
burst 5 a1.tld3 +norec
|
||||||
# basic rate limiting
|
# basic rate limiting
|
||||||
burst 3 a1.tld2
|
burst 3 a1.tld2
|
||||||
# 1 second delay allows an additional response.
|
# 1 second delay allows an additional response.
|
||||||
sleep 1
|
sleep 1
|
||||||
burst 21 a1.tld2
|
burst 21 a1.tld2
|
||||||
# request 30 different qnames to try a wild card
|
# Request 30 different qnames to try a wildcard.
|
||||||
burst 30 'x$CNT.a2.tld2'
|
burst 30 'x$CNT.a2.tld2'
|
||||||
|
# These should be counted and limited but are not. See RT33138.
|
||||||
|
burst 10 'y.x$CNT.a2.tld2'
|
||||||
|
|
||||||
# IP TC drop NXDOMAIN SERVFAIL
|
# IP TC drop NXDOMAIN SERVFAIL
|
||||||
# check for 24 results
|
# referrals to "."
|
||||||
# including the 1 second delay
|
ck_result a1.tld3 '' 2 1 2 0 0
|
||||||
|
# check 24 results including 1 second delay that allows an additional response
|
||||||
ck_result a1.tld2 192.0.2.1 3 7 14 0 0
|
ck_result a1.tld2 192.0.2.1 3 7 14 0 0
|
||||||
|
|
||||||
# Check the wild card answers.
|
# Check the wild card answers.
|
||||||
# The parent name of the 30 requests is counted.
|
# The parent name of the 30 requests is counted.
|
||||||
ck_result 'x*.a2.tld2' 192.0.2.2 2 10 18 0 0
|
ck_result 'x*.a2.tld2' 192.0.2.2 2 10 18 0 0
|
||||||
|
|
||||||
|
# These should be limited but are not. See RT33138.
|
||||||
|
ck_result 'y.x*.a2.tld2' 192.0.2.2 10 0 0 0 0
|
||||||
|
|
||||||
#########
|
#########
|
||||||
sec_start
|
sec_start
|
||||||
@@ -178,6 +204,10 @@ ck_result 'y*.a3.tld3' 192.0.3.3 3 6 12 0 0
|
|||||||
# NXDOMAIN responses are also limited based on the parent name.
|
# NXDOMAIN responses are also limited based on the parent name.
|
||||||
ck_result 'z*.a4.tld2' x 0 6 12 2 0
|
ck_result 'z*.a4.tld2' x 0 6 12 2 0
|
||||||
|
|
||||||
|
$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
|
||||||
|
ckstats first dropped 58
|
||||||
|
ckstats first truncated 30
|
||||||
|
|
||||||
|
|
||||||
#########
|
#########
|
||||||
sec_start
|
sec_start
|
||||||
@@ -185,6 +215,9 @@ sec_start
|
|||||||
burst 20 a5.tld2 +tcp
|
burst 20 a5.tld2 +tcp
|
||||||
burst 20 a6.tld2 -b $ns7
|
burst 20 a6.tld2 -b $ns7
|
||||||
burst 20 a7.tld4
|
burst 20 a7.tld4
|
||||||
|
burst 2 a8.tld2 AAAA
|
||||||
|
burst 2 a8.tld2 TXT
|
||||||
|
burst 2 a8.tld2 SPF
|
||||||
|
|
||||||
# TCP responses are not rate limited
|
# TCP responses are not rate limited
|
||||||
ck_result a5.tld2 192.0.2.5 20 0 0 0 0
|
ck_result a5.tld2 192.0.2.5 20 0 0 0 0
|
||||||
@@ -196,6 +229,13 @@ ck_result a6.tld2 192.0.2.6 20 0 0 0 0
|
|||||||
# other rate limiting can be triggered before the SERVFAIL limit is reached.
|
# other rate limiting can be triggered before the SERVFAIL limit is reached.
|
||||||
ck_result a7.tld4 192.0.2.1 0 6 12 0 2
|
ck_result a7.tld4 192.0.2.1 0 6 12 0 2
|
||||||
|
|
||||||
|
# NODATA responses are counted as the same regardless of qtype.
|
||||||
|
ck_result a8.tld2 '' 2 2 2 0 0
|
||||||
|
|
||||||
|
$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
|
||||||
|
ckstats second dropped 72
|
||||||
|
ckstats second truncated 38
|
||||||
|
|
||||||
|
|
||||||
#########
|
#########
|
||||||
sec_start
|
sec_start
|
||||||
@@ -203,23 +243,14 @@ sec_start
|
|||||||
# all-per-second
|
# all-per-second
|
||||||
# The qnames are all unique but the client IP address is constant.
|
# The qnames are all unique but the client IP address is constant.
|
||||||
CNT=101
|
CNT=101
|
||||||
burst 80 'all$CNT.a8.tld2'
|
burst 80 'all$CNT.a9.tld2'
|
||||||
ck_result 'a*.a8.tld2' 192.0.2.8 70 0 10 0 0
|
|
||||||
|
|
||||||
|
ck_result 'a*.a9.tld2' 192.0.2.8 70 0 10 0 0
|
||||||
|
|
||||||
$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
|
$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
|
||||||
ckstats () {
|
ckstats final dropped 82
|
||||||
CNT=`sed -n -e "s/[ ]*\([0-9]*\).responses $1 for rate limits.*/\1/p" \
|
ckstats final truncated 38
|
||||||
ns2/named.stats`
|
|
||||||
CNT=`expr 0$CNT + 0`
|
|
||||||
if test "$CNT" -ne $2; then
|
|
||||||
setret "I:wrong $1 statistics of $CNT instead of $2"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
ckstats dropped 77
|
|
||||||
ckstats truncated 35
|
|
||||||
|
|
||||||
echo "I:exit status: $ret"
|
echo "I:exit status: $ret"
|
||||||
# exit $ret
|
exit $ret
|
||||||
[ $ret -ne 0 ] && echo "I:test failure overridden"
|
|
||||||
exit 0
|
|
||||||
|
@@ -5449,7 +5449,7 @@ badresp:1,adberr:0,findfail:0,valfail:0]
|
|||||||
<optional> filter-aaaa-on-v4 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional>
|
<optional> filter-aaaa-on-v4 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional>
|
||||||
<optional> filter-aaaa-on-v6 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional>
|
<optional> filter-aaaa-on-v6 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional>
|
||||||
<optional> filter-aaaa { <replaceable>address_match_list</replaceable> }; </optional>
|
<optional> filter-aaaa { <replaceable>address_match_list</replaceable> }; </optional>
|
||||||
<optional> dns64 <replaceable>IPv6-prefix</replaceable> {
|
<optional> dns64 <replaceable>ipv6-prefix</replaceable> {
|
||||||
<optional> clients { <replaceable>address_match_list</replaceable> }; </optional>
|
<optional> clients { <replaceable>address_match_list</replaceable> }; </optional>
|
||||||
<optional> mapped { <replaceable>address_match_list</replaceable> }; </optional>
|
<optional> mapped { <replaceable>address_match_list</replaceable> }; </optional>
|
||||||
<optional> exclude { <replaceable>address_match_list</replaceable> }; </optional>
|
<optional> exclude { <replaceable>address_match_list</replaceable> }; </optional>
|
||||||
@@ -5487,14 +5487,16 @@ badresp:1,adberr:0,findfail:0,valfail:0]
|
|||||||
<optional> deny-answer-aliases { <replaceable>namelist</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
|
<optional> deny-answer-aliases { <replaceable>namelist</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
|
||||||
<optional> rate-limit {
|
<optional> rate-limit {
|
||||||
<optional> responses-per-second <replaceable>number</replaceable> ; </optional>
|
<optional> responses-per-second <replaceable>number</replaceable> ; </optional>
|
||||||
<optional> errors-per-second <replaceable>number</replaceable> ; </optional>
|
<optional> referrals-per-second <replaceable>number</replaceable> ; </optional>
|
||||||
|
<optional> nodata-per-second <replaceable>number</replaceable> ; </optional>
|
||||||
<optional> nxdomains-per-second <replaceable>number</replaceable> ; </optional>
|
<optional> nxdomains-per-second <replaceable>number</replaceable> ; </optional>
|
||||||
|
<optional> errors-per-second <replaceable>number</replaceable> ; </optional>
|
||||||
<optional> all-per-second <replaceable>number</replaceable> ; </optional>
|
<optional> all-per-second <replaceable>number</replaceable> ; </optional>
|
||||||
<optional> window <replaceable>number</replaceable> ; </optional>
|
<optional> window <replaceable>number</replaceable> ; </optional>
|
||||||
<optional> log-only <replaceable>yes_or_no</replaceable> ; </optional>
|
<optional> log-only <replaceable>yes_or_no</replaceable> ; </optional>
|
||||||
<optional> qps-scale <replaceable>number</replaceable> ; </optional>
|
<optional> qps-scale <replaceable>number</replaceable> ; </optional>
|
||||||
<optional> IPv4-prefix-length <replaceable>number</replaceable> ; </optional>
|
<optional> ipv4-prefix-length <replaceable>number</replaceable> ; </optional>
|
||||||
<optional> IPv6-prefix-length <replaceable>number</replaceable> ; </optional>
|
<optional> ipv6-prefix-length <replaceable>number</replaceable> ; </optional>
|
||||||
<optional> slip <replaceable>number</replaceable> ; </optional>
|
<optional> slip <replaceable>number</replaceable> ; </optional>
|
||||||
<optional> exempt-clients { <replaceable>address_match_list</replaceable> } ; </optional>
|
<optional> exempt-clients { <replaceable>address_match_list</replaceable> } ; </optional>
|
||||||
<optional> max-table-size <replaceable>number</replaceable> ; </optional>
|
<optional> max-table-size <replaceable>number</replaceable> ; </optional>
|
||||||
@@ -10089,61 +10091,95 @@ ns.domain.com.rpz-nsdname CNAME .
|
|||||||
</sect3>
|
</sect3>
|
||||||
|
|
||||||
<sect3>
|
<sect3>
|
||||||
<title>Rate Limiting</title>
|
<title>Response Rate Limiting</title>
|
||||||
<para>
|
<para>
|
||||||
Excessive essentially identical UDP <emphasis>responses</emphasis>
|
Excessive almost identical UDP <emphasis>responses</emphasis>
|
||||||
can be discarded by configuring a
|
can be controlled by configuring a
|
||||||
<command>rate-limit</command> clause in an
|
<command>rate-limit</command> clause in an
|
||||||
<command>options</command> statement.
|
<command>options</command> or <command>view</command> statement.
|
||||||
This mechanism keeps BIND 9 from being used
|
This mechanism keeps authoritative BIND 9 from being used
|
||||||
in amplifying reflection denial of service attacks
|
in amplifying reflection denial of service (DoS) attacks.
|
||||||
as well as partially protecting BIND 9 itself from
|
Short truncated (TC=1) responses can be sent to provide
|
||||||
some denial of service attacks.
|
rate-limited responses to legitimate clients within
|
||||||
Very short truncated responses can be sent to provide
|
a range of forged, attacked IP addresses.
|
||||||
rate-limited responses to legitimate
|
Legitimate clients react to dropped or truncated response
|
||||||
clients within a range of attacked and forged IP addresses,
|
by retrying with UDP or with TCP respectively.
|
||||||
Legitimate clients react to truncated response by retrying
|
|
||||||
with TCP.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Rate limiting works by setting
|
This mechanism is intended for authoritative DNS servers.
|
||||||
<command>responses-per-second</command>
|
It can be used on recursive servers but can slow
|
||||||
to a number of repetitions per second for responses for a given name
|
applications such as SMTP servers (mail receivers) and
|
||||||
and record type to a DNS client.
|
HTTP clients (web browsers) that repeatedly request the
|
||||||
|
same domains.
|
||||||
|
When possible, closing "open" recursive servers is better.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<command>Responses-per-second</command> is a limit on
|
Response rate limiting uses a "credit" or "token bucket" scheme.
|
||||||
identical responses instead of a limit on all responses or
|
Each combination of identical response and client
|
||||||
even all responses to a single client.
|
has a conceptual account that earns a specified number
|
||||||
10 identical responses per second is a generous limit except perhaps
|
of credits every second.
|
||||||
when many clients are using a single IP address via network
|
A prospective response debits its account by one.
|
||||||
address translation (NAT).
|
Responses are dropped or truncated
|
||||||
The default limit of zero specifies an unbounded limit to turn off
|
while the account is negative.
|
||||||
rate-limiting in a view or to only rate-limit NXDOMAIN or other
|
Responses are tracked within a rolling window of time
|
||||||
errors.
|
which defaults to 15 seconds, but can be configured with
|
||||||
|
the <command>window</command> option to any value from
|
||||||
|
1 to 3600 seconds (1 hour).
|
||||||
|
The account cannot become more positive than
|
||||||
|
the per-second limit
|
||||||
|
or more negative than <command>window</command>
|
||||||
|
times the per-second limit.
|
||||||
|
When the specified number of credits for a class of
|
||||||
|
responses is set to 0, those responses are not rate limited.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The notion of "identical responses"
|
The notions of "identical response" and "DNS client"
|
||||||
and "single DNS client" cannot be simplistic.
|
for rate limiting are not simplistic.
|
||||||
All responses to a CIDR block with prefix
|
All responses to an address block are counted as if to a
|
||||||
length specified with <command>IPv4-prefix-length</command>
|
single client.
|
||||||
(default 24) or <command>IPv6-prefix-length</command>
|
The prefix lengths of addresses blocks are
|
||||||
(default 56) are assumed to come from a single DNS client.
|
specified with <command>ipv4-prefix-length</command> (default 24)
|
||||||
Requests for a name that result in DNS NXDOMAIN
|
and <command>ipv6-prefix-length</command> (default 56).
|
||||||
errors are considered identical.
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
All non-empty responses for a valid domain name (qname)
|
||||||
|
and record type (qtype) are identical and have a limit specified
|
||||||
|
with <command>responses-per-second</command>
|
||||||
|
(default 0 or no limit).
|
||||||
|
All empty (NODATA) responses for a valid domain,
|
||||||
|
regardless of query type, are identical.
|
||||||
|
Responses in the NODATA class are limited by
|
||||||
|
<command>nodata-per-second</command>
|
||||||
|
(default <command>responses-per-second</command>).
|
||||||
|
Requests for any and all undefined subdomains of a given
|
||||||
|
valid domain result in NXDOMAIN errors, and are identical
|
||||||
|
regardless of query type.
|
||||||
|
They are limited by <command>nxdomain-per-second</command>
|
||||||
|
(default <command>responses-per-second</command>).
|
||||||
This controls some attacks using random names, but
|
This controls some attacks using random names, but
|
||||||
accommodates servers that expect many legitimate NXDOMAIN responses
|
can be relaxed or turned off (set to 0)
|
||||||
such as anti-spam blacklists.
|
on servers that expect many legitimate
|
||||||
By default the limit on NXDOMAIN errors is the same as the
|
NXDOMAIN responses, such as from anti-spam blacklists.
|
||||||
<command>responses-per-second</command> value,
|
Referrals or delegations to the server of a given
|
||||||
but it can be set separately with
|
domain are identical and are limited by
|
||||||
<command>nxdomains-per-second</command>.
|
<command>referrals-per-second</command>
|
||||||
All requests for all names or types that result in DNS errors
|
(default <command>responses-per-second</command>).
|
||||||
such as SERVFAIL and FORMERR (but not NXDOMAIN) are considered
|
</para>
|
||||||
identical.
|
|
||||||
|
<para>
|
||||||
|
Responses generated from local wildcards are counted and limited
|
||||||
|
as if they were for the parent domain name.
|
||||||
|
This controls flooding using random.wild.example.com.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
All requests that result in DNS errors other
|
||||||
|
than NXDOMAIN, such as SERVFAIL and FORMERR, are identical
|
||||||
|
regardless of requested name (qname) or record type (qtype).
|
||||||
This controls attacks using invalid requests or distant,
|
This controls attacks using invalid requests or distant,
|
||||||
broken authoritative servers.
|
broken authoritative servers.
|
||||||
By default the limit on errors is the same as the
|
By default the limit on errors is the same as the
|
||||||
@@ -10152,33 +10188,6 @@ ns.domain.com.rpz-nsdname CNAME .
|
|||||||
<command>errors-per-second</command>.
|
<command>errors-per-second</command>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
|
||||||
Rate limiting uses a "credit" or "token bucket" scheme.
|
|
||||||
Each identical response has a conceptual account
|
|
||||||
that is given <command>responses-per-second</command>,
|
|
||||||
<command>errors-per-second</command>, and
|
|
||||||
<command>nxdomains-per-second</command> credits every second.
|
|
||||||
A DNS request triggering some desired response debits
|
|
||||||
the account by one.
|
|
||||||
Responses are not sent while the account is negative.
|
|
||||||
The account cannot become more positive than
|
|
||||||
the per-second limit
|
|
||||||
or more negative than <command>window</command>
|
|
||||||
times the per-second limit.
|
|
||||||
A DNS client that sends requests that are not
|
|
||||||
answered can be penalized for up to <command>window</command>
|
|
||||||
seconds (default 15).
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Responses generated from local wildcards are counted and limited
|
|
||||||
as if they were for the parent domain name.
|
|
||||||
This prevents flooding by requesting random.wild.example.com.
|
|
||||||
For similar reasons, NXDOMAIN responses are counted and rate
|
|
||||||
limited by the valid domain name nearest to the
|
|
||||||
query name with an SOA record.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Many attacks using DNS involve UDP requests with forged source
|
Many attacks using DNS involve UDP requests with forged source
|
||||||
addresses.
|
addresses.
|
||||||
@@ -10188,14 +10197,15 @@ ns.domain.com.rpz-nsdname CNAME .
|
|||||||
There is a mechanism that can answer some legitimate
|
There is a mechanism that can answer some legitimate
|
||||||
requests from a client whose address is being forged in a flood.
|
requests from a client whose address is being forged in a flood.
|
||||||
Setting <command>slip</command> to 2 (its default) causes every
|
Setting <command>slip</command> to 2 (its default) causes every
|
||||||
other UDP request to be answered with a small response
|
other UDP request to be answered with a small truncated (TC=1)
|
||||||
claiming that the response would have been truncated.
|
response.
|
||||||
The small size and relative infrequency of the response make
|
The small size and reduced frequency, and so lack of
|
||||||
it unattractive for abuse.
|
amplification, of "slipped" responses make them unattractive
|
||||||
<command>Slip</command> must be between 0 and 10.
|
for reflection DoS attacks.
|
||||||
A value of 0 does not "slip"
|
<command>slip</command> must be between 0 and 10.
|
||||||
or sends no rate limiting truncated responses.
|
A value of 0 does not "slip";
|
||||||
Some error responses includinge REFUSED and SERVFAIL
|
no truncated responses are sent due to rate limiting.
|
||||||
|
Some error responses including REFUSED and SERVFAIL
|
||||||
cannot be replaced with truncated responses and are instead
|
cannot be replaced with truncated responses and are instead
|
||||||
leaked at the <command>slip</command> rate.
|
leaked at the <command>slip</command> rate.
|
||||||
</para>
|
</para>
|
||||||
@@ -10225,8 +10235,8 @@ ns.domain.com.rpz-nsdname CNAME .
|
|||||||
<command>rate-limit</command> statements in <command>view</command>
|
<command>rate-limit</command> statements in <command>view</command>
|
||||||
statements instead of the global <command>option</command>
|
statements instead of the global <command>option</command>
|
||||||
statement.
|
statement.
|
||||||
A <command>rate-limit</command> statement in a view replaces
|
A <command>rate-limit</command> statement in a view replaces,
|
||||||
instead of being merged with a <command>rate-limit</command>
|
rather than supplementing, a <command>rate-limit</command>
|
||||||
statement among the main options.
|
statement among the main options.
|
||||||
DNS clients within a view can be exempted from rate limits
|
DNS clients within a view can be exempted from rate limits
|
||||||
with the <command>exempt-clients</command> clause.
|
with the <command>exempt-clients</command> clause.
|
||||||
|
@@ -71,7 +71,8 @@ typedef struct dns_rrl_hash dns_rrl_hash_t;
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
DNS_RRL_RTYPE_FREE = 0,
|
DNS_RRL_RTYPE_FREE = 0,
|
||||||
DNS_RRL_RTYPE_QUERY,
|
DNS_RRL_RTYPE_QUERY,
|
||||||
DNS_RRL_RTYPE_DELEGATION,
|
DNS_RRL_RTYPE_REFERRAL,
|
||||||
|
DNS_RRL_RTYPE_NODATA,
|
||||||
DNS_RRL_RTYPE_NXDOMAIN,
|
DNS_RRL_RTYPE_NXDOMAIN,
|
||||||
DNS_RRL_RTYPE_ERROR,
|
DNS_RRL_RTYPE_ERROR,
|
||||||
DNS_RRL_RTYPE_ALL,
|
DNS_RRL_RTYPE_ALL,
|
||||||
@@ -190,6 +191,13 @@ struct dns_rrl_qname_buf {
|
|||||||
dns_fixedname_t qname;
|
dns_fixedname_t qname;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct dns_rrl_rate dns_rrl_rate_t;
|
||||||
|
struct dns_rrl_rate {
|
||||||
|
int r;
|
||||||
|
int scaled;
|
||||||
|
const char *str;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Per-view query rate limit parameters and a pointer to database.
|
* Per-view query rate limit parameters and a pointer to database.
|
||||||
*/
|
*/
|
||||||
@@ -199,12 +207,14 @@ struct dns_rrl {
|
|||||||
isc_mem_t *mctx;
|
isc_mem_t *mctx;
|
||||||
|
|
||||||
isc_boolean_t log_only;
|
isc_boolean_t log_only;
|
||||||
int responses_per_second;
|
dns_rrl_rate_t responses_per_second;
|
||||||
int errors_per_second;
|
dns_rrl_rate_t referrals_per_second;
|
||||||
int nxdomains_per_second;
|
dns_rrl_rate_t nodata_per_second;
|
||||||
int all_per_second;
|
dns_rrl_rate_t nxdomains_per_second;
|
||||||
|
dns_rrl_rate_t errors_per_second;
|
||||||
|
dns_rrl_rate_t all_per_second;
|
||||||
|
dns_rrl_rate_t slip;
|
||||||
int window;
|
int window;
|
||||||
int slip;
|
|
||||||
double qps_scale;
|
double qps_scale;
|
||||||
int max_entries;
|
int max_entries;
|
||||||
|
|
||||||
@@ -215,11 +225,6 @@ struct dns_rrl {
|
|||||||
int qps_responses;
|
int qps_responses;
|
||||||
isc_stdtime_t qps_time;
|
isc_stdtime_t qps_time;
|
||||||
double qps;
|
double qps;
|
||||||
int scaled_responses_per_second;
|
|
||||||
int scaled_errors_per_second;
|
|
||||||
int scaled_nxdomains_per_second;
|
|
||||||
int scaled_all_per_second;
|
|
||||||
int scaled_slip;
|
|
||||||
|
|
||||||
unsigned int probes;
|
unsigned int probes;
|
||||||
unsigned int searches;
|
unsigned int searches;
|
||||||
|
214
lib/dns/rrl.c
214
lib/dns/rrl.c
@@ -167,11 +167,11 @@ set_age(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_stdtime_t now) {
|
|||||||
if (ts >= DNS_RRL_MAX_TS) {
|
if (ts >= DNS_RRL_MAX_TS) {
|
||||||
ts_gen = (ts_gen + 1) % DNS_RRL_TS_BASES;
|
ts_gen = (ts_gen + 1) % DNS_RRL_TS_BASES;
|
||||||
for (e_old = ISC_LIST_TAIL(rrl->lru), i = 0;
|
for (e_old = ISC_LIST_TAIL(rrl->lru), i = 0;
|
||||||
e_old != NULL && e_old->ts_gen == ts_gen;
|
e_old != NULL && (e_old->ts_gen == ts_gen ||
|
||||||
|
!ISC_LINK_LINKED(e_old, hlink));
|
||||||
e_old = ISC_LIST_PREV(e_old, lru), ++i)
|
e_old = ISC_LIST_PREV(e_old, lru), ++i)
|
||||||
{
|
{
|
||||||
if (e_old->ts_valid)
|
e_old->ts_valid = ISC_FALSE;
|
||||||
e_old->ts_valid = ISC_FALSE;
|
|
||||||
}
|
}
|
||||||
if (i != 0)
|
if (i != 0)
|
||||||
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
||||||
@@ -403,9 +403,16 @@ make_key(const dns_rrl_t *rrl, dns_rrl_key_t *key,
|
|||||||
memset(key, 0, sizeof(*key));
|
memset(key, 0, sizeof(*key));
|
||||||
|
|
||||||
key->s.rtype = rtype;
|
key->s.rtype = rtype;
|
||||||
if (rtype == DNS_RRL_RTYPE_QUERY || rtype == DNS_RRL_RTYPE_DELEGATION) {
|
if (rtype == DNS_RRL_RTYPE_QUERY) {
|
||||||
key->s.qclass = qclass & 0xff;
|
|
||||||
key->s.qtype = qtype;
|
key->s.qtype = qtype;
|
||||||
|
key->s.qclass = qclass & 0xff;
|
||||||
|
} else if (rtype == DNS_RRL_RTYPE_REFERRAL ||
|
||||||
|
rtype == DNS_RRL_RTYPE_NODATA) {
|
||||||
|
/*
|
||||||
|
* Because there is no qtype in the empty answer sections of
|
||||||
|
* referral and NODATA responses, count them as the same.
|
||||||
|
*/
|
||||||
|
key->s.qclass = qclass & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qname != NULL && qname->labels != 0) {
|
if (qname != NULL && qname->labels != 0) {
|
||||||
@@ -440,33 +447,40 @@ make_key(const dns_rrl_t *rrl, dns_rrl_key_t *key,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline dns_rrl_rate_t *
|
||||||
response_balance(const dns_rrl_t *rrl, const dns_rrl_entry_t *e, int age) {
|
get_rate(dns_rrl_t *rrl, dns_rrl_rtype_t rtype) {
|
||||||
int balance, rate = 0;
|
switch (rtype) {
|
||||||
|
case DNS_RRL_RTYPE_QUERY:
|
||||||
balance = e->responses;
|
return (&rrl->responses_per_second);
|
||||||
if (balance < 0)
|
case DNS_RRL_RTYPE_REFERRAL:
|
||||||
switch (e->key.s.rtype) {
|
return (&rrl->referrals_per_second);
|
||||||
case DNS_RRL_RTYPE_QUERY:
|
case DNS_RRL_RTYPE_NODATA:
|
||||||
case DNS_RRL_RTYPE_DELEGATION:
|
return (&rrl->nodata_per_second);
|
||||||
rate = rrl->scaled_responses_per_second;
|
case DNS_RRL_RTYPE_NXDOMAIN:
|
||||||
break;
|
return (&rrl->nxdomains_per_second);
|
||||||
case DNS_RRL_RTYPE_NXDOMAIN:
|
case DNS_RRL_RTYPE_ERROR:
|
||||||
rate = rrl->scaled_nxdomains_per_second;
|
return (&rrl->errors_per_second);
|
||||||
break;
|
case DNS_RRL_RTYPE_ALL:
|
||||||
case DNS_RRL_RTYPE_ERROR:
|
return (&rrl->all_per_second);
|
||||||
rate = rrl->scaled_errors_per_second;
|
default:
|
||||||
break;
|
INSIST(0);
|
||||||
case DNS_RRL_RTYPE_ALL:
|
|
||||||
rate = rrl->scaled_all_per_second;
|
|
||||||
break;
|
|
||||||
case DNS_RRL_RTYPE_TCP:
|
|
||||||
rate = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
INSIST(0);
|
|
||||||
}
|
}
|
||||||
balance += age * rate;
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
response_balance(dns_rrl_t *rrl, const dns_rrl_entry_t *e, int age) {
|
||||||
|
dns_rrl_rate_t *ratep;
|
||||||
|
int balance, rate;
|
||||||
|
|
||||||
|
if (e->key.s.rtype == DNS_RRL_RTYPE_TCP) {
|
||||||
|
rate = 1;
|
||||||
|
} else {
|
||||||
|
ratep = get_rate(rrl, e->key.s.rtype);
|
||||||
|
rate = ratep->scaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
balance = e->responses + age * rate;
|
||||||
if (balance > rate)
|
if (balance > rate)
|
||||||
balance = rate;
|
balance = rate;
|
||||||
return (balance);
|
return (balance);
|
||||||
@@ -551,7 +565,7 @@ get_entry(dns_rrl_t *rrl, const isc_sockaddr_t *client_addr,
|
|||||||
e = NULL;
|
e = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!e->logged && response_balance(rrl, e, age) >= 0)
|
if (!e->logged && response_balance(rrl, e, age) > 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (e == NULL) {
|
if (e == NULL) {
|
||||||
@@ -598,35 +612,16 @@ debit_rrl_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, double qps, double scale,
|
|||||||
const isc_sockaddr_t *client_addr, isc_stdtime_t now,
|
const isc_sockaddr_t *client_addr, isc_stdtime_t now,
|
||||||
char *log_buf, unsigned int log_buf_len)
|
char *log_buf, unsigned int log_buf_len)
|
||||||
{
|
{
|
||||||
int rate, new_rate, *ratep, slip, new_slip, age, log_secs, min;
|
int rate, new_rate, slip, new_slip, age, log_secs, min;
|
||||||
const char *rate_str = NULL;
|
dns_rrl_rate_t *ratep;
|
||||||
dns_rrl_entry_t const *credit_e;
|
dns_rrl_entry_t const *credit_e;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pick the rate counter.
|
* Pick the rate counter.
|
||||||
* Optionally adjust the rate by the estimated query/second rate.
|
* Optionally adjust the rate by the estimated query/second rate.
|
||||||
*/
|
*/
|
||||||
switch (e->key.s.rtype) {
|
ratep = get_rate(rrl, e->key.s.rtype);
|
||||||
case DNS_RRL_RTYPE_QUERY:
|
rate = ratep->r;
|
||||||
case DNS_RRL_RTYPE_DELEGATION:
|
|
||||||
rate = rrl->responses_per_second;
|
|
||||||
ratep = &rrl->scaled_responses_per_second;
|
|
||||||
break;
|
|
||||||
case DNS_RRL_RTYPE_NXDOMAIN:
|
|
||||||
rate = rrl->nxdomains_per_second;
|
|
||||||
ratep = &rrl->scaled_nxdomains_per_second;
|
|
||||||
break;
|
|
||||||
case DNS_RRL_RTYPE_ERROR:
|
|
||||||
rate = rrl->errors_per_second;
|
|
||||||
ratep = &rrl->scaled_errors_per_second;
|
|
||||||
break;
|
|
||||||
case DNS_RRL_RTYPE_ALL:
|
|
||||||
rate = rrl->all_per_second;
|
|
||||||
ratep = &rrl->scaled_all_per_second;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
INSIST(0);
|
|
||||||
}
|
|
||||||
if (rate == 0)
|
if (rate == 0)
|
||||||
return (DNS_RRL_RESULT_OK);
|
return (DNS_RRL_RESULT_OK);
|
||||||
|
|
||||||
@@ -648,36 +643,16 @@ debit_rrl_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, double qps, double scale,
|
|||||||
new_rate = (int) (rate * scale);
|
new_rate = (int) (rate * scale);
|
||||||
if (new_rate < 1)
|
if (new_rate < 1)
|
||||||
new_rate = 1;
|
new_rate = 1;
|
||||||
if (*ratep != new_rate) {
|
if (ratep->scaled != new_rate) {
|
||||||
if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG1)) {
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
||||||
switch (e->key.s.rtype) {
|
DNS_LOGMODULE_REQUEST,
|
||||||
case DNS_RRL_RTYPE_QUERY:
|
DNS_RRL_LOG_DEBUG1,
|
||||||
case DNS_RRL_RTYPE_DELEGATION:
|
"%d qps scaled %s by %.2f"
|
||||||
rate_str = "responses-per-second";
|
" from %d to %d",
|
||||||
break;
|
(int)qps, ratep->str, scale,
|
||||||
case DNS_RRL_RTYPE_NXDOMAIN:
|
rate, new_rate);
|
||||||
rate_str = "nxdomains-per-second";
|
|
||||||
break;
|
|
||||||
case DNS_RRL_RTYPE_ERROR:
|
|
||||||
rate_str = "errors-per-second";
|
|
||||||
break;
|
|
||||||
case DNS_RRL_RTYPE_ALL:
|
|
||||||
rate_str = "all-per-second";
|
|
||||||
break;
|
|
||||||
case DNS_RRL_RTYPE_FREE:
|
|
||||||
case DNS_RRL_RTYPE_TCP:
|
|
||||||
INSIST(0);
|
|
||||||
}
|
|
||||||
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
||||||
DNS_LOGMODULE_REQUEST,
|
|
||||||
DNS_RRL_LOG_DEBUG1,
|
|
||||||
"%d qps scaled %s by %.2f"
|
|
||||||
" from %d to %d",
|
|
||||||
(int)qps, rate_str, scale,
|
|
||||||
rate, new_rate);
|
|
||||||
}
|
|
||||||
rate = new_rate;
|
rate = new_rate;
|
||||||
*ratep = rate;
|
ratep->scaled = rate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -736,22 +711,21 @@ debit_rrl_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, double qps, double scale,
|
|||||||
/*
|
/*
|
||||||
* Drop this response unless it should slip or leak.
|
* Drop this response unless it should slip or leak.
|
||||||
*/
|
*/
|
||||||
slip = rrl->slip;
|
slip = rrl->slip.r;
|
||||||
if (slip > 2 && scale < 1.0) {
|
if (slip > 2 && scale < 1.0) {
|
||||||
new_slip = (int) (slip * scale);
|
new_slip = (int) (slip * scale);
|
||||||
if (new_slip < 2)
|
if (new_slip < 2)
|
||||||
new_slip = 2;
|
new_slip = 2;
|
||||||
if (rrl->scaled_slip != new_slip) {
|
if (rrl->slip.scaled != new_slip) {
|
||||||
if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG1))
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
||||||
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
DNS_LOGMODULE_REQUEST,
|
||||||
DNS_LOGMODULE_REQUEST,
|
DNS_RRL_LOG_DEBUG1,
|
||||||
DNS_RRL_LOG_DEBUG1,
|
"%d qps scaled slip"
|
||||||
"%d qps scaled slip"
|
" by %.2f from %d to %d",
|
||||||
" by %.2f from %d to %d",
|
(int)qps, scale,
|
||||||
(int)qps, scale,
|
slip, new_slip);
|
||||||
slip, new_slip);
|
|
||||||
slip = new_slip;
|
slip = new_slip;
|
||||||
rrl->scaled_slip = slip;
|
rrl->slip.scaled = slip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slip != 0 && e->key.s.rtype != DNS_RRL_RTYPE_ALL) {
|
if (slip != 0 && e->key.s.rtype != DNS_RRL_RTYPE_ALL) {
|
||||||
@@ -853,34 +827,36 @@ make_log_buf(dns_rrl_t *rrl, dns_rrl_entry_t *e,
|
|||||||
|
|
||||||
switch (e->key.s.rtype) {
|
switch (e->key.s.rtype) {
|
||||||
case DNS_RRL_RTYPE_QUERY:
|
case DNS_RRL_RTYPE_QUERY:
|
||||||
ADD_LOG_CSTR(&lb, "response");
|
|
||||||
break;
|
break;
|
||||||
case DNS_RRL_RTYPE_DELEGATION:
|
case DNS_RRL_RTYPE_REFERRAL:
|
||||||
ADD_LOG_CSTR(&lb, "referral");
|
ADD_LOG_CSTR(&lb, "referral ");
|
||||||
|
break;
|
||||||
|
case DNS_RRL_RTYPE_NODATA:
|
||||||
|
ADD_LOG_CSTR(&lb, "NODATA ");
|
||||||
break;
|
break;
|
||||||
case DNS_RRL_RTYPE_NXDOMAIN:
|
case DNS_RRL_RTYPE_NXDOMAIN:
|
||||||
ADD_LOG_CSTR(&lb, "NXDOMAIN response");
|
ADD_LOG_CSTR(&lb, "NXDOMAIN ");
|
||||||
break;
|
break;
|
||||||
case DNS_RRL_RTYPE_ERROR:
|
case DNS_RRL_RTYPE_ERROR:
|
||||||
if (resp_result == ISC_R_SUCCESS) {
|
if (resp_result == ISC_R_SUCCESS) {
|
||||||
ADD_LOG_CSTR(&lb, "error response");
|
ADD_LOG_CSTR(&lb, "error ");
|
||||||
} else {
|
} else {
|
||||||
rstr = isc_result_totext(resp_result);
|
rstr = isc_result_totext(resp_result);
|
||||||
add_log_str(&lb, rstr, strlen(rstr));
|
add_log_str(&lb, rstr, strlen(rstr));
|
||||||
ADD_LOG_CSTR(&lb, " response");
|
ADD_LOG_CSTR(&lb, " error ");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DNS_RRL_RTYPE_ALL:
|
case DNS_RRL_RTYPE_ALL:
|
||||||
ADD_LOG_CSTR(&lb, "all response");
|
ADD_LOG_CSTR(&lb, "all ");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
INSIST(0);
|
INSIST(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plural)
|
if (plural)
|
||||||
ADD_LOG_CSTR(&lb, "s to ");
|
ADD_LOG_CSTR(&lb, "responses to ");
|
||||||
else
|
else
|
||||||
ADD_LOG_CSTR(&lb, " to ");
|
ADD_LOG_CSTR(&lb, "response to ");
|
||||||
|
|
||||||
memset(&cidr, 0, sizeof(cidr));
|
memset(&cidr, 0, sizeof(cidr));
|
||||||
if (e->key.s.ipv6) {
|
if (e->key.s.ipv6) {
|
||||||
@@ -899,7 +875,8 @@ make_log_buf(dns_rrl_t *rrl, dns_rrl_entry_t *e,
|
|||||||
add_log_str(&lb, strbuf, strlen(strbuf));
|
add_log_str(&lb, strbuf, strlen(strbuf));
|
||||||
|
|
||||||
if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY ||
|
if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY ||
|
||||||
e->key.s.rtype == DNS_RRL_RTYPE_DELEGATION ||
|
e->key.s.rtype == DNS_RRL_RTYPE_REFERRAL ||
|
||||||
|
e->key.s.rtype == DNS_RRL_RTYPE_NODATA ||
|
||||||
e->key.s.rtype == DNS_RRL_RTYPE_NXDOMAIN) {
|
e->key.s.rtype == DNS_RRL_RTYPE_NXDOMAIN) {
|
||||||
qbuf = get_qname(rrl, e);
|
qbuf = get_qname(rrl, e);
|
||||||
if (save_qname && qbuf == NULL &&
|
if (save_qname && qbuf == NULL &&
|
||||||
@@ -947,8 +924,10 @@ make_log_buf(dns_rrl_t *rrl, dns_rrl_entry_t *e,
|
|||||||
if (e->key.s.rtype != DNS_RRL_RTYPE_NXDOMAIN) {
|
if (e->key.s.rtype != DNS_RRL_RTYPE_NXDOMAIN) {
|
||||||
ADD_LOG_CSTR(&lb, " ");
|
ADD_LOG_CSTR(&lb, " ");
|
||||||
(void)dns_rdataclass_totext(e->key.s.qclass, &lb);
|
(void)dns_rdataclass_totext(e->key.s.qclass, &lb);
|
||||||
ADD_LOG_CSTR(&lb, " ");
|
if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY) {
|
||||||
(void)dns_rdatatype_totext(e->key.s.qtype, &lb);
|
ADD_LOG_CSTR(&lb, " ");
|
||||||
|
(void)dns_rdatatype_totext(e->key.s.qtype, &lb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
snprintf(strbuf, sizeof(strbuf), " (%08x)",
|
snprintf(strbuf, sizeof(strbuf), " (%08x)",
|
||||||
e->key.s.qname_hash);
|
e->key.s.qname_hash);
|
||||||
@@ -1117,14 +1096,23 @@ dns_rrl(dns_view_t *view,
|
|||||||
* Find the right kind of entry, creating it if necessary.
|
* Find the right kind of entry, creating it if necessary.
|
||||||
* If that is impossible, then nothing more can be done
|
* If that is impossible, then nothing more can be done
|
||||||
*/
|
*/
|
||||||
if (resp_result == ISC_R_SUCCESS)
|
switch (resp_result) {
|
||||||
|
case ISC_R_SUCCESS:
|
||||||
rtype = DNS_RRL_RTYPE_QUERY;
|
rtype = DNS_RRL_RTYPE_QUERY;
|
||||||
else if (resp_result == DNS_R_DELEGATION)
|
break;
|
||||||
rtype = DNS_RRL_RTYPE_DELEGATION;
|
case DNS_R_DELEGATION:
|
||||||
else if (resp_result == DNS_R_NXDOMAIN)
|
rtype = DNS_RRL_RTYPE_REFERRAL;
|
||||||
|
break;
|
||||||
|
case DNS_R_NXRRSET:
|
||||||
|
rtype = DNS_RRL_RTYPE_NODATA;
|
||||||
|
break;
|
||||||
|
case DNS_R_NXDOMAIN:
|
||||||
rtype = DNS_RRL_RTYPE_NXDOMAIN;
|
rtype = DNS_RRL_RTYPE_NXDOMAIN;
|
||||||
else
|
break;
|
||||||
|
default:
|
||||||
rtype = DNS_RRL_RTYPE_ERROR;
|
rtype = DNS_RRL_RTYPE_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
e = get_entry(rrl, client_addr, qclass, qtype, qname, rtype,
|
e = get_entry(rrl, client_addr, qclass, qtype, qname, rtype,
|
||||||
now, ISC_TRUE, log_buf, log_buf_len);
|
now, ISC_TRUE, log_buf, log_buf_len);
|
||||||
if (e == NULL) {
|
if (e == NULL) {
|
||||||
@@ -1148,7 +1136,7 @@ dns_rrl(dns_view_t *view,
|
|||||||
rrl_result = debit_rrl_entry(rrl, e, qps, scale, client_addr, now,
|
rrl_result = debit_rrl_entry(rrl, e, qps, scale, client_addr, now,
|
||||||
log_buf, log_buf_len);
|
log_buf, log_buf_len);
|
||||||
|
|
||||||
if (rrl->all_per_second != 0) {
|
if (rrl->all_per_second.r != 0) {
|
||||||
/*
|
/*
|
||||||
* We must debit the all-per-second token bucket if we have
|
* We must debit the all-per-second token bucket if we have
|
||||||
* an all-per-second limit for the IP address.
|
* an all-per-second limit for the IP address.
|
||||||
|
@@ -1324,16 +1324,17 @@ static cfg_type_t cfg_type_rpz = {
|
|||||||
*/
|
*/
|
||||||
static cfg_clausedef_t rrl_clauses[] = {
|
static cfg_clausedef_t rrl_clauses[] = {
|
||||||
{ "responses-per-second", &cfg_type_uint32, 0 },
|
{ "responses-per-second", &cfg_type_uint32, 0 },
|
||||||
{ "errors-per-second", &cfg_type_uint32, 0 },
|
{ "referrals-per-second", &cfg_type_uint32, 0 },
|
||||||
|
{ "nodata-per-second", &cfg_type_uint32, 0 },
|
||||||
{ "nxdomains-per-second", &cfg_type_uint32, 0 },
|
{ "nxdomains-per-second", &cfg_type_uint32, 0 },
|
||||||
{ "responses-per-second", &cfg_type_uint32, 0 },
|
{ "errors-per-second", &cfg_type_uint32, 0 },
|
||||||
{ "all-per-second", &cfg_type_uint32, 0 },
|
{ "all-per-second", &cfg_type_uint32, 0 },
|
||||||
{ "slip", &cfg_type_uint32, 0 },
|
{ "slip", &cfg_type_uint32, 0 },
|
||||||
{ "window", &cfg_type_uint32, 0 },
|
{ "window", &cfg_type_uint32, 0 },
|
||||||
{ "log-only", &cfg_type_boolean, 0 },
|
{ "log-only", &cfg_type_boolean, 0 },
|
||||||
{ "qps-scale", &cfg_type_uint32, 0 },
|
{ "qps-scale", &cfg_type_uint32, 0 },
|
||||||
{ "IPv4-prefix-length", &cfg_type_uint32, 0 },
|
{ "ipv4-prefix-length", &cfg_type_uint32, 0 },
|
||||||
{ "IPv6-prefix-length", &cfg_type_uint32, 0 },
|
{ "ipv6-prefix-length", &cfg_type_uint32, 0 },
|
||||||
{ "exempt-clients", &cfg_type_bracketed_aml, 0 },
|
{ "exempt-clients", &cfg_type_bracketed_aml, 0 },
|
||||||
{ "max-table-size", &cfg_type_uint32, 0 },
|
{ "max-table-size", &cfg_type_uint32, 0 },
|
||||||
{ "min-table-size", &cfg_type_uint32, 0 },
|
{ "min-table-size", &cfg_type_uint32, 0 },
|
||||||
|
Reference in New Issue
Block a user