diff options
Diffstat (limited to 'lib/dns/include/dns/rrl.h')
-rw-r--r-- | lib/dns/include/dns/rrl.h | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/lib/dns/include/dns/rrl.h b/lib/dns/include/dns/rrl.h new file mode 100644 index 0000000..bdffed9 --- /dev/null +++ b/lib/dns/include/dns/rrl.h @@ -0,0 +1,269 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/* + * Rate limit DNS responses. + */ + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/lang.h> + +#include <dns/fixedname.h> +#include <dns/rdata.h> +#include <dns/types.h> + +ISC_LANG_BEGINDECLS + +/* + * Memory allocation or other failures. + */ +#define DNS_RRL_LOG_FAIL ISC_LOG_WARNING +/* + * dropped or slipped responses. + */ +#define DNS_RRL_LOG_DROP ISC_LOG_INFO +/* + * Major events in dropping or slipping. + */ +#define DNS_RRL_LOG_DEBUG1 ISC_LOG_DEBUG(3) +/* + * Limit computations. + */ +#define DNS_RRL_LOG_DEBUG2 ISC_LOG_DEBUG(4) +/* + * Even less interesting. + */ +#define DNS_RRL_LOG_DEBUG3 ISC_LOG_DEBUG(9) + +#define DNS_RRL_LOG_ERR_LEN 64 +#define DNS_RRL_LOG_BUF_LEN \ + (sizeof("would continue limiting") + DNS_RRL_LOG_ERR_LEN + \ + sizeof(" responses to ") + ISC_NETADDR_FORMATSIZE + \ + sizeof("/128 for IN ") + DNS_RDATATYPE_FORMATSIZE + \ + DNS_NAME_FORMATSIZE) + +typedef struct dns_rrl_hash dns_rrl_hash_t; + +/* + * Response types. + */ +typedef enum { + DNS_RRL_RTYPE_FREE = 0, + DNS_RRL_RTYPE_QUERY, + DNS_RRL_RTYPE_REFERRAL, + DNS_RRL_RTYPE_NODATA, + DNS_RRL_RTYPE_NXDOMAIN, + DNS_RRL_RTYPE_ERROR, + DNS_RRL_RTYPE_ALL, + DNS_RRL_RTYPE_TCP, +} dns_rrl_rtype_t; + +/* + * A rate limit bucket key. + * This should be small to limit the total size of the database. + * The hash of the qname should be wide enough to make the probability + * of collisions among requests from a single IP address block less than 50%. + * We need a 32-bit hash value for 10000 qps (e.g. random qnames forged + * by attacker) to collide with legitimate qnames from the target with + * probability at most 1%. + */ +#define DNS_RRL_MAX_PREFIX 64 +typedef union dns_rrl_key dns_rrl_key_t; +struct dns__rrl_key { + uint32_t ip[DNS_RRL_MAX_PREFIX / 32]; + uint32_t qname_hash; + dns_rdatatype_t qtype; + uint8_t qclass; + unsigned int rtype : 4; /* dns_rrl_rtype_t */ + unsigned int ipv6 : 1; +}; +union dns_rrl_key { + struct dns__rrl_key s; + uint16_t w[sizeof(struct dns__rrl_key) / sizeof(uint16_t)]; +}; + +/* + * A rate-limit entry. + * This should be small to limit the total size of the table of entries. + */ +typedef struct dns_rrl_entry dns_rrl_entry_t; +typedef ISC_LIST(dns_rrl_entry_t) dns_rrl_bin_t; +struct dns_rrl_entry { + ISC_LINK(dns_rrl_entry_t) lru; + ISC_LINK(dns_rrl_entry_t) hlink; + dns_rrl_key_t key; +#define DNS_RRL_RESPONSE_BITS 24 + signed int responses : DNS_RRL_RESPONSE_BITS; +#define DNS_RRL_QNAMES_BITS 8 + unsigned int log_qname : DNS_RRL_QNAMES_BITS; + +#define DNS_RRL_TS_GEN_BITS 2 + unsigned int ts_gen : DNS_RRL_TS_GEN_BITS; + unsigned int ts_valid : 1; +#define DNS_RRL_HASH_GEN_BITS 1 + unsigned int hash_gen : DNS_RRL_HASH_GEN_BITS; + unsigned int logged : 1; +#define DNS_RRL_LOG_BITS 11 + unsigned int log_secs : DNS_RRL_LOG_BITS; + +#define DNS_RRL_TS_BITS 12 + unsigned int ts : DNS_RRL_TS_BITS; + +#define DNS_RRL_MAX_SLIP 10 + unsigned int slip_cnt : 4; +}; + +#define DNS_RRL_MAX_TIME_TRAVEL 5 +#define DNS_RRL_FOREVER (1 << DNS_RRL_TS_BITS) +#define DNS_RRL_MAX_TS (DNS_RRL_FOREVER - 1) + +#define DNS_RRL_MAX_RESPONSES ((1 << (DNS_RRL_RESPONSE_BITS - 1)) - 1) +#define DNS_RRL_MAX_WINDOW 3600 +#if DNS_RRL_MAX_WINDOW >= DNS_RRL_MAX_TS +#error "DNS_RRL_MAX_WINDOW is too large" +#endif /* if DNS_RRL_MAX_WINDOW >= DNS_RRL_MAX_TS */ +#define DNS_RRL_MAX_RATE 1000 +#if DNS_RRL_MAX_RATE >= (DNS_RRL_MAX_RESPONSES / DNS_RRL_MAX_WINDOW) +#error "DNS_RRL_MAX_rate is too large" +#endif /* if DNS_RRL_MAX_RATE >= (DNS_RRL_MAX_RESPONSES / DNS_RRL_MAX_WINDOW) \ + */ + +#if (1 << DNS_RRL_LOG_BITS) >= DNS_RRL_FOREVER +#error DNS_RRL_LOG_BITS is too big +#endif /* if (1 << DNS_RRL_LOG_BITS) >= DNS_RRL_FOREVER */ +#define DNS_RRL_MAX_LOG_SECS 1800 +#if DNS_RRL_MAX_LOG_SECS >= (1 << DNS_RRL_LOG_BITS) +#error "DNS_RRL_MAX_LOG_SECS is too large" +#endif /* if DNS_RRL_MAX_LOG_SECS >= (1 << DNS_RRL_LOG_BITS) */ +#define DNS_RRL_STOP_LOG_SECS 60 +#if DNS_RRL_STOP_LOG_SECS >= (1 << DNS_RRL_LOG_BITS) +#error "DNS_RRL_STOP_LOG_SECS is too large" +#endif /* if DNS_RRL_STOP_LOG_SECS >= (1 << DNS_RRL_LOG_BITS) */ + +/* + * A hash table of rate-limit entries. + */ +struct dns_rrl_hash { + isc_stdtime_t check_time; + unsigned int gen : DNS_RRL_HASH_GEN_BITS; + int length; + dns_rrl_bin_t bins[1]; +}; + +/* + * A block of rate-limit entries. + */ +typedef struct dns_rrl_block dns_rrl_block_t; +struct dns_rrl_block { + ISC_LINK(dns_rrl_block_t) link; + int size; + dns_rrl_entry_t entries[1]; +}; + +/* + * A rate limited qname buffer. + */ +typedef struct dns_rrl_qname_buf dns_rrl_qname_buf_t; +struct dns_rrl_qname_buf { + ISC_LINK(dns_rrl_qname_buf_t) link; + const dns_rrl_entry_t *e; + unsigned int index; + 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. + */ +typedef struct dns_rrl dns_rrl_t; +struct dns_rrl { + isc_mutex_t lock; + isc_mem_t *mctx; + + bool log_only; + dns_rrl_rate_t responses_per_second; + dns_rrl_rate_t referrals_per_second; + dns_rrl_rate_t nodata_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; + double qps_scale; + int max_entries; + + dns_acl_t *exempt; + + int num_entries; + + int qps_responses; + isc_stdtime_t qps_time; + double qps; + + unsigned int probes; + unsigned int searches; + + ISC_LIST(dns_rrl_block_t) blocks; + ISC_LIST(dns_rrl_entry_t) lru; + + dns_rrl_hash_t *hash; + dns_rrl_hash_t *old_hash; + unsigned int hash_gen; + + unsigned int ts_gen; +#define DNS_RRL_TS_BASES (1 << DNS_RRL_TS_GEN_BITS) + isc_stdtime_t ts_bases[DNS_RRL_TS_BASES]; + + int ipv4_prefixlen; + uint32_t ipv4_mask; + int ipv6_prefixlen; + uint32_t ipv6_mask[4]; + + isc_stdtime_t log_stops_time; + dns_rrl_entry_t *last_logged; + int num_logged; + int num_qnames; + ISC_LIST(dns_rrl_qname_buf_t) qname_free; +#define DNS_RRL_QNAMES (1 << DNS_RRL_QNAMES_BITS) + dns_rrl_qname_buf_t *qnames[DNS_RRL_QNAMES]; +}; + +typedef enum { + DNS_RRL_RESULT_OK, + DNS_RRL_RESULT_DROP, + DNS_RRL_RESULT_SLIP, +} dns_rrl_result_t; + +dns_rrl_result_t +dns_rrl(dns_view_t *view, dns_zone_t *zone, const isc_sockaddr_t *client_addr, + bool is_tcp, dns_rdataclass_t rdclass, dns_rdatatype_t qtype, + const dns_name_t *qname, isc_result_t resp_result, isc_stdtime_t now, + bool wouldlog, char *log_buf, unsigned int log_buf_len); + +void +dns_rrl_view_destroy(dns_view_t *view); + +isc_result_t +dns_rrl_init(dns_rrl_t **rrlp, dns_view_t *view, int min_entries); + +ISC_LANG_ENDDECLS |