diff --git a/Knot.files b/Knot.files index 6ad59293bd7f08bc519201636ebfb1207921686f..2fc1bf67fa14cad2765ebafaf1692979ebeb0c40 100644 --- a/Knot.files +++ b/Knot.files @@ -218,6 +218,8 @@ src/knot/conf/confdb.c src/knot/conf/confdb.h src/knot/conf/confio.c src/knot/conf/confio.h +src/knot/conf/migration.c +src/knot/conf/migration.h src/knot/conf/scheme.c src/knot/conf/scheme.h src/knot/conf/tools.c @@ -269,6 +271,10 @@ src/knot/modules/online_sign/online_sign.h src/knot/modules/rosedb/rosedb.c src/knot/modules/rosedb/rosedb.h src/knot/modules/rosedb/rosedb_tool.c +src/knot/modules/rrl/functions.c +src/knot/modules/rrl/functions.h +src/knot/modules/rrl/rrl.c +src/knot/modules/rrl/rrl.h src/knot/modules/stats/stats.c src/knot/modules/stats/stats.h src/knot/modules/synth_record/synth_record.c @@ -308,8 +314,6 @@ src/knot/server/dthreads.c src/knot/server/dthreads.h src/knot/server/journal.c src/knot/server/journal.h -src/knot/server/rrl.c -src/knot/server/rrl.h src/knot/server/serialization.c src/knot/server/serialization.h src/knot/server/server.c @@ -579,12 +583,12 @@ tests/libknot/test_yparser.c tests/libknot/test_ypscheme.c tests/libknot/test_yptrafo.c tests/modules/online_sign.c +tests/modules/rrl.c tests/node.c tests/process_answer.c tests/process_query.c tests/query_module.c tests/requestor.c -tests/rrl.c tests/server.c tests/test_conf.h tests/utils/test_cert.c diff --git a/doc/configuration.rst b/doc/configuration.rst index 3972d3893fad9382965e31fe0b57caf11f30048e..448523570402bafa8bd18b822cd03b57007189ff 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -247,31 +247,6 @@ processed:: file: example.com.zone acl: update_acl -Response rate limiting -====================== - -Response rate limiting (RRL) is a method to combat DNS reflection amplification -attacks. These attacks rely on the fact that source address of a UDP query -can be forged, and without a worldwide deployment of `BCP38 -<https://tools.ietf.org/html/bcp38>`_, such a forgery cannot be prevented. -An attacker can use a DNS server (or multiple servers) as an amplification -source and can flood a victim with a large number of unsolicited DNS responses. - -The RRL lowers the amplification factor of these attacks by sending some of -the responses as truncated or by dropping them altogether. - -You can enable RRL by setting the :ref:`server_rate-limit` option in the -:ref:`server section<Server section>`. The option controls how many responses -per second are permitted for each flow. Responses exceeding this rate are -limited. The option :ref:`server_rate-limit-slip` then configures how many -limited responses are sent as truncated (slip) instead of being dropped. - -:: - - server: - rate-limit: 200 # Allow 200 resp/s for each flow - rate-limit-slip: 2 # Every other response slips - .. _dnssec: Automatic DNSSEC signing diff --git a/doc/man/knot.conf.5in b/doc/man/knot.conf.5in index f03d0c5d71f8d7cba0debfe782f8d9e7fbf5c957..4bf887e091f2efb2e518e2e46b23ff738d34ef39 100644 --- a/doc/man/knot.conf.5in +++ b/doc/man/knot.conf.5in @@ -149,10 +149,6 @@ server: max\-udp\-payload: SIZE max\-ipv4\-udp\-payload: SIZE max\-ipv6\-udp\-payload: SIZE - rate\-limit: INT - rate\-limit\-slip: INT - rate\-limit\-table\-size: INT - rate\-limit\-whitelist: ADDR[/INT] | ADDR\-ADDR ... listen: ADDR[@INT] ... .ft P .fi @@ -243,69 +239,6 @@ A maximum number of TCP clients connected in parallel, set this below the file descriptor limit to avoid resource exhaustion. .sp \fIDefault:\fP 100 -.SS rate\-limit -.sp -Rate limiting is based on the token bucket scheme. A rate basically -represents a number of tokens available each second. Each response is -processed and classified (based on several discriminators, e.g. -source netblock, query type, zone name, rcode, etc.). Classified responses are -then hashed and assigned to a bucket containing number of available -tokens, timestamp and metadata. When available tokens are exhausted, -response is dropped or sent as truncated (see \fI\%rate\-limit\-slip\fP). -Number of available tokens is recalculated each second. -.sp -\fIDefault:\fP 0 (disabled) -.SS rate\-limit\-table\-size -.sp -Size of the hash table in a number of buckets. The larger the hash table, the lesser -the probability of a hash collision, but at the expense of additional memory costs. -Each bucket is estimated roughly to 32 bytes. The size should be selected as -a reasonably large prime due to better hash function distribution properties. -Hash table is internally chained and works well up to a fill rate of 90 %, general -rule of thumb is to select a prime near 1.2 * maximum_qps. -.sp -\fIDefault:\fP 393241 -.SS rate\-limit\-slip -.sp -As attacks using DNS/UDP are usually based on a forged source address, -an attacker could deny services to the victim\(aqs netblock if all -responses would be completely blocked. The idea behind SLIP mechanism -is to send each N\s-2\uth\d\s0 response as truncated, thus allowing client to -reconnect via TCP for at least some degree of service. It is worth -noting, that some responses can\(aqt be truncated (e.g. SERVFAIL). -.INDENT 0.0 -.IP \(bu 2 -Setting the value to \fB0\fP will cause that all rate\-limited responses will -be dropped. The outbound bandwidth and packet rate will be strictly capped -by the \fI\%rate\-limit\fP option. All legitimate requestors affected -by the limit will face denial of service and will observe excessive timeouts. -Therefore this setting is not recommended. -.IP \(bu 2 -Setting the value to \fB1\fP will cause that all rate\-limited responses will -be sent as truncated. The amplification factor of the attack will be reduced, -but the outbound data bandwidth won\(aqt be lower than the incoming bandwidth. -Also the outbound packet rate will be the same as without RRL. -.IP \(bu 2 -Setting the value to \fB2\fP will cause that half of the rate\-limited responses -will be dropped, the other half will be sent as truncated. With this -configuration, both outbound bandwidth and packet rate will be lower than the -inbound. On the other hand, the dropped responses enlarge the time window -for possible cache poisoning attack on the resolver. -.IP \(bu 2 -Setting the value to anything \fBlarger than 2\fP will keep on decreasing -the outgoing rate\-limited bandwidth, packet rate, and chances to notify -legitimate requestors to reconnect using TCP. These attributes are inversely -proportional to the configured value. Setting the value high is not advisable. -.UNINDENT -.sp -\fIDefault:\fP 1 -.SS rate\-limit\-whitelist -.sp -A list of IP addresses, network subnets, or network ranges to exempt from -rate limiting. Empty list means that no incoming connection will be -white\-listed. -.sp -\fIDefault:\fP not set .SS max\-udp\-payload .sp Maximum EDNS0 UDP payload size default for both IPv4 and IPv6. @@ -1089,6 +1022,90 @@ Minimum severity level for messages related to zones that are logged. Minimum severity level for all message types that are logged. .sp \fIDefault:\fP not set +.SH MODULE RRL +.sp +A response rate limiting module. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +mod\-rrl: + \- id: STR + rate\-limit: INT + slip: INT + table\-size: INT + whitelist: ADDR[/INT] | ADDR\-ADDR ... +.ft P +.fi +.UNINDENT +.UNINDENT +.SS id +.sp +A module identifier. +.SS rate\-limit +.sp +Rate limiting is based on the token bucket scheme. A rate basically +represents a number of tokens available each second. Each response is +processed and classified (based on several discriminators, e.g. +source netblock, query type, zone name, rcode, etc.). Classified responses are +then hashed and assigned to a bucket containing number of available +tokens, timestamp and metadata. When available tokens are exhausted, +response is dropped or sent as truncated (see \fI\%slip\fP). +Number of available tokens is recalculated each second. +.sp +\fIRequired\fP +.SS table\-size +.sp +Size of the hash table in a number of buckets. The larger the hash table, the lesser +the probability of a hash collision, but at the expense of additional memory costs. +Each bucket is estimated roughly to 32 bytes. The size should be selected as +a reasonably large prime due to better hash function distribution properties. +Hash table is internally chained and works well up to a fill rate of 90 %, general +rule of thumb is to select a prime near 1.2 * maximum_qps. +.sp +\fIDefault:\fP 393241 +.SS slip +.sp +As attacks using DNS/UDP are usually based on a forged source address, +an attacker could deny services to the victim\(aqs netblock if all +responses would be completely blocked. The idea behind SLIP mechanism +is to send each N\s-2\uth\d\s0 response as truncated, thus allowing client to +reconnect via TCP for at least some degree of service. It is worth +noting, that some responses can\(aqt be truncated (e.g. SERVFAIL). +.INDENT 0.0 +.IP \(bu 2 +Setting the value to \fB0\fP will cause that all rate\-limited responses will +be dropped. The outbound bandwidth and packet rate will be strictly capped +by the \fI\%rate\-limit\fP option. All legitimate requestors affected +by the limit will face denial of service and will observe excessive timeouts. +Therefore this setting is not recommended. +.IP \(bu 2 +Setting the value to \fB1\fP will cause that all rate\-limited responses will +be sent as truncated. The amplification factor of the attack will be reduced, +but the outbound data bandwidth won\(aqt be lower than the incoming bandwidth. +Also the outbound packet rate will be the same as without RRL. +.IP \(bu 2 +Setting the value to \fB2\fP will cause that half of the rate\-limited responses +will be dropped, the other half will be sent as truncated. With this +configuration, both outbound bandwidth and packet rate will be lower than the +inbound. On the other hand, the dropped responses enlarge the time window +for possible cache poisoning attack on the resolver. +.IP \(bu 2 +Setting the value to anything \fBlarger than 2\fP will keep on decreasing +the outgoing rate\-limited bandwidth, packet rate, and chances to notify +legitimate requestors to reconnect using TCP. These attributes are inversely +proportional to the configured value. Setting the value high is not advisable. +.UNINDENT +.sp +\fIDefault:\fP 1 +.SS whitelist +.sp +A list of IP addresses, network subnets, or network ranges to exempt from +rate limiting. Empty list means that no incoming connection will be +white\-listed. +.sp +\fIDefault:\fP not set .SH MODULE DNSTAP .sp The module dnstap allows query and response logging. diff --git a/doc/modules.rst b/doc/modules.rst index 1fca0c8481cb2d6b1cca6677435d77d3ec7bf7a3..5cc3cf6bb71e38dc782f45df0467588a760c3c1c 100644 --- a/doc/modules.rst +++ b/doc/modules.rst @@ -31,7 +31,36 @@ an identifier must be created and then referenced in the form of .. NOTE:: Query modules are processed in the order they are specified in the - zone/template configuration. + zone/template configuration. In most cases, the recommended order is:: + + mod-synth-record, mod-online-sign, mod-rrl, mod-dnstap, mod-stats + +``rrl`` — Response rate limiting +-------------------------------- + +Response rate limiting (RRL) is a method to combat DNS reflection amplification +attacks. These attacks rely on the fact that source address of a UDP query +can be forged, and without a worldwide deployment of `BCP38 +<https://tools.ietf.org/html/bcp38>`_, such a forgery cannot be prevented. +An attacker can use a DNS server (or multiple servers) as an amplification +source and can flood a victim with a large number of unsolicited DNS responses. +The RRL lowers the amplification factor of these attacks by sending some of +the responses as truncated or by dropping them altogether. + +The module introduces two counters. The number of slipped and dropped responses. + +You can enable RRL by setting the :ref:`mod-rrl<mod-rrl>` module globally or per zone. + +:: + + mod-rrl: + - id: default + rate-limit: 200 # Allow 200 resp/s for each flow + slip: 2 # Every other response slips + + template: + - id: default + global-module: mod-rrl/default # Enable RRL globally ``dnstap`` – dnstap-enabled query logging ----------------------------------------- @@ -503,7 +532,7 @@ AAAA-only glue records. ------------------------ The module sends empty truncated response to any UDP query. This is similar -to a slipped answer in :ref:`response rate limiting<server_rate-limit>`. +to a slipped answer in :ref:`response rate limiting<mod-rrl_rate-limit>`. TCP queries are not affected. To enable this module globally, you need to add something like the following diff --git a/doc/reference.rst b/doc/reference.rst index 783c421b6c3385dfd7d8c1b279c39a142c34f622..2087261383152863c9eadb663db966d8afd0e407 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -102,10 +102,6 @@ General options related to the server. max-udp-payload: SIZE max-ipv4-udp-payload: SIZE max-ipv6-udp-payload: SIZE - rate-limit: INT - rate-limit-slip: INT - rate-limit-table-size: INT - rate-limit-whitelist: ADDR[/INT] | ADDR-ADDR ... listen: ADDR[@INT] ... .. _server_identity: @@ -249,83 +245,6 @@ descriptor limit to avoid resource exhaustion. *Default:* 100 -.. _server_rate-limit: - -rate-limit ----------- - -Rate limiting is based on the token bucket scheme. A rate basically -represents a number of tokens available each second. Each response is -processed and classified (based on several discriminators, e.g. -source netblock, query type, zone name, rcode, etc.). Classified responses are -then hashed and assigned to a bucket containing number of available -tokens, timestamp and metadata. When available tokens are exhausted, -response is dropped or sent as truncated (see :ref:`server_rate-limit-slip`). -Number of available tokens is recalculated each second. - -*Default:* 0 (disabled) - -.. _server_rate-limit-table-size: - -rate-limit-table-size ---------------------- - -Size of the hash table in a number of buckets. The larger the hash table, the lesser -the probability of a hash collision, but at the expense of additional memory costs. -Each bucket is estimated roughly to 32 bytes. The size should be selected as -a reasonably large prime due to better hash function distribution properties. -Hash table is internally chained and works well up to a fill rate of 90 %, general -rule of thumb is to select a prime near 1.2 * maximum_qps. - -*Default:* 393241 - -.. _server_rate-limit-slip: - -rate-limit-slip ---------------- - -As attacks using DNS/UDP are usually based on a forged source address, -an attacker could deny services to the victim's netblock if all -responses would be completely blocked. The idea behind SLIP mechanism -is to send each N\ :sup:`th` response as truncated, thus allowing client to -reconnect via TCP for at least some degree of service. It is worth -noting, that some responses can't be truncated (e.g. SERVFAIL). - -- Setting the value to **0** will cause that all rate-limited responses will - be dropped. The outbound bandwidth and packet rate will be strictly capped - by the :ref:`server_rate-limit` option. All legitimate requestors affected - by the limit will face denial of service and will observe excessive timeouts. - Therefore this setting is not recommended. - -- Setting the value to **1** will cause that all rate-limited responses will - be sent as truncated. The amplification factor of the attack will be reduced, - but the outbound data bandwidth won't be lower than the incoming bandwidth. - Also the outbound packet rate will be the same as without RRL. - -- Setting the value to **2** will cause that half of the rate-limited responses - will be dropped, the other half will be sent as truncated. With this - configuration, both outbound bandwidth and packet rate will be lower than the - inbound. On the other hand, the dropped responses enlarge the time window - for possible cache poisoning attack on the resolver. - -- Setting the value to anything **larger than 2** will keep on decreasing - the outgoing rate-limited bandwidth, packet rate, and chances to notify - legitimate requestors to reconnect using TCP. These attributes are inversely - proportional to the configured value. Setting the value high is not advisable. - -*Default:* 1 - -.. _server_rate-limit-whitelist: - -rate-limit-whitelist --------------------- - -A list of IP addresses, network subnets, or network ranges to exempt from -rate limiting. Empty list means that no incoming connection will be -white-listed. - -*Default:* not set - .. _server_max-udp-payload: max-udp-payload @@ -1274,6 +1193,106 @@ Minimum severity level for all message types that are logged. *Default:* not set +.. _mod-rrl: + +Module rrl +========== + +A response rate limiting module. + +:: + + mod-rrl: + - id: STR + rate-limit: INT + slip: INT + table-size: INT + whitelist: ADDR[/INT] | ADDR-ADDR ... + +.. _mod-rrl_id: + +id +-- + +A module identifier. + +.. _mod-rrl_rate-limit: + +rate-limit +---------- + +Rate limiting is based on the token bucket scheme. A rate basically +represents a number of tokens available each second. Each response is +processed and classified (based on several discriminators, e.g. +source netblock, query type, zone name, rcode, etc.). Classified responses are +then hashed and assigned to a bucket containing number of available +tokens, timestamp and metadata. When available tokens are exhausted, +response is dropped or sent as truncated (see :ref:`mod-rrl_slip`). +Number of available tokens is recalculated each second. + +*Required* + +.. _mod-rrl_table-size: + +table-size +---------- + +Size of the hash table in a number of buckets. The larger the hash table, the lesser +the probability of a hash collision, but at the expense of additional memory costs. +Each bucket is estimated roughly to 32 bytes. The size should be selected as +a reasonably large prime due to better hash function distribution properties. +Hash table is internally chained and works well up to a fill rate of 90 %, general +rule of thumb is to select a prime near 1.2 * maximum_qps. + +*Default:* 393241 + +.. _mod-rrl_slip: + +slip +---- + +As attacks using DNS/UDP are usually based on a forged source address, +an attacker could deny services to the victim's netblock if all +responses would be completely blocked. The idea behind SLIP mechanism +is to send each N\ :sup:`th` response as truncated, thus allowing client to +reconnect via TCP for at least some degree of service. It is worth +noting, that some responses can't be truncated (e.g. SERVFAIL). + +- Setting the value to **0** will cause that all rate-limited responses will + be dropped. The outbound bandwidth and packet rate will be strictly capped + by the :ref:`mod-rrl_rate-limit` option. All legitimate requestors affected + by the limit will face denial of service and will observe excessive timeouts. + Therefore this setting is not recommended. + +- Setting the value to **1** will cause that all rate-limited responses will + be sent as truncated. The amplification factor of the attack will be reduced, + but the outbound data bandwidth won't be lower than the incoming bandwidth. + Also the outbound packet rate will be the same as without RRL. + +- Setting the value to **2** will cause that half of the rate-limited responses + will be dropped, the other half will be sent as truncated. With this + configuration, both outbound bandwidth and packet rate will be lower than the + inbound. On the other hand, the dropped responses enlarge the time window + for possible cache poisoning attack on the resolver. + +- Setting the value to anything **larger than 2** will keep on decreasing + the outgoing rate-limited bandwidth, packet rate, and chances to notify + legitimate requestors to reconnect using TCP. These attributes are inversely + proportional to the configured value. Setting the value high is not advisable. + +*Default:* 1 + +.. _mod-rrl_whitelist: + +whitelist +--------- + +A list of IP addresses, network subnets, or network ranges to exempt from +rate limiting. Empty list means that no incoming connection will be +white-listed. + +*Default:* not set + .. _Module dnstap: Module dnstap diff --git a/src/Makefile.am b/src/Makefile.am index 406b0089a490c35dbc49cb64b9db5140ecac85bd..183e0d29d3639d562e80365e555913d2b193b11f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -234,6 +234,8 @@ libknotd_la_SOURCES = \ knot/conf/confdb.h \ knot/conf/confio.c \ knot/conf/confio.h \ + knot/conf/migration.c \ + knot/conf/migration.h \ knot/conf/scheme.c \ knot/conf/scheme.h \ knot/conf/tools.c \ @@ -280,6 +282,10 @@ libknotd_la_SOURCES = \ knot/modules/online_sign/online_sign.h \ knot/modules/online_sign/nsec_next.c \ knot/modules/online_sign/nsec_next.h \ + knot/modules/rrl/functions.c \ + knot/modules/rrl/functions.h \ + knot/modules/rrl/rrl.c \ + knot/modules/rrl/rrl.h \ knot/modules/stats/stats.c \ knot/modules/stats/stats.h \ knot/modules/synth_record/synth_record.c\ @@ -331,8 +337,6 @@ libknotd_la_SOURCES = \ knot/server/dthreads.h \ knot/server/journal.c \ knot/server/journal.h \ - knot/server/rrl.c \ - knot/server/rrl.h \ knot/server/serialization.c \ knot/server/serialization.h \ knot/server/server.c \ diff --git a/src/knot/conf/base.c b/src/knot/conf/base.c index 8918022dc95fcad34ae61679158771adcb70bf5b..15e6f1523a66e07df0300656791b17a0baad1621 100644 --- a/src/knot/conf/base.c +++ b/src/knot/conf/base.c @@ -133,15 +133,10 @@ static void init_cache( val = conf_get(conf, C_SRV, C_MAX_TCP_CLIENTS); conf->cache.srv_max_tcp_clients = conf_int(&val); - val = conf_get(conf, C_SRV, C_RATE_LIMIT_SLIP); - conf->cache.srv_rate_limit_slip = conf_int(&val); - val = conf_get(conf, C_CTL, C_TIMEOUT); conf->cache.ctl_timeout = conf_int(&val) * 1000; conf->cache.srv_nsid = conf_get(conf, C_SRV, C_NSID); - - conf->cache.srv_rate_limit_whitelist = conf_get(conf, C_SRV, C_RATE_LIMIT_WHITELIST); } int conf_new( diff --git a/src/knot/conf/base.h b/src/knot/conf/base.h index 4b803c2a9fdc7ee2726ce7f2851343eecb8a89fd..8105b2076935e1a3e4d9c462592ab9db1a3b97fb 100644 --- a/src/knot/conf/base.h +++ b/src/knot/conf/base.h @@ -106,10 +106,8 @@ typedef struct { int32_t srv_tcp_idle_timeout; int32_t srv_tcp_reply_timeout; int32_t srv_max_tcp_clients; - int32_t srv_rate_limit_slip; int32_t ctl_timeout; conf_val_t srv_nsid; - conf_val_t srv_rate_limit_whitelist; } cache; /*! List of active query modules. */ diff --git a/src/knot/conf/migration.c b/src/knot/conf/migration.c new file mode 100644 index 0000000000000000000000000000000000000000..08099ce3e4bf2b9f66977ef4b54c3e4818d0424f --- /dev/null +++ b/src/knot/conf/migration.c @@ -0,0 +1,133 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "knot/common/log.h" +#include "knot/conf/migration.h" +#include "knot/conf/confdb.h" + +static void try_unset(conf_t *conf, knot_db_txn_t *txn, yp_name_t *key0, yp_name_t *key1) +{ + int ret = conf_db_unset(conf, txn, key0, key1, NULL, 0, NULL, 0, true); + if (ret != KNOT_EOK && ret != KNOT_ENOENT) { + log_warning("conf, migration, failed to unset '%s%s%s' (%s)", + key0 + 1, + (key1 != NULL) ? "/" : "", + (key1 != NULL) ? key1 + 1 : "", + knot_strerror(ret)); + } +} + +#define check_set(conf, txn, key0, key1, id, id_len, data, data_len) \ + ret = conf_db_set(conf, txn, key0, key1, id, id_len, data, data_len); \ + if (ret != KNOT_EOK && ret != KNOT_CONF_EREDEFINE) { \ + log_error("conf, migration, failed to set '%s%s%s' (%s)", \ + key0 + 1, \ + (key1 != NULL) ? "/" : "", \ + (key1 != NULL) ? key1 + 1 : "", \ + knot_strerror(ret)); \ + return ret; \ + } + +static int migrate_rrl( + conf_t *conf, + knot_db_txn_t *txn) +{ + #define MOD_RRL "\x07""mod-rrl" + #define MOD_RATE_LIMIT "\x0A""rate-limit" + #define MOD_SLIP "\x04""slip" + #define MOD_TBL_SIZE "\x0A""table-size" + #define MOD_WHITELIST "\x09""whitelist" + + const uint8_t *id = CONF_DEFAULT_ID + 1; + const size_t id_len = CONF_DEFAULT_ID[0]; + const uint8_t *dflt_rrl = (const uint8_t *)MOD_RRL "default\0"; + const size_t dflt_rrl_len = 16; + + conf_val_t val; + int ret = conf_db_get(conf, txn, C_SRV, C_RATE_LIMIT, NULL, 0, &val); + + // Migrate old configuration if RRL enabled. + if (ret == KNOT_EOK && conf_int(&val) > 0) { + log_notice("config, migrating RRL configuration from server to mod-rrl"); + + // Create equivalent mod-rrl configuration. + check_set(conf, txn, MOD_RRL, C_ID, id, id_len, NULL, 0); + check_set(conf, txn, MOD_RRL, MOD_RATE_LIMIT, id, id_len, + val.data, val.len); + + conf_db_get(conf, txn, C_SRV, C_RATE_LIMIT_SLIP, NULL, 0, &val); + if (val.code == KNOT_EOK) { + conf_val(&val); + check_set(conf, txn, MOD_RRL, MOD_SLIP, id, id_len, + val.data, val.len); + } + + conf_db_get(conf, txn, C_SRV, C_RATE_LIMIT_TBL_SIZE, NULL, 0, &val); + if (val.code == KNOT_EOK) { + conf_val(&val); + check_set(conf, txn, MOD_RRL, MOD_TBL_SIZE, id, id_len, + val.data, val.len); + } + + conf_db_get(conf, txn, C_SRV, C_RATE_LIMIT_WHITELIST, NULL, 0, &val); + while (val.code == KNOT_EOK) { + conf_val(&val); + check_set(conf, txn, MOD_RRL, MOD_WHITELIST, id, id_len, + val.data, val.len); + conf_val_next(&val); + } + + // Create default template and assing global module. + check_set(conf, txn, C_TPL, C_ID, id, id_len, NULL, 0); + check_set(conf, txn, C_TPL, C_GLOBAL_MODULE, id, id_len, + dflt_rrl, dflt_rrl_len); + } + + // Drop old RRL configuration. + try_unset(conf, txn, C_SRV, C_RATE_LIMIT); + try_unset(conf, txn, C_SRV, C_RATE_LIMIT_SLIP); + try_unset(conf, txn, C_SRV, C_RATE_LIMIT_TBL_SIZE); + try_unset(conf, txn, C_SRV, C_RATE_LIMIT_WHITELIST); + + return KNOT_EOK; +} + +int conf_migrate( + conf_t *conf) +{ + if (conf == NULL) { + return KNOT_EINVAL; + } + + knot_db_txn_t txn; + int ret = conf->api->txn_begin(conf->db, &txn, 0); + if (ret != KNOT_EOK) { + return ret; + } + + ret = migrate_rrl(conf, &txn); + if (ret != KNOT_EOK) { + conf->api->txn_abort(&txn); + return ret; + } + + ret = conf->api->txn_commit(&txn); + if (ret != KNOT_EOK) { + return ret; + } + + return conf_refresh_txn(conf); +} diff --git a/src/knot/conf/migration.h b/src/knot/conf/migration.h new file mode 100644 index 0000000000000000000000000000000000000000..dd7912f70d09cd5bd1e2fa6a50a2f37efc467159 --- /dev/null +++ b/src/knot/conf/migration.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/conf/base.h" + +/*! + * Migrates from an old configuration schema. + * + * \param[in] conf Configuration. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_migrate( + conf_t *conf +); diff --git a/src/knot/conf/scheme.c b/src/knot/conf/scheme.c index af4e0596295563f94e67ec06701a50712ae7b440..566feaffe67675f6f220360104c6967f41414000 100644 --- a/src/knot/conf/scheme.c +++ b/src/knot/conf/scheme.c @@ -23,12 +23,12 @@ #include "knot/conf/confio.h" #include "knot/conf/tools.h" #include "knot/common/log.h" -#include "knot/server/rrl.h" #include "knot/updates/acl.h" #include "libknot/rrtype/opt.h" #include "dnssec/lib/dnssec/tsig.h" #include "dnssec/lib/dnssec/key.h" +#include "knot/modules/rrl/rrl.h" #include "knot/modules/stats/stats.h" #include "knot/modules/synth_record/synth_record.h" #include "knot/modules/dnsproxy/dnsproxy.h" @@ -122,13 +122,14 @@ static const yp_item_t desc_server[] = { { C_MAX_IPV6_UDP_PAYLOAD, YP_TINT, YP_VINT = { KNOT_EDNS_MIN_UDP_PAYLOAD, KNOT_EDNS_MAX_UDP_PAYLOAD, 4096, YP_SSIZE } }, + { C_LISTEN, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + /* Obsolete items. */ { C_RATE_LIMIT, YP_TINT, YP_VINT = { 0, INT32_MAX, 0 } }, - { C_RATE_LIMIT_SLIP, YP_TINT, YP_VINT = { 0, RRL_SLIP_MAX, 1 } }, + { C_RATE_LIMIT_SLIP, YP_TINT, YP_VINT = { 0, 100, 1 } }, { C_RATE_LIMIT_TBL_SIZE, YP_TINT, YP_VINT = { 1, INT32_MAX, 393241 } }, { C_RATE_LIMIT_WHITELIST, YP_TDATA, YP_VDATA = { 0, NULL, addr_range_to_bin, addr_range_to_txt }, YP_FMULTI }, - { C_LISTEN, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI }, - { C_COMMENT, YP_TSTR, YP_VNONE }, { NULL } }; @@ -266,7 +267,7 @@ static const yp_item_t desc_zone[] = { }; const yp_item_t conf_scheme[] = { - { C_SRV, YP_TGRP, YP_VGRP = { desc_server }, CONF_IO_FRLD_SRV }, + { C_SRV, YP_TGRP, YP_VGRP = { desc_server }, CONF_IO_FRLD_SRV, { check_server } }, { C_CTL, YP_TGRP, YP_VGRP = { desc_control } }, { C_LOG, YP_TGRP, YP_VGRP = { desc_log }, YP_FMULTI | CONF_IO_FRLD_LOG }, { C_STATS, YP_TGRP, YP_VGRP = { desc_stats }, CONF_IO_FRLD_SRV }, @@ -276,6 +277,8 @@ const yp_item_t conf_scheme[] = { { C_ACL, YP_TGRP, YP_VGRP = { desc_acl }, YP_FMULTI, { check_acl } }, { C_RMT, YP_TGRP, YP_VGRP = { desc_remote }, YP_FMULTI, { check_remote } }, /* MODULES */ + { C_MOD_RRL, YP_TGRP, YP_VGRP = { scheme_mod_rrl }, FMOD, + { check_mod_rrl } }, { C_MOD_STATS, YP_TGRP, YP_VGRP = { scheme_mod_stats }, FMOD }, { C_MOD_SYNTH_RECORD, YP_TGRP, YP_VGRP = { scheme_mod_synth_record }, FMOD, { check_mod_synth_record } }, diff --git a/src/knot/conf/tools.c b/src/knot/conf/tools.c index 2823711700a6cec6cbc3e6dd183907ee74d3a359..14127c198a20c967abbb091c17d98a8ade9cd092 100644 --- a/src/knot/conf/tools.c +++ b/src/knot/conf/tools.c @@ -353,6 +353,40 @@ int check_modref( return KNOT_EOK; } +int check_server( + conf_check_t *args) +{ + bool present = false; + + conf_val_t val; + val = conf_get_txn(args->conf, args->txn, C_SRV, C_RATE_LIMIT); + if (val.code == KNOT_EOK) { + present = true; + } + + val = conf_get_txn(args->conf, args->txn, C_SRV, C_RATE_LIMIT_SLIP); + if (val.code == KNOT_EOK) { + present = true; + } + + val = conf_get_txn(args->conf, args->txn, C_SRV, C_RATE_LIMIT_TBL_SIZE); + if (val.code == KNOT_EOK) { + present = true; + } + + val = conf_get_txn(args->conf, args->txn, C_SRV, C_RATE_LIMIT_WHITELIST); + if (val.code == KNOT_EOK) { + present = true; + } + + if (present) { + CONF_LOG(LOG_NOTICE, "obsolete RRL configuration in the server, " + "use module mod-rrl instead"); + } + + return KNOT_EOK; +} + int check_keystore( conf_check_t *args) { @@ -503,8 +537,8 @@ int check_zone( C_DNSSEC_POLICY, args->id); if (conf_bool(&signing) && policy.code != KNOT_EOK) { CONF_LOG(LOG_NOTICE, "DNSSEC policy settings in KASP database " - "is obsolete and will be removed in the next major release. " - "Use zone.dnssec-policy in server configuration instead."); + "is obsolete and will be removed in the next major release, " + "use zone.dnssec-policy in server configuration instead"); } return KNOT_EOK; diff --git a/src/knot/conf/tools.h b/src/knot/conf/tools.h index 8378a0b42d0d5a6617fa2991d2b349011b1be036..041d360e5efa4d1841d28fde4e487913f82564bf 100644 --- a/src/knot/conf/tools.h +++ b/src/knot/conf/tools.h @@ -84,6 +84,10 @@ int check_modref( conf_check_t *args ); +int check_server( + conf_check_t *args +); + int check_keystore( conf_check_t *args ); diff --git a/src/knot/server/rrl.c b/src/knot/modules/rrl/functions.c similarity index 91% rename from src/knot/server/rrl.c rename to src/knot/modules/rrl/functions.c index 27026f8a09a7500ca57d06a2378abbcb2816be32..df6cdd56250e1413a516a71268b99165acbacd26 100644 --- a/src/knot/server/rrl.c +++ b/src/knot/modules/rrl/functions.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,13 +17,11 @@ #include <assert.h> #include <time.h> -#include "dnssec/random.h" -#include "knot/common/log.h" -#include "knot/server/rrl.h" -#include "knot/zone/zone.h" -#include "libknot/libknot.h" #include "contrib/murmurhash3/murmurhash3.h" #include "contrib/sockaddr.h" +#include "dnssec/random.h" +#include "knot/modules/rrl/functions.h" +#include "knot/common/log.h" /* Hopscotch defines. */ #define HOP_LEN (sizeof(unsigned)*8) @@ -33,7 +31,6 @@ #define RRL_V4_PREFIX ((uint32_t)0x00ffffff) /* /24 */ #define RRL_V6_PREFIX ((uint64_t)0x00ffffffffffffff) /* /56 */ /* Defaults */ -#define RRL_DEFAULT_RATE 100 #define RRL_CAPACITY 4 /* N seconds. */ #define RRL_SSTART 2 /* 1/Nth of the rate for slow start */ #define RRL_PSIZE_LARGE 1024 @@ -60,16 +57,16 @@ struct cls_name { }; static const struct cls_name rrl_cls_names[] = { - {CLS_NORMAL, "POSITIVE" }, - {CLS_ERROR, "ERROR" }, - {CLS_NXDOMAIN,"NXDOMAIN"}, - {CLS_EMPTY, "EMPTY"}, - {CLS_LARGE, "LARGE"}, - {CLS_WILDCARD,"WILDCARD"}, - {CLS_ANY, "ANY"}, - {CLS_DNSSEC, "DNSSEC"}, - {CLS_NULL, "NULL"}, - {CLS_NULL, NULL} + { CLS_NORMAL, "POSITIVE" }, + { CLS_ERROR, "ERROR" }, + { CLS_NXDOMAIN, "NXDOMAIN"}, + { CLS_EMPTY, "EMPTY"}, + { CLS_LARGE, "LARGE"}, + { CLS_WILDCARD, "WILDCARD"}, + { CLS_ANY, "ANY"}, + { CLS_DNSSEC, "DNSSEC"}, + { CLS_NULL, "NULL"}, + { CLS_NULL, NULL} }; static inline const char *rrl_clsstr(int code) @@ -134,15 +131,12 @@ static uint8_t rrl_clsid(rrl_req_t *p) return ret; } -static int rrl_clsname(char *dst, size_t maxlen, uint8_t cls, - rrl_req_t *req, const zone_t *zone) +static int rrl_clsname(char *dst, size_t maxlen, uint8_t cls, rrl_req_t *req, + const knot_dname_t *name) { - /* Fallback zone (for errors etc.) */ - const knot_dname_t *dn = (const knot_dname_t *)"\x00"; - - /* Found associated zone. */ - if (zone != NULL) { - dn = zone->name; + if (name == NULL) { + /* Fallback for errors etc. */ + name = (const knot_dname_t *)"\x00"; } switch (cls) { @@ -153,17 +147,17 @@ static int rrl_clsname(char *dst, size_t maxlen, uint8_t cls, default: /* Use QNAME */ if (req->query) { - dn = knot_pkt_qname(req->query); + name = knot_pkt_qname(req->query); } break; } /* Write to wire */ - return knot_dname_to_wire((uint8_t *)dst, dn, maxlen); + return knot_dname_to_wire((uint8_t *)dst, name, maxlen); } static int rrl_classify(char *dst, size_t maxlen, const struct sockaddr_storage *a, - rrl_req_t *p, const zone_t *z, uint32_t seed) + rrl_req_t *p, const knot_dname_t *z, uint32_t seed) { if (!dst || !p || !a || maxlen == 0) { return KNOT_EINVAL; @@ -295,7 +289,7 @@ static void rrl_log_state(const struct sockaddr_storage *ss, uint16_t flags, uin what = "enters"; } - log_notice("rate limiting, address '%s' class '%s' %s limiting", + log_notice("mod-rrl, address '%s' class '%s' %s limiting", addr_str, rrl_clsstr(cls), what); #endif } @@ -375,7 +369,7 @@ int rrl_setlocks(rrl_table_t *rrl, unsigned granularity) } rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, rrl_req_t *p, - const zone_t *zone, uint32_t stamp, int *lock) + const knot_dname_t *zone, uint32_t stamp, int *lock) { char buf[RRL_CLSBLK_MAXLEN]; int len = rrl_classify(buf, sizeof(buf), a, p, zone, t->seed); @@ -437,7 +431,7 @@ rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, rrl_req_t } int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *a, rrl_req_t *req, - const zone_t *zone) + const knot_dname_t *zone) { if (!rrl || !req || !a) { return KNOT_EINVAL; diff --git a/src/knot/server/rrl.h b/src/knot/modules/rrl/functions.h similarity index 81% rename from src/knot/server/rrl.h rename to src/knot/modules/rrl/functions.h index 6d01ff0b0c70e77178a5c64a64776d404098bbca..a81fa02da59cae7a48d12aaba7e4f58a1b58a55b 100644 --- a/src/knot/server/rrl.h +++ b/src/knot/modules/rrl/functions.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -13,22 +13,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/*! - * \file rrl.h - * - * \author Marek Vavrusa <marek.vavusa@nic.cz> - * - * \brief Response-rate limiting API. - * - * \addtogroup network - * @{ - */ #pragma once #include <stdint.h> #include <pthread.h> #include <sys/socket.h> + +#include "libknot/dname.h" #include "libknot/packet/pkt.h" /* Defaults */ @@ -41,19 +33,17 @@ enum { RRL_WILDCARD = 1 << 1 /*!< Query to wildcard name. */ }; -struct zone; - /*! * \brief RRL hash bucket. */ -typedef struct rrl_item { +typedef struct { unsigned hop; /* Hop bitmap. */ uint64_t netblk; /* Prefix associated. */ - uint16_t ntok; /* Tokens available */ - uint8_t cls; /* Bucket class */ - uint8_t flags; /* Flags */ - uint32_t qname; /* imputed(QNAME) hash */ - uint32_t time; /* Timestamp */ + uint16_t ntok; /* Tokens available. */ + uint8_t cls; /* Bucket class. */ + uint8_t flags; /* Flags. */ + uint32_t qname; /* imputed(QNAME) hash. */ + uint32_t time; /* Timestamp. */ } rrl_item_t; /*! @@ -68,20 +58,20 @@ typedef struct rrl_item { * As of now lock K for bucket N is calculated as K = N % (num_buckets). */ -typedef struct rrl_table { - uint32_t rate; /* Configured RRL limit */ +typedef struct { + uint32_t rate; /* Configured RRL limit. */ uint32_t seed; /* Pseudorandom seed for hashing. */ pthread_mutex_t ll; - pthread_mutex_t *lk; /* Table locks. */ + pthread_mutex_t *lk; /* Table locks. */ unsigned lk_count; /* Table lock count (granularity). */ - size_t size; /* Number of buckets */ - rrl_item_t arr[]; /* Buckets */ + size_t size; /* Number of buckets. */ + rrl_item_t arr[]; /* Buckets. */ } rrl_table_t; /*! * \brief RRL request descriptor. */ -typedef struct rrl_req { +typedef struct { const uint8_t *w; uint16_t len; unsigned flags; @@ -128,13 +118,13 @@ int rrl_setlocks(rrl_table_t *rrl, unsigned granularity); * \param t RRL table. * \param a Source address. * \param p RRL request. - * \param zone Relate zone. + * \param zone Relate zone name. * \param stamp Timestamp (current time). * \param lock Held lock. * \return assigned bucket */ -rrl_item_t* rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, rrl_req_t *p, - const struct zone *zone, uint32_t stamp, int* lock); +rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, rrl_req_t *p, + const knot_dname_t *zone, uint32_t stamp, int *lock); /*! * \brief Query the RRL table for accept or deny, when the rate limit is reached. @@ -142,12 +132,12 @@ rrl_item_t* rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, rrl_req_t * \param rrl RRL table. * \param a Source address. * \param req RRL request (containing resp., flags and question). - * \param zone Zone related to the response (or NULL). + * \param zone Zone name related to the response (or NULL). * \retval KNOT_EOK if passed. * \retval KNOT_ELIMIT when the limit is reached. */ int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *a, rrl_req_t *req, - const struct zone *zone); + const knot_dname_t *zone); /*! * \brief Roll a dice whether answer slips or not. @@ -187,5 +177,3 @@ int rrl_lock(rrl_table_t *rrl, int lk_id); * \retval KNOT_ERROR */ int rrl_unlock(rrl_table_t *rrl, int lk_id); - -/*! @} */ diff --git a/src/knot/modules/rrl/rrl.c b/src/knot/modules/rrl/rrl.c new file mode 100644 index 0000000000000000000000000000000000000000..445465a18e5a0fb9da51270c360e55ffd9ac9bc1 --- /dev/null +++ b/src/knot/modules/rrl/rrl.c @@ -0,0 +1,173 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "contrib/sockaddr.h" +#include "contrib/mempattern.h" +#include "knot/modules/rrl/rrl.h" +#include "knot/modules/rrl/functions.h" + +/* Module configuration scheme. */ +#define MOD_RATE_LIMIT "\x0A""rate-limit" +#define MOD_SLIP "\x04""slip" +#define MOD_TBL_SIZE "\x0A""table-size" +#define MOD_WHITELIST "\x09""whitelist" + +const yp_item_t scheme_mod_rrl[] = { + { C_ID, YP_TSTR, YP_VNONE }, + { MOD_RATE_LIMIT, YP_TINT, YP_VINT = { 1, INT32_MAX } }, + { MOD_SLIP, YP_TINT, YP_VINT = { 0, RRL_SLIP_MAX, 1 } }, + { MOD_TBL_SIZE, YP_TINT, YP_VINT = { 1, INT32_MAX, 393241 } }, + { MOD_WHITELIST, YP_TDATA, YP_VDATA = { 0, NULL, addr_range_to_bin, + addr_range_to_txt }, YP_FMULTI }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + { NULL } +}; + +int check_mod_rrl(conf_check_t *args) +{ + conf_val_t rl = conf_rawid_get_txn(args->conf, args->txn, C_MOD_RRL, + MOD_RATE_LIMIT, args->id, args->id_len); + if (rl.code != KNOT_EOK) { + args->err_str = "no rate limit specified"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +typedef struct { + mod_ctr_t *counters; + rrl_table_t *rrl; + int slip; + conf_val_t whitelist; +} rrl_ctx_t; + +static int ratelimit_apply(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx) +{ + assert(pkt && qdata && ctx); + + rrl_ctx_t *context = ctx; + + // Rate limit is not applied to TCP connections. + if (!(qdata->param->proc_flags & NS_QUERY_LIMIT_SIZE)) { + return state; + } + + // Exempt clients. + if (conf_addr_range_match(&context->whitelist, qdata->param->remote)) { + return state; + } + + rrl_req_t req = { + .w = pkt->wire, + .query = qdata->query + }; + + if (!EMPTY_LIST(qdata->wildcards)) { + req.flags = RRL_WILDCARD; + } + + const knot_dname_t *zone_name = (qdata->zone != NULL) ? qdata->zone->name : NULL; + + if (rrl_query(context->rrl, qdata->param->remote, &req, zone_name) == KNOT_EOK) { + // Rate limiting not applied. + return state; + } + + if (context->slip > 0 && rrl_slip_roll(context->slip)) { + // Slip the answer. + mod_ctr_incr(context->counters, 0, 1); + qdata->err_truncated = true; + return KNOT_STATE_FAIL; + } else { + // Drop the answer. + mod_ctr_incr(context->counters, 1, 1); + pkt->size = 0; + return KNOT_STATE_DONE; + } +} + +int rrl_load(struct query_plan *plan, struct query_module *self, + const knot_dname_t *zone) +{ + assert(self); + + // Create RRL context. + rrl_ctx_t *ctx = mm_alloc(self->mm, sizeof(rrl_ctx_t)); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + memset(ctx, 0, sizeof(*ctx)); + + // Create table. + conf_val_t val = conf_mod_get(self->config, MOD_TBL_SIZE, self->id); + ctx->rrl = rrl_create(conf_int(&val)); + if (ctx->rrl == NULL) { + mm_free(self->mm, ctx); + return KNOT_ENOMEM; + } + + // Set locks. + int ret = rrl_setlocks(ctx->rrl, RRL_LOCK_GRANULARITY); + if (ret != KNOT_EOK) { + rrl_unload(self); + return ret; + } + + // Set rate limit. + val = conf_mod_get(self->config, MOD_RATE_LIMIT, self->id); + ret = rrl_setrate(ctx->rrl, conf_int(&val)); + if (ret != KNOT_EOK) { + rrl_unload(self); + return ret; + } + + // Get whitelist. + val = conf_mod_get(self->config, MOD_WHITELIST, self->id); + ctx->whitelist = val; + + // Get slip. + val = conf_mod_get(self->config, MOD_SLIP, self->id); + ctx->slip = conf_int(&val); + + // Set up statistics counters. + ret = mod_stats_add(self, "slipped", 1, NULL); + if (ret != KNOT_EOK) { + rrl_unload(self); + return ret; + } + + ret = mod_stats_add(self, "dropped", 1, NULL); + if (ret != KNOT_EOK) { + rrl_unload(self); + return ret; + } + + ctx->counters = self->stats; + self->ctx = ctx; + + return query_plan_step(plan, QPLAN_END, ratelimit_apply, self->ctx); +} + +void rrl_unload(struct query_module *self) +{ + assert(self); + + rrl_ctx_t *ctx = self->ctx; + + rrl_destroy(ctx->rrl); + mm_free(self->mm, self->ctx); +} diff --git a/src/knot/modules/rrl/rrl.h b/src/knot/modules/rrl/rrl.h new file mode 100644 index 0000000000000000000000000000000000000000..acb7590f840485374bdc0bdf0626692304d8d8a3 --- /dev/null +++ b/src/knot/modules/rrl/rrl.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/nameserver/query_module.h" + +/*! \brief Module scheme. */ +#define C_MOD_RRL "\x07""mod-rrl" +extern const yp_item_t scheme_mod_rrl[]; +int check_mod_rrl(conf_check_t *args); + +/*! \brief Module interface. */ +int rrl_load(struct query_plan *plan, struct query_module *self, + const knot_dname_t *zone); +void rrl_unload(struct query_module *self); diff --git a/src/knot/nameserver/process_query.c b/src/knot/nameserver/process_query.c index 29afa68effd63d00302e72a0a96f1bdc200fe47d..b3debdf37d636d1495f01c85b4ae8e0041fecd67 100644 --- a/src/knot/nameserver/process_query.c +++ b/src/knot/nameserver/process_query.c @@ -413,52 +413,6 @@ static int process_query_err(knot_layer_t *ctx, knot_pkt_t *pkt) return KNOT_STATE_DONE; } -/*! - * \brief Apply rate limit. - */ -static int ratelimit_apply(int state, knot_pkt_t *pkt, knot_layer_t *ctx) -{ - /* Check if rate limiting applies. */ - struct query_data *qdata = QUERY_DATA(ctx); - server_t *server = qdata->param->server; - if (server->rrl == NULL) { - return state; - } - - /* Exempt clients. */ - conf_val_t *whitelist = &conf()->cache.srv_rate_limit_whitelist; - if (conf_addr_range_match(whitelist, qdata->param->remote)) { - return state; - } - - rrl_req_t rrl_rq = {0}; - rrl_rq.w = pkt->wire; - rrl_rq.query = qdata->query; - if (!EMPTY_LIST(qdata->wildcards)) { - rrl_rq.flags = RRL_WILDCARD; - } - if (rrl_query(server->rrl, qdata->param->remote, - &rrl_rq, qdata->zone) == KNOT_EOK) { - /* Rate limiting not applied. */ - return state; - } - - /* Now it is slip or drop. */ - int slip = conf()->cache.srv_rate_limit_slip; - if (slip > 0 && rrl_slip_roll(slip)) { - /* Answer slips. */ - if (process_query_err(ctx, pkt) != KNOT_STATE_DONE) { - return KNOT_STATE_FAIL; - } - knot_wire_set_tc(pkt->wire); - } else { - /* Drop answer. */ - pkt->size = 0; - } - - return KNOT_STATE_DONE; -} - static int process_query_out(knot_layer_t *ctx, knot_pkt_t *pkt) { assert(pkt && ctx); @@ -558,11 +512,6 @@ finish: } } - /* Rate limits (if applicable). */ - if (qdata->param->proc_flags & NS_QUERY_LIMIT_RATE) { - next_state = ratelimit_apply(next_state, pkt, ctx); - } - rcu_read_unlock(); return next_state; diff --git a/src/knot/nameserver/process_query.h b/src/knot/nameserver/process_query.h index 0698e6d7a4c8f4b6388533b334d6ac261560a268..401da20d2985f3fe49429e17a2dff9a3c12ce398 100644 --- a/src/knot/nameserver/process_query.h +++ b/src/knot/nameserver/process_query.h @@ -29,8 +29,7 @@ enum process_query_flag { NS_QUERY_NO_AXFR = 1 << 0, /* Don't process AXFR */ NS_QUERY_NO_IXFR = 1 << 1, /* Don't process IXFR */ NS_QUERY_LIMIT_ANY = 1 << 2, /* Limit ANY QTYPE (respond with TC=1) */ - NS_QUERY_LIMIT_RATE = 1 << 3, /* Apply rate limits. */ - NS_QUERY_LIMIT_SIZE = 1 << 4 /* Apply UDP size limit. */ + NS_QUERY_LIMIT_SIZE = 1 << 3 /* Apply UDP size limit. */ }; /* Module load parameters. */ diff --git a/src/knot/nameserver/query_module.c b/src/knot/nameserver/query_module.c index 8e0ca7d7c55a4b607d5569015f3c54bd0f85e715..67c67f5a8aeaacf8edd3bc1ccfd9b0bf5b144ba7 100644 --- a/src/knot/nameserver/query_module.c +++ b/src/knot/nameserver/query_module.c @@ -19,6 +19,7 @@ #include "knot/nameserver/query_module.h" #include "contrib/mempattern.h" +#include "knot/modules/rrl/rrl.h" #include "knot/modules/stats/stats.h" #include "knot/modules/synth_record/synth_record.h" #include "knot/modules/dnsproxy/dnsproxy.h" @@ -34,6 +35,7 @@ /*! \note All modules should be dynamically loaded later on. */ static_module_t MODULES[] = { + { C_MOD_RRL, &rrl_load, &rrl_unload, MOD_SCOPE_ANY }, { C_MOD_SYNTH_RECORD, &synth_record_load, &synth_record_unload, MOD_SCOPE_ANY }, { C_MOD_DNSPROXY, &dnsproxy_load, &dnsproxy_unload, MOD_SCOPE_ANY }, { C_MOD_ONLINE_SIGN, &online_sign_load, &online_sign_unload, MOD_SCOPE_ZONE, true }, diff --git a/src/knot/server/server.c b/src/knot/server/server.c index c1bff958cdd6363c8704438353bf1e7cbe9e9e71..cf91649064c4f32b622334b951ec3c732efaa3f8 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -24,6 +24,7 @@ #include "knot/common/log.h" #include "knot/common/stats.h" #include "knot/conf/confio.h" +#include "knot/conf/migration.h" #include "knot/server/server.h" #include "knot/server/udp-handler.h" #include "knot/server/tcp-handler.h" @@ -399,9 +400,6 @@ void server_deinit(server_t *server) /* Free threads and event handlers. */ worker_pool_destroy(server->workers); - /* Free rate limits. */ - rrl_destroy(server->rrl); - /* Free zone database. */ knot_zonedb_deep_free(&server->zone_db); @@ -528,6 +526,12 @@ static int reload_conf(conf_t *new_conf) log_info("reloading configuration database"); } + // Migrate from old schema. + int ret = conf_migrate(new_conf); + if (ret != KNOT_EOK) { + log_error("failed to migrate configuration (%s)", knot_strerror(ret)); + } + /* Refresh hostname. */ conf_refresh_hostname(new_conf); @@ -656,39 +660,6 @@ static int reconfigure_threads(conf_t *conf, server_t *server) return reset_handler(server, IO_TCP, conf_tcp_threads(conf), tcp_master); } -static int reconfigure_rate_limits(conf_t *conf, server_t *server) -{ - conf_val_t val = conf_get(conf, C_SRV, C_RATE_LIMIT); - int64_t rrl = conf_int(&val); - - /* Rate limiting. */ - if (!server->rrl && rrl > 0) { - val = conf_get(conf, C_SRV, C_RATE_LIMIT_TBL_SIZE); - server->rrl = rrl_create(conf_int(&val)); - if (!server->rrl) { - log_error("failed to initialize rate limiting table"); - } else { - rrl_setlocks(server->rrl, RRL_LOCK_GRANULARITY); - } - } - if (server->rrl) { - if (rrl_rate(server->rrl) != rrl) { - /* We cannot free it, threads may use it. - * Setting it to <1 will disable rate limiting. */ - if (rrl < 1) { - log_info("rate limiting, disabled"); - } else { - log_info("rate limiting, enabled with %i responses/second", - (int)rrl); - } - rrl_setrate(server->rrl, rrl); - - } /* At this point, old buckets will converge to new rate. */ - } - - return KNOT_EOK; -} - void server_reconfigure(conf_t *conf, server_t *server) { if (conf == NULL || server == NULL) { @@ -700,14 +671,8 @@ void server_reconfigure(conf_t *conf, server_t *server) log_info("Knot DNS %s starting", PACKAGE_VERSION); } - /* Reconfigure rate limits. */ - int ret; - if ((ret = reconfigure_rate_limits(conf, server)) < 0) { - log_error("failed to reconfigure rate limits (%s)", - knot_strerror(ret)); - } - /* Reconfigure server threads. */ + int ret; if ((ret = reconfigure_threads(conf, server)) < 0) { log_error("failed to reconfigure server threads (%s)", knot_strerror(ret)); diff --git a/src/knot/server/server.h b/src/knot/server/server.h index 284923591ac04aa8e67be859d2a599ff90f70c5d..4b155764673489ff3415d80df053fdac9936497b 100644 --- a/src/knot/server/server.h +++ b/src/knot/server/server.h @@ -34,7 +34,6 @@ #include "knot/common/fdset.h" #include "knot/server/dthreads.h" #include "knot/common/ref.h" -#include "knot/server/rrl.h" #include "knot/worker/pool.h" #include "knot/zone/zonedb.h" #include "contrib/ucw/lists.h" @@ -110,10 +109,7 @@ typedef struct server { evsched_t sched; /*! \brief List of interfaces. */ - ifacelist_t* ifaces; - - /*! \brief Rate limiting. */ - rrl_table_t *rrl; + ifacelist_t *ifaces; } server_t; diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c index e789ec9db4ee2dd9f22b1e3cf534c0a901ea2c9a..16d0130a23b46c6234a9b5b0398d0e00c5df2db5 100644 --- a/src/knot/server/udp-handler.c +++ b/src/knot/server/udp-handler.c @@ -59,19 +59,15 @@ static void udp_handle(udp_context_t *udp, int fd, struct sockaddr_storage *ss, struct iovec *rx, struct iovec *tx) { /* Create query processing parameter. */ - struct process_query_param param = {0}; - param.remote = ss; - param.proc_flags = NS_QUERY_NO_AXFR|NS_QUERY_NO_IXFR; /* No transfers. */ - param.proc_flags |= NS_QUERY_LIMIT_SIZE; /* Enforce UDP packet size limit. */ - param.proc_flags |= NS_QUERY_LIMIT_ANY; /* Limit ANY over UDP (depends on zone as well). */ - param.socket = fd; - param.server = udp->server; - param.thread_id = udp->thread_id; - - /* Rate limit is applied? */ - if (unlikely(udp->server->rrl != NULL) && udp->server->rrl->rate > 0) { - param.proc_flags |= NS_QUERY_LIMIT_RATE; - } + struct process_query_param param = { + .remote = ss, + .proc_flags = NS_QUERY_NO_AXFR | NS_QUERY_NO_IXFR | /* No transfers. */ + NS_QUERY_LIMIT_SIZE | /* Enforce UDP packet size limit. */ + NS_QUERY_LIMIT_ANY, /* Limit ANY over UDP (depends on zone as well). */ + .socket = fd, + .server = udp->server, + .thread_id = udp->thread_id + }; /* Start query processing. */ udp->layer.state = knot_layer_begin(&udp->layer, ¶m); diff --git a/src/utils/knotd/main.c b/src/utils/knotd/main.c index dd2bec909975e5e5735b408ed368dc47d9461b86..a8ebaaf471e659f24de5542e08a2345bfeaf086c 100644 --- a/src/utils/knotd/main.c +++ b/src/utils/knotd/main.c @@ -36,6 +36,7 @@ #include "libknot/libknot.h" #include "knot/ctl/process.h" #include "knot/conf/conf.h" +#include "knot/conf/migration.h" #include "knot/common/log.h" #include "knot/common/process.h" #include "knot/common/stats.h" @@ -354,6 +355,12 @@ static int set_config(const char *confdb, const char *config) } } + // Migrate from old schema. + ret = conf_migrate(new_conf); + if (ret != KNOT_EOK) { + log_error("failed to migrate configuration (%s)", knot_strerror(ret)); + } + /* Activate global query modules. */ conf_activate_modules(new_conf, NULL, &new_conf->query_modules, &new_conf->query_plan); diff --git a/tests-extra/tests/modules/rrl/test.py b/tests-extra/tests/modules/rrl/test.py new file mode 100644 index 0000000000000000000000000000000000000000..a79f02e9f513e17a307f1189cd1a1296350c260e --- /dev/null +++ b/tests-extra/tests/modules/rrl/test.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 + +'''RRL module functionality test''' + +import dns.exception +import dns.message +import dns.query +import os +import time + +from dnstest.libknot import libknot +from dnstest.test import Test +from dnstest.module import ModRRL +from dnstest.utils import * + +ctl = libknot.control.KnotCtl() + +t = Test(stress=False) + +ModRRL.check() + +knot = t.server("knot") +zones = t.zone_rnd(2, dnssec=False, records=1) +t.link(zones, knot) + +def send_queries(server, name, run_time=1.0, query_time=0.05): + """ + Send UDP queries to the server for certain time and get replies statistics. + """ + replied, slipped, dropped = 0, 0, 0 + start = time.time() + while time.time() < start + run_time: + try: + query = dns.message.make_query(name, "SOA", want_dnssec=False) + response = dns.query.udp(query, server.addr, port=server.port, timeout=query_time) + except dns.exception.Timeout: + response = None + + if response is None: + dropped += 1 + elif response.flags & dns.flags.TC: + slipped += 1 + else: + replied += 1 + + return dict(replied=replied, slipped=slipped, dropped=dropped) + +def check_result(name, res, rate=0, slip=None): + """ + Check response result. + + We cannot send queries in parallel. And we have to give the server some time + to respond, especially under valgrind. Therefore we have to be tolerant when + counting responses when packets are being dropped. + """ + detail_log("RRL %s" % name) + detail_log(", ".join(["%s %d" % (s, res[s]) for s in ["replied", "slipped", "dropped"]])) + + ok = False + + if rate == 0: + ok = res["replied"] >= 100 and res["slipped"] == 0 and res["dropped"] == 0 + elif slip == 0: + ok = res["replied"] > 0 and res["replied"] < 100 and \ + res["slipped"] == 0 and res["dropped"] >= 5 + elif slip == 1: + ok = res["replied"] > 0 and res["replied"] < 100 and \ + res["slipped"] >= 100 and res["dropped"] == 0 + else: + ok = res["replied"] > 0 and res["replied"] < 100 and \ + res["slipped"] >= 5 and res["dropped"] >= 5 + + if ok: + detail_log("success") + else: + detail_log("error") + set_err("RRL ERROR") + +def cmp_stats(server, res, zone_name=None): + try: + ctl = libknot.control.KnotCtl() + ctl.connect(os.path.join(server.dir, "knot.sock")) + + if zone_name: + ctl.send_block(cmd="zone-stats", section="mod-rrl", zone=zone_name, flags="F") + else: + ctl.send_block(cmd="stats", section="mod-rrl", flags="F") + + stats = ctl.receive_stats() + detail_log(stats) + + if zone_name: + ok = int(stats["zone"][zone_name.lower()]["mod-rrl"]["dropped"]) == res["dropped"] and \ + int(stats["zone"][zone_name.lower()]["mod-rrl"]["slipped"]) == res["slipped"] + else: + ok = int(stats["mod-rrl"]["dropped"]) == res["dropped"] and \ + int(stats["mod-rrl"]["slipped"]) == res["slipped"] + + if ok: + detail_log("stats success") + else: + detail_log("stats error") + set_err("RRL STATS ERROR") + + finally: + ctl.send(libknot.control.KnotCtlType.END) + ctl.close() + +def reconfigure(server, zone, rate_limit, slip, whitelist=None): + """ + Reconfigure server module. + """ + server.clear_modules(None) + server.clear_modules(zone) + server.add_module(zone, ModRRL(rate_limit=rate_limit, slip=slip, whitelist=whitelist)) + server.gen_confile() + server.reload() + +t.start() + +knot.zones_wait(zones) + +### RRL global module + +# Disabled +res = send_queries(knot, zones[0].name) +check_result("disabled", res) + +# All drop +reconfigure(knot, None, 5, 0) +res = send_queries(knot, zones[0].name) +check_result("global, zone 1, all drop", res, 5, 0) +cmp_stats(knot, res) +time.sleep(2) +res = send_queries(knot, zones[1].name) +check_result("global, zone 2, all drop", res, 5, 0) + +# All slip +reconfigure(knot, None, 5, 1) +res = send_queries(knot, zones[0].name) +check_result("global, zone 1, all slip", res, 5, 1) +cmp_stats(knot, res) +time.sleep(2) +res = send_queries(knot, zones[1].name) +check_result("global, zone 2, all slip", res, 5, 1) + +# 50% slip +reconfigure(knot, None, 5, 2) +res = send_queries(knot, zones[0].name) +check_result("global, zone 1, 50% slip", res, 5, 2) +cmp_stats(knot, res) +time.sleep(2) +res = send_queries(knot, zones[1].name) +check_result("global, zone 2, 50% slip", res, 5, 2) + +# Whitelist +reconfigure(knot, None, 5, 0, whitelist=knot.addr) +res = send_queries(knot, zones[0].name) +cmp_stats(knot, res) +check_result("global, zone 1, whitelist", res, 0) +time.sleep(2) +res = send_queries(knot, zones[1].name) +check_result("global, zone 2, whitelist", res, 0) + +### RRL zone module + +# All drop +reconfigure(knot, zones[0], 5, 0) +res = send_queries(knot, zones[0].name) +check_result("zone 1, all drop", res, 5, 0) +cmp_stats(knot, res, zones[0].name) +time.sleep(2) +res = send_queries(knot, zones[1].name) +check_result("zone 2, disabled", res) + +# All slip +reconfigure(knot, zones[0], 5, 1) +res = send_queries(knot, zones[0].name) +check_result("zone 1, all slip", res, 5, 1) +cmp_stats(knot, res, zones[0].name) +time.sleep(2) +res = send_queries(knot, zones[1].name) +check_result("zone 2, disabled", res) + +# 50% slip +reconfigure(knot, zones[0], 5, 2) +res = send_queries(knot, zones[0].name) +check_result("zone 1, 50% slip", res, 5, 2) +cmp_stats(knot, res, zones[0].name) +time.sleep(2) +res = send_queries(knot, zones[1].name) +check_result("zone 2, disabled", res) + +# Whitelist +reconfigure(knot, zones[0], 5, 0, whitelist=knot.addr) +res = send_queries(knot, zones[0].name) +check_result("zone 1, whitelist", res, 0) +cmp_stats(knot, res, zones[0].name) +time.sleep(2) +res = send_queries(knot, zones[1].name) +check_result("zone 2, disabled", res) + +t.end() diff --git a/tests-extra/tools/dnstest/module.py b/tests-extra/tools/dnstest/module.py index 744ed25f1fbf1b5052d9cbff251fedc37347fd42..4673e485918758c2ac155e7c903682e7931c9531 100644 --- a/tests-extra/tools/dnstest/module.py +++ b/tests-extra/tools/dnstest/module.py @@ -109,6 +109,36 @@ class ModDnstap(KnotModule): return conf +class ModRRL(KnotModule): + '''RRL module''' + + src_name = "rrl_load" + conf_name = "mod-rrl" + + def __init__(self, rate_limit, slip=None, table_size=None, whitelist=None): + super().__init__() + self.rate_limit = rate_limit + self.slip = slip + self.table_size = table_size + self.whitelist = whitelist + + def get_conf(self, conf=None): + if not conf: + conf = dnstest.config.KnotConf() + + conf.begin(self.conf_name) + conf.id_item("id", self.conf_id) + conf.item_str("rate-limit", self.rate_limit) + if self.slip or self.slip == 0: + conf.item_str("slip", self.slip) + if self.table_size: + conf.item_str("table-size", self.table_size) + if self.whitelist: + conf.item_str("whitelist", self.whitelist) + conf.end() + + return conf + class ModDnsproxy(KnotModule): '''Dnsproxy module''' diff --git a/tests-extra/tools/dnstest/server.py b/tests-extra/tools/dnstest/server.py index a84975c365ec4c775aec306fe121f75a7dd1fbee..57e9188ff800598fc8fbb77d6cd55c5d23c07909 100644 --- a/tests-extra/tools/dnstest/server.py +++ b/tests-extra/tools/dnstest/server.py @@ -64,6 +64,9 @@ class Zone(object): def add_module(self, module): self.modules.append(module) + def clear_modules(self): + self.modules.clear() + def disable_master(self, new_zone_file): self.zfile.remove() self.zfile = new_zone_file @@ -661,6 +664,14 @@ class Server(object): else: self.modules.append(module) + def clear_modules(self, zone): + zone = zone_arg_check(zone) + + if zone: + self.zones[zone.name].clear_modules() + else: + self.modules.clear() + def clean(self, zone=True, timers=True): if zone: zone = zone_arg_check(zone) diff --git a/tests/.gitignore b/tests/.gitignore index 3d3af24a514ba319b52079411f0240a3f9b3f121..49a3af1bb3c6f4286ed4a81ff11fcf510fd1d403 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -46,12 +46,12 @@ /fdset /journal /modules/online_sign +/modules/rrl /node /process_answer /process_query /query_module /requestor -/rrl /semantic_check /server /utils/test_cert diff --git a/tests/Makefile.am b/tests/Makefile.am index 5e08bdb9e44a3c7955d088c7a5d30eec26586aa9..a3bc0277d3fe3e4155ae88680b15901d4f07e9de 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -49,6 +49,7 @@ check_PROGRAMS += \ check_PROGRAMS += \ modules/online_sign \ + modules/rrl \ utils/test_cert \ utils/test_lookup \ acl \ @@ -65,7 +66,6 @@ check_PROGRAMS += \ process_query \ query_module \ requestor \ - rrl \ server \ worker_pool \ worker_queue \ diff --git a/tests/confio.c b/tests/confio.c index 175765e6a38e81a23b72586aa50c5b6ec9581c61..e4f0b8e4cfb4bbbdaf4f1153fae126431f4fe9d1 100644 --- a/tests/confio.c +++ b/tests/confio.c @@ -365,7 +365,7 @@ static void test_conf_io_set(void) KNOT_YP_EINVAL_ITEM, "set unknown key1"); ok(conf_io_set("include", NULL, NULL, NULL) == KNOT_YP_ENODATA, "set non-group without data"); - ok(conf_io_set("server", "rate-limit", NULL, "x") == + ok(conf_io_set("server", "background-workers", NULL, "x") == KNOT_EINVAL, "set invalid data"); // ERR callback @@ -459,7 +459,7 @@ static void test_conf_io_unset(void) KNOT_YP_EINVAL_ITEM, "unset unknown key1"); ok(conf_io_unset("include", NULL, NULL, "file") == KNOT_ENOTSUP, "unset non-group item"); - ok(conf_io_unset("server", "rate-limit", NULL, "x") == + ok(conf_io_unset("server", "background-workers", NULL, "x") == KNOT_EINVAL, "unset invalid data"); // Single group, single value. @@ -885,7 +885,7 @@ static void test_conf_io_list(void) ok(conf_io_list("server", &io) == KNOT_EOK, "list group"); ref = "server.version\n" - "server.rate-limit\n" + "server.background-workers\n" "server.listen\n" "server.tcp-handshake-timeout\n" "server.tcp-idle-timeout\n" @@ -893,14 +893,13 @@ static void test_conf_io_list(void) "server.max-tcp-clients\n" "server.max-udp-payload\n" "server.max-ipv4-udp-payload\n" - "server.max-ipv6-udp-payload\n" - "server.rate-limit-slip"; + "server.max-ipv6-udp-payload"; ok(strcmp(ref, out) == 0, "compare result"); } static const yp_item_t desc_server[] = { { C_VERSION, YP_TSTR, YP_VNONE }, - { C_RATE_LIMIT, YP_TINT, YP_VNONE }, + { C_BG_WORKERS, YP_TINT, YP_VNONE }, { C_LISTEN, YP_TADDR, YP_VNONE, YP_FMULTI }, // Required config cache items - assert fix. { C_TCP_HSHAKE_TIMEOUT, YP_TINT, YP_VNONE }, @@ -910,7 +909,6 @@ static const yp_item_t desc_server[] = { { C_MAX_UDP_PAYLOAD, YP_TINT, YP_VNONE }, { C_MAX_IPV4_UDP_PAYLOAD, YP_TINT, YP_VNONE }, { C_MAX_IPV6_UDP_PAYLOAD, YP_TINT, YP_VNONE }, - { C_RATE_LIMIT_SLIP, YP_TINT, YP_VNONE }, { NULL } }; diff --git a/tests/rrl.c b/tests/modules/rrl.c similarity index 91% rename from tests/rrl.c rename to tests/modules/rrl.c index e96d7b9504fec7cca14f0e69b891678300621ff0..c128d51e186fc53970241c8eaeaf4366cdaa459e 100644 --- a/tests/rrl.c +++ b/tests/modules/rrl.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,17 +14,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <sys/types.h> -#include <sys/socket.h> #include <tap/basic.h> #include "dnssec/crypto.h" #include "dnssec/random.h" -#include "knot/conf/conf.h" -#include "knot/server/rrl.h" -#include "knot/zone/zone.h" -#include "libknot/descriptor.h" +#include "libknot/libknot.h" #include "contrib/sockaddr.h" +#include "knot/modules/rrl/functions.h" /* Enable time-dependent tests. */ //#define ENABLE_TIMED_TESTS @@ -50,12 +46,12 @@ struct runnable_data { rrl_table_t *rrl; struct sockaddr_storage *addr; rrl_req_t *rq; - zone_t *zone; + knot_dname_t *zone; }; static void* rrl_runnable(void *arg) { - struct runnable_data* d = (struct runnable_data*)arg; + struct runnable_data *d = (struct runnable_data *)arg; struct sockaddr_storage addr; memcpy(&addr, d->addr, sizeof(struct sockaddr_storage)); int lock = -1; @@ -143,9 +139,7 @@ int main(int argc, char *argv[]) is_int(KNOT_EOK, ret, "rrl: setlocks"); /* 4. N unlimited requests. */ - knot_dname_t *zone_name = knot_dname_from_str_alloc("rrl."); - zone_t *zone = zone_new(zone_name); - knot_dname_free(&zone_name, NULL); + knot_dname_t *zone = knot_dname_from_str_alloc("rrl."); struct sockaddr_storage addr; struct sockaddr_storage addr6; @@ -199,7 +193,7 @@ int main(int argc, char *argv[]) ok(rd.passed, "rrl: hashtable is ~ consistent"); #endif - zone_free(&zone); + knot_dname_free(&zone, NULL); knot_pkt_free(&query); rrl_destroy(rrl); dnssec_crypto_cleanup();