diff options
Diffstat (limited to '')
-rw-r--r-- | lib/selection.h | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/lib/selection.h b/lib/selection.h new file mode 100644 index 0000000..34cc69c --- /dev/null +++ b/lib/selection.h @@ -0,0 +1,269 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz> + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +/** + * @file selection.h + * Provides server selection API (see `kr_server_selection`) + * and functions common to both implementations. + */ + +#include "lib/cache/api.h" + +/* After KR_NS_TIMEOUT_ROW_DEAD consecutive timeouts + * where at least one was over KR_NS_TIMEOUT_MIN_DEAD_TIMEOUT ms, + * we consider the upstream IP dead for KR_NS_TIMEOUT_RETRY_INTERVAL ms */ +#define KR_NS_TIMEOUT_ROW_DEAD 4 +#define KR_NS_TIMEOUT_MIN_DEAD_TIMEOUT 800 /* == DEFAULT_TIMEOUT * 2 */ +#define KR_NS_TIMEOUT_RETRY_INTERVAL 1000 + +/** + * These errors are to be reported as feedback to server selection. + * See `kr_server_selection::error` for more details. + */ +enum kr_selection_error { + KR_SELECTION_OK = 0, + + // Network errors + KR_SELECTION_QUERY_TIMEOUT, + KR_SELECTION_TLS_HANDSHAKE_FAILED, + KR_SELECTION_TCP_CONNECT_FAILED, + KR_SELECTION_TCP_CONNECT_TIMEOUT, + + // RCODEs + KR_SELECTION_REFUSED, + KR_SELECTION_SERVFAIL, + KR_SELECTION_FORMERR, /// inside an answer without an OPT record + KR_SELECTION_FORMERR_EDNS, /// with an OPT record + KR_SELECTION_NOTIMPL, + KR_SELECTION_OTHER_RCODE, + + // DNS errors + KR_SELECTION_MALFORMED, + /** Name or type mismatch. */ + KR_SELECTION_MISMATCHED, + KR_SELECTION_TRUNCATED, + KR_SELECTION_DNSSEC_ERROR, + KR_SELECTION_LAME_DELEGATION, + /** Too long chain, or a cycle. */ + KR_SELECTION_BAD_CNAME, + + /** Leave this last, as it is used as array size. */ + KR_SELECTION_NUMBER_OF_ERRORS +}; + +enum kr_transport_protocol { + /** Selected name with no IPv4 address, it has to be resolved first. */ + KR_TRANSPORT_RESOLVE_A, + /** Selected name with no IPv6 address, it has to be resolved first. */ + KR_TRANSPORT_RESOLVE_AAAA, + KR_TRANSPORT_UDP, + KR_TRANSPORT_TCP, + KR_TRANSPORT_TLS, +}; + +/** + * Output of the selection algorithm. + */ +struct kr_transport { + knot_dname_t *ns_name; /**< Set to "." for forwarding targets.*/ + union kr_sockaddr address; + size_t address_len; + enum kr_transport_protocol protocol; + unsigned timeout; /**< Timeout in ms to be set for UDP transmission. */ + /** Timeout was capped to a maximum value based on the other candidates + * when choosing this transport. The timeout therefore can be much lower + * than what we expect it to be. We basically probe the server for a sudden + * network change but we expect it to timeout in most cases. We have to keep + * this in mind when noting the timeout in cache. */ + bool timeout_capped; + /** True iff transport was set in worker.c:subreq_finalize, + * that means it may be different from the one originally chosen one.*/ + bool deduplicated; +}; + +struct local_state { + int timeouts; /**< Number of timeouts that occurred resolving this query.*/ + bool truncated; /**< Query was truncated, switch to TCP. */ + /** Force resolution of a new NS name (if possible) + * Done by selection.c:error in some cases. */ + bool force_resolve; + /** Used to work around auths with broken TCP. */ + bool force_udp; + void *private; /**< Inner state of the implementation.*/ +}; + +/** + * Specifies a API for selecting transports and giving feedback on the choices. + * + * The function pointers are to be used throughout resolver when some information about + * the transport is obtained. E.g. RTT in `worker.c` or RCODE in `iterate.c`,… + */ +struct kr_server_selection { + bool initialized; + /** + * Puts a pointer to next transport of @p qry to @p transport . + * + * Allocates new kr_transport in request's mempool, chooses transport to be used for this query. + * Selection may fail, so @p transport can be set to NULL. + * + * @param transport to be filled with pointer to the chosen transport or NULL on failure + */ + void (*choose_transport)(struct kr_query *qry, + struct kr_transport **transport); + /** Report back the RTT of network operation for transport in ms. */ + void (*update_rtt)(struct kr_query *qry, + const struct kr_transport *transport, unsigned rtt); + /** Report back error encountered with the chosen transport. See `enum kr_selection` */ + void (*error)(struct kr_query *qry, + const struct kr_transport *transport, + enum kr_selection_error error); + + struct local_state *local_state; +}; + +/** + * @brief Initialize the server selection API for @p qry. + * + * The implementation is to be chosen based on qry->flags. + */ +KR_EXPORT +void kr_server_selection_init(struct kr_query *qry); + +/** + * @brief Add forwarding target to request. + * + * This is exposed to Lua in order to add forwarding targets to request. + * These are then shared by all the queries in said request. + */ +KR_EXPORT +int kr_forward_add_target(struct kr_request *req, const struct sockaddr *sock); + + + + + +/* Below are internal parts shared by ./selection_{forward,iter}.c */ + +/** + * To be held per IP address in the global LMDB cache + */ +struct rtt_state { + int32_t srtt; /**< Smoothed RTT, i.e. an estimate of round-trip time. */ + int32_t variance; /**< An estimate of RTT's standard derivation (not variance). */ + /** Note: some TCP and TLS failures are also considered as timeouts. */ + int32_t consecutive_timeouts; + /** Timestamp of pronouncing this IP bad based on KR_NS_TIMEOUT_ROW_DEAD */ + uint64_t dead_since; +}; + +/** + * @brief To be held per IP address and locally "inside" query. + */ +struct address_state { + /** Used to distinguish old and valid records in local_state; -1 means unusable IP. */ + unsigned int generation; + struct rtt_state rtt_state; + knot_dname_t *ns_name; + bool tls_capable : 1; + /* TODO: uncomment these once we actually use this information in selection + bool tcp_waiting : 1; + bool tcp_connected : 1; + */ + int choice_array_index; + int error_count; + bool broken; + int errors[KR_SELECTION_NUMBER_OF_ERRORS]; +}; + +/** + * @brief Array of these is one of inputs for the actual selection algorithm (`select_transport`) + */ +struct choice { + union kr_sockaddr address; + size_t address_len; + struct address_state *address_state; + /** used to overwrite the port number; + * if zero, `select_transport` determines it. */ + uint16_t port; +}; + +/** + * @brief Array of these is description of names to be resolved (i.e. name without some address) + */ +struct to_resolve { + knot_dname_t *name; + /** Either KR_TRANSPORT_RESOLVE_A or KR_TRANSPORT_RESOLVE_AAAA is valid here. */ + enum kr_transport_protocol type; +}; + +/** + * @brief Based on passed choices, choose the next transport. + * + * Common function to both implementations (iteration and forwarding). + * The `*_choose_transport` functions from `selection_*.h` preprocess the input for this one. + * + * @param choices Options to choose from, see struct above + * @param unresolved Array of names that can be resolved (i.e. no A/AAAA record) + * @param timeouts Number of timeouts that occurred in this query (used for exponential backoff) + * @param mempool Memory context of current request + * @param tcp Force TCP as transport protocol + * @param[out] choice_index Optionally index of the chosen transport in the @p choices array. + * @return Chosen transport (on mempool) or NULL when no choice is viable + */ +struct kr_transport *select_transport(const struct choice choices[], int choices_len, + const struct to_resolve unresolved[], + int unresolved_len, int timeouts, + struct knot_mm *mempool, bool tcp, + size_t *choice_index); + +/** + * Common part of RTT feedback mechanism. Notes RTT to global cache. + */ +void update_rtt(struct kr_query *qry, struct address_state *addr_state, + const struct kr_transport *transport, unsigned rtt); + +/** + * Common part of error feedback mechanism. + */ +void error(struct kr_query *qry, struct address_state *addr_state, + const struct kr_transport *transport, + enum kr_selection_error sel_error); + +/** + * Get RTT state from cache. Returns `default_rtt_state` on unknown addresses. + * + * Note that this opens a cache transaction which is usually closed by calling + * `put_rtt_state`, i.e. callee is responsible for its closing + * (e.g. calling kr_cache_commit). + */ +struct rtt_state get_rtt_state(const uint8_t *ip, size_t len, + struct kr_cache *cache); + +int put_rtt_state(const uint8_t *ip, size_t len, struct rtt_state state, + struct kr_cache *cache); + +/** + * @internal Helper function for conversion between different IP representations. + */ +void bytes_to_ip(uint8_t *bytes, size_t len, uint16_t port, union kr_sockaddr *dst); + +/** + * @internal Helper function for conversion between different IP representations. + */ +uint8_t *ip_to_bytes(const union kr_sockaddr *src, size_t len); + +/** + * @internal Fetch per-address information from various sources. + * + * Note that this opens a RO cache transaction; the callee is responsible + * for its closing not too long afterwards (e.g. calling kr_cache_commit). + */ +void update_address_state(struct address_state *state, union kr_sockaddr *address, + size_t address_len, struct kr_query *qry); + +/** @internal Return whether IPv6 is considered to be broken. */ +bool no6_is_bad(void); + |