diff options
Diffstat (limited to 'src/resolvers.c')
-rw-r--r-- | src/resolvers.c | 236 |
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 |