summaryrefslogtreecommitdiffstats
path: root/src/resolvers.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/resolvers.c236
1 files changed, 197 insertions, 39 deletions
diff --git a/src/resolvers.c b/src/resolvers.c
index 3275cd2..47b0cce 100644
--- a/src/resolvers.c
+++ b/src/resolvers.c
@@ -28,6 +28,7 @@
#include <haproxy/check.h>
#include <haproxy/cli.h>
#include <haproxy/dns.h>
+#include <haproxy/dns_ring.h>
#include <haproxy/errors.h>
#include <haproxy/fd.h>
#include <haproxy/http_rules.h>
@@ -36,7 +37,6 @@
#include <haproxy/protocol.h>
#include <haproxy/proxy.h>
#include <haproxy/resolvers.h>
-#include <haproxy/ring.h>
#include <haproxy/sample.h>
#include <haproxy/sc_strm.h>
#include <haproxy/server.h>
@@ -50,6 +50,10 @@
#include <haproxy/vars.h>
#include <haproxy/xxhash.h>
+#if defined(USE_PROMEX)
+#include <promex/promex.h>
+#endif
+
struct list sec_resolvers = LIST_HEAD_INIT(sec_resolvers);
struct list resolv_srvrq_list = LIST_HEAD_INIT(resolv_srvrq_list);
@@ -92,7 +96,7 @@ enum {
RSLV_STAT_END,
};
-static struct name_desc resolv_stats[] = {
+static struct stat_col resolv_stats[] = {
[RSLV_STAT_ID] = { .name = "id", .desc = "ID" },
[RSLV_STAT_PID] = { .name = "pid", .desc = "Parent ID" },
[RSLV_STAT_SENT] = { .name = "sent", .desc = "Sent" },
@@ -114,26 +118,79 @@ static struct name_desc resolv_stats[] = {
static struct dns_counters dns_counters;
-static void resolv_fill_stats(void *d, struct field *stats)
+static int resolv_fill_stats(void *d, struct field *stats, unsigned int *selected_field)
{
struct dns_counters *counters = d;
- stats[RSLV_STAT_ID] = mkf_str(FO_CONFIG, counters->id);
- stats[RSLV_STAT_PID] = mkf_str(FO_CONFIG, counters->pid);
- stats[RSLV_STAT_SENT] = mkf_u64(FN_GAUGE, counters->sent);
- stats[RSLV_STAT_SND_ERROR] = mkf_u64(FN_GAUGE, counters->snd_error);
- stats[RSLV_STAT_VALID] = mkf_u64(FN_GAUGE, counters->app.resolver.valid);
- stats[RSLV_STAT_UPDATE] = mkf_u64(FN_GAUGE, counters->app.resolver.update);
- stats[RSLV_STAT_CNAME] = mkf_u64(FN_GAUGE, counters->app.resolver.cname);
- stats[RSLV_STAT_CNAME_ERROR] = mkf_u64(FN_GAUGE, counters->app.resolver.cname_error);
- stats[RSLV_STAT_ANY_ERR] = mkf_u64(FN_GAUGE, counters->app.resolver.any_err);
- stats[RSLV_STAT_NX] = mkf_u64(FN_GAUGE, counters->app.resolver.nx);
- stats[RSLV_STAT_TIMEOUT] = mkf_u64(FN_GAUGE, counters->app.resolver.timeout);
- stats[RSLV_STAT_REFUSED] = mkf_u64(FN_GAUGE, counters->app.resolver.refused);
- stats[RSLV_STAT_OTHER] = mkf_u64(FN_GAUGE, counters->app.resolver.other);
- stats[RSLV_STAT_INVALID] = mkf_u64(FN_GAUGE, counters->app.resolver.invalid);
- stats[RSLV_STAT_TOO_BIG] = mkf_u64(FN_GAUGE, counters->app.resolver.too_big);
- stats[RSLV_STAT_TRUNCATED] = mkf_u64(FN_GAUGE, counters->app.resolver.truncated);
- stats[RSLV_STAT_OUTDATED] = mkf_u64(FN_GAUGE, counters->app.resolver.outdated);
+ unsigned int current_field = (selected_field != NULL ? *selected_field : 0);
+
+ for (; current_field < RSLV_STAT_END; current_field++) {
+ struct field metric = { 0 };
+
+ switch (current_field) {
+ case RSLV_STAT_ID:
+ metric = mkf_str(FO_CONFIG, counters->id);
+ break;
+ case RSLV_STAT_PID:
+ metric = mkf_str(FO_CONFIG, counters->pid);
+ break;
+ case RSLV_STAT_SENT:
+ metric = mkf_u64(FN_GAUGE, counters->sent);
+ break;
+ case RSLV_STAT_SND_ERROR:
+ metric = mkf_u64(FN_GAUGE, counters->snd_error);
+ break;
+ case RSLV_STAT_VALID:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.valid);
+ break;
+ case RSLV_STAT_UPDATE:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.update);
+ break;
+ case RSLV_STAT_CNAME:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.cname);
+ break;
+ case RSLV_STAT_CNAME_ERROR:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.cname_error);
+ break;
+ case RSLV_STAT_ANY_ERR:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.any_err);
+ break;
+ case RSLV_STAT_NX:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.nx);
+ break;
+ case RSLV_STAT_TIMEOUT:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.timeout);
+ break;
+ case RSLV_STAT_REFUSED:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.refused);
+ break;
+ case RSLV_STAT_OTHER:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.other);
+ break;
+ case RSLV_STAT_INVALID:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.invalid);
+ break;
+ case RSLV_STAT_TOO_BIG:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.too_big);
+ break;
+ case RSLV_STAT_TRUNCATED:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.truncated);
+ break;
+ case RSLV_STAT_OUTDATED:
+ metric = mkf_u64(FN_GAUGE, counters->app.resolver.outdated);
+ break;
+ default:
+ /* not used for frontends. If a specific metric
+ * is requested, return an error. Otherwise continue.
+ */
+ if (selected_field != NULL)
+ return 0;
+ continue;
+ }
+ stats[current_field] = metric;
+ if (selected_field != NULL)
+ break;
+ }
+ return 1;
}
static struct stats_module rslv_stats_module = {
@@ -170,6 +227,20 @@ struct resolvers *find_resolvers_by_id(const char *id)
return NULL;
}
+/* Returns a pointer to the nameserver matching numerical <id> within <parent>
+ * resolver section. NULL is returned if no match is found.
+ */
+struct dns_nameserver *find_nameserver_by_resolvers_and_id(struct resolvers *parent, unsigned int id)
+{
+ struct dns_nameserver *ns;
+
+ list_for_each_entry(ns, &parent->nameservers, list) {
+ if (ns->puid == id)
+ return ns;
+ }
+ return NULL;
+}
+
/* Returns a pointer on the SRV request matching the name <name> for the proxy
* <px>. NULL is returned if no match is found.
*/
@@ -645,14 +716,17 @@ static void leave_resolver_code()
*/
static void resolv_srvrq_cleanup_srv(struct server *srv)
{
+ struct server_inetaddr srv_addr;
+
_resolv_unlink_resolution(srv->resolv_requester);
HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
- srvrq_update_srv_status(srv, 1);
+ srvrq_set_srv_down(srv);
ha_free(&srv->hostname);
ha_free(&srv->hostname_dn);
srv->hostname_dn_len = 0;
- memset(&srv->addr, 0, sizeof(srv->addr));
- srv->svc_port = 0;
+ memset(&srv_addr, 0, sizeof(srv_addr));
+ /* unset server's addr AND port */
+ server_set_inetaddr(srv, &srv_addr, SERVER_INETADDR_UPDATER_NONE, NULL);
srv->flags |= SRV_F_NO_RESOLUTION;
ebpt_delete(&srv->host_dn);
@@ -815,12 +889,16 @@ static void resolv_check_response(struct resolv_resolution *res)
srv_found:
/* And update this server, if found (srv is locked here) */
if (srv) {
+ struct server_inetaddr srv_addr;
+ uint8_t ip_change = 0;
+
/* re-enable DNS resolution for this server by default */
srv->flags &= ~SRV_F_NO_RESOLUTION;
srv->srvrq_check->expire = TICK_ETERNITY;
- srv->svc_port = item->port;
- srv->flags &= ~SRV_F_MAPPORTS;
+ server_get_inetaddr(srv, &srv_addr);
+ srv_addr.port.svc = item->port;
+ srv_addr.port.map = 0;
/* Check if an Additional Record is associated to this SRV record.
* Perform some sanity checks too to ensure the record can be used.
@@ -833,10 +911,12 @@ srv_found:
switch (item->ar_item->type) {
case DNS_RTYPE_A:
- srv_update_addr(srv, &item->ar_item->data.in4.sin_addr, AF_INET, "DNS additional record");
+ srv_addr.family = AF_INET;
+ srv_addr.addr.v4 = item->ar_item->data.in4.sin_addr;
break;
case DNS_RTYPE_AAAA:
- srv_update_addr(srv, &item->ar_item->data.in6.sin6_addr, AF_INET6, "DNS additional record");
+ srv_addr.family = AF_INET6;
+ srv_addr.addr.v6 = item->ar_item->data.in6.sin6_addr;
break;
}
@@ -846,8 +926,15 @@ srv_found:
* It is usless to perform an extra resolution
*/
_resolv_unlink_resolution(srv->resolv_requester);
+
+ ip_change = 1;
}
+ if (ip_change)
+ server_set_inetaddr_warn(srv, &srv_addr, SERVER_INETADDR_UPDATER_DNS_AR);
+ else
+ server_set_inetaddr(srv, &srv_addr, SERVER_INETADDR_UPDATER_NONE, NULL);
+
if (!srv->hostname_dn) {
const char *msg = NULL;
char hostname[DNS_MAX_NAME_SIZE+1];
@@ -873,9 +960,6 @@ srv_found:
resolv_link_resolution(srv, OBJ_TYPE_SERVER, 1);
}
- /* Update the server status */
- srvrq_update_srv_status(srv, (srv->addr.ss_family != AF_INET && srv->addr.ss_family != AF_INET6));
-
if (!srv->resolv_opts.ignore_weight) {
char weight[9];
int ha_weight;
@@ -2487,11 +2571,11 @@ static void resolvers_destroy(struct resolvers *resolvers)
fd_delete(ns->dgram->conn.t.sock.fd);
close(ns->dgram->conn.t.sock.fd);
}
- ring_free(ns->dgram->ring_req);
+ dns_ring_free(ns->dgram->ring_req);
free(ns->dgram);
}
if (ns->stream) {
- ring_free(ns->stream->ring_req);
+ dns_ring_free(ns->stream->ring_req);
task_destroy(ns->stream->task_req);
task_destroy(ns->stream->task_rsp);
free(ns->stream);
@@ -2684,14 +2768,15 @@ static int stats_dump_resolv_to_buffer(struct stconn *sc,
list_for_each_entry(mod, stat_modules, list) {
struct counters_node *counters = EXTRA_COUNTERS_GET(ns->extra_counters, mod);
- mod->fill_stats(counters, stats + idx);
+ if (!mod->fill_stats(counters, stats + idx, NULL))
+ continue;
idx += mod->stats_count;
}
if (!stats_dump_one_line(stats, idx, appctx))
return 0;
- if (!stats_putchk(appctx, NULL))
+ if (!stats_putchk(appctx, NULL, NULL))
goto full;
return 1;
@@ -2797,6 +2882,7 @@ int resolv_allocate_counters(struct list *stat_modules)
if (strcmp(mod->name, "resolvers") == 0) {
ns->counters = (struct dns_counters *)ns->extra_counters->data + mod->counters_off[COUNTERS_RSLV];
ns->counters->id = ns->id;
+ ns->counters->ns_puid = ns->puid;
ns->counters->pid = resolvers->id;
}
}
@@ -3238,7 +3324,7 @@ int check_action_do_resolve(struct act_rule *rule, struct proxy *px, char **err)
void resolvers_setup_proxy(struct proxy *px)
{
- px->last_change = ns_to_sec(now_ns);
+ px->fe_counters.last_change = px->be_counters.last_change = ns_to_sec(now_ns);
px->cap = PR_CAP_FE | PR_CAP_BE;
px->maxconn = 0;
px->conn_retries = 1;
@@ -3371,7 +3457,9 @@ static int parse_resolve_conf(char **errmsg, char **warnmsg)
newnameserver->parent = curr_resolvers;
newnameserver->process_responses = resolv_process_responses;
newnameserver->conf.line = resolv_linenum;
+ newnameserver->puid = curr_resolvers->nb_nameservers;
LIST_APPEND(&curr_resolvers->nameservers, &newnameserver->list);
+ curr_resolvers->nb_nameservers++;
}
resolv_out:
@@ -3428,6 +3516,7 @@ static int resolvers_new(struct resolvers **resolvers, const char *id, const cha
r->timeout.resolve = 1000;
r->timeout.retry = 1000;
r->resolve_retries = 3;
+ r->nb_nameservers = 0;
LIST_INIT(&r->nameservers);
LIST_INIT(&r->resolutions.curr);
LIST_INIT(&r->resolutions.wait);
@@ -3572,8 +3661,10 @@ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
newnameserver->parent = curr_resolvers;
newnameserver->process_responses = resolv_process_responses;
newnameserver->conf.line = linenum;
+ newnameserver->puid = curr_resolvers->nb_nameservers;
/* the nameservers are linked backward first */
LIST_APPEND(&curr_resolvers->nameservers, &newnameserver->list);
+ curr_resolvers->nb_nameservers++;
}
else if (strcmp(args[0], "parse-resolv-conf") == 0) {
err_code |= parse_resolve_conf(&errmsg, &warnmsg);
@@ -3744,14 +3835,14 @@ out:
*/
int resolvers_create_default()
{
- int err_code = 0;
+ int err_code = ERR_NONE;
if (global.mode & MODE_MWORKER_WAIT) /* does not create the section if in wait mode */
- return 0;
+ return ERR_NONE;
/* if the section already exists, do nothing */
if (find_resolvers_by_id("default"))
- return 0;
+ return ERR_NONE;
curr_resolvers = NULL;
err_code |= resolvers_new(&curr_resolvers, "default", "<internal>", 0);
@@ -3777,7 +3868,7 @@ err:
/* we never return an error there, we only try to create this section
* if that's possible */
- return 0;
+ return ERR_NONE;
}
int cfg_post_parse_resolvers()
@@ -3811,3 +3902,70 @@ REGISTER_CONFIG_SECTION("resolvers", cfg_parse_resolvers, cfg_post_parse_re
REGISTER_POST_DEINIT(resolvers_deinit);
REGISTER_CONFIG_POSTPARSER("dns runtime resolver", resolvers_finalize_config);
REGISTER_PRE_CHECK(resolvers_create_default);
+
+#if defined(USE_PROMEX)
+
+static int rslv_promex_metric_info(unsigned int id, struct promex_metric *metric, struct ist *desc)
+{
+ if (id >= RSLV_STAT_END)
+ return -1;
+ if (id == RSLV_STAT_ID || id == RSLV_STAT_PID)
+ return 0;
+
+ *metric = (struct promex_metric){ .n = ist(resolv_stats[id].name), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_MODULE_METRIC };
+ *desc = ist(resolv_stats[id].desc);
+ return 1;
+}
+
+static void *rslv_promex_start_ts(void *unused, unsigned int id)
+{
+ struct resolvers *resolver = LIST_NEXT(&sec_resolvers, struct resolvers *, list);
+
+ return LIST_NEXT(&resolver->nameservers, struct dns_nameserver *, list);
+}
+
+static void *rslv_promex_next_ts(void *unused, void *metric_ctx, unsigned int id)
+{
+ struct dns_nameserver *ns = metric_ctx;
+ struct resolvers *resolver = ns->parent;
+
+ ns = LIST_NEXT(&ns->list, struct dns_nameserver *, list);
+ if (&ns->list == &resolver->nameservers) {
+ resolver = LIST_NEXT(&resolver->list, struct resolvers *, list);
+ ns = ((&resolver->list == &sec_resolvers)
+ ? NULL
+ : LIST_NEXT(&resolver->nameservers, struct dns_nameserver *, list));
+ }
+ return ns;
+}
+
+static int rslv_promex_fill_ts(void *unused, void *metric_ctx, unsigned int id, struct promex_label *labels, struct field *field)
+{
+ struct dns_nameserver *ns = metric_ctx;
+ struct resolvers *resolver = ns->parent;
+ struct field stats[RSLV_STAT_END];
+ int ret;
+
+ labels[0].name = ist("resolver");
+ labels[0].value = ist(resolver->id);
+ labels[1].name = ist("nameserver");
+ labels[1].value = ist(ns->id);
+
+ ret = resolv_fill_stats(ns->counters, stats, &id);
+ if (ret == 1)
+ *field = stats[id];
+ return ret;
+}
+
+static struct promex_module promex_resolver_module = {
+ .name = IST("resolver"),
+ .metric_info = rslv_promex_metric_info,
+ .start_ts = rslv_promex_start_ts,
+ .next_ts = rslv_promex_next_ts,
+ .fill_ts = rslv_promex_fill_ts,
+ .nb_metrics = RSLV_STAT_END,
+};
+
+INITCALL1(STG_REGISTER, promex_register_module, &promex_resolver_module);
+
+#endif