]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
Non-trivial merge 'master-5' (see 'modules/stats') docs-develop-stat-s8hiue/deployments/4185
authorOto Šťáva <oto.stava@nic.cz>
Mon, 27 May 2024 10:59:51 +0000 (12:59 +0200)
committerOto Šťáva <oto.stava@nic.cz>
Mon, 27 May 2024 11:35:08 +0000 (13:35 +0200)
modules/stats: adapt aggregate stats to the new hierarchical format

Knot Resolver 6 changed the structure of the stats return value, making
it more hierarchical, i.e. the returned object contains nested
"sub-objects", rather than being flat. This commit adapts the new
aggregate stats to this new structure, for consistency.

1  2 
.gitlab-ci.yml
NEWS
modules/stats/stats.c

diff --cc .gitlab-ci.yml
Simple merge
diff --cc NEWS
index 9b3ed72ad6f2e0818b536e9a6c4832b88b319312,dca74386432170a166679bbddf2a2b888ab8cc3f..6ed72b2639156877a7fb179911a7384f5f6cc108
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
 +Knot Resolver 6.0.8 (2024-0m-dd)
 +================================
 +
 +Improvements
 +------------
 +- TLS (DoT, DoH): respect crypto policy overrides in OS (!1526)
 +- arch package: fix after they renamed a dependency (!1536)
 +- manager: export metrics to JSON via management HTTP API (!1527)
 +
 +  * JSON is the new default metrics output format
 +  * the ``prometheus-client`` Python package is now an optional dependency,
 +    required only for Prometheus export to work
 +- cache: prefetching records
 +
 +  * predict module: prefetching expiring records moved to prefetch module
 +  * prefetch module: new module to prefetch expiring records
++- stats: add separate metrics for IPv6 and IPv4 (!1545)
 +
 +.. TODO: Change the link below to a versioned one when releasing.
 +
 +Incompatible changes
 +--------------------
 +- cache: the ``/cache/prediction`` configuration property has been reorganized
 +  into ``/cache/prefetch/expiring`` and ``/cache/prefetch/prediction``, changing
 +  the default behaviour as well. See the `relevant documentation section
 +  <https://www.knot-resolver.cz/documentation/latest/config-cache-predict.html>`_
 +  for more.
 +
 +
 +Knot Resolver 6.0.7 (2024-03-27)
 +================================
 +
 +Improvements
 +------------
 +- manager: clear the cache via management HTTP API (#876, !1491)
 +- manager: added support for Python 3.12 and removed for 3.7 (!1502)
 +- manager: use build-time install prefix to execute `kresd` instead of PATH (!1511)
 +- docs: documentation is now separated into user and developer parts (!1514)
 +- daemon: ignore UDP requests from ports < 1024 (!1507)
 +- manager: increase startup timeout for processes (!1518, !1520)
 +- local-data: increase default DB size to 2G on 64-bit platforms (!1518)
 +
 +Bugfixes
 +--------
 +- fix listening by interface name containing dashes (#900, !1500)
 +- fix kresctl http request timeout (!1505)
 +- fix RPZ if it contains apex NS record (!1516)
 +- fix RPZ if SOA is repated, as usual in AXFR output (!1521)
 +- avoid RPZ overriding the root SOA (!1521)
 +- fix on 32-bit systems with 64-bit time_t (!1510)
 +- fix paths to knot-dns libs if exec_prefix != prefix (!1503)
 +- manager: add missing early check that neither a custom port nor TLS is set for
 +  authoritative server forwarding (#902, !1505)
 +
 +
 +Knot Resolver 6.0.6 (2024-02-13)
 +================================
 +
 +Security
 +--------
 +- CVE-2023-50868: NSEC3 closest encloser proof can exhaust CPU
 +
 +  * validator: lower the NSEC3 iteration limit (150 -> 50)
 +  * validator: similarly also limit excessive NSEC3 salt length
 +  * cache: limit the amount of work on SHA1 in NSEC3 aggressive cache
 +  * validator: limit the amount of work on SHA1 in NSEC3 proofs
 +  * validator: refuse to validate answers with more than 8 NSEC3 records
 +
 +- CVE-2023-50387 "KeyTrap": DNSSEC verification complexity
 +  could be exploited to exhaust CPU resources and stall DNS resolvers.
 +  Solution boils down mainly to limiting crypto-validations per packet.
 +
 +  We would like to thank Elias Heftrig, Haya Schulmann, Niklas Vogel and Michael Waidner
 +  from the German National Research Center for Applied Cybersecurity ATHENE
 +  for bringing this vulnerability to our attention.
 +
 +Improvements
 +------------
 +- update addresses of B.root-servers.net (!1478)
 +- tweak the default run_dir on non-Linux (!1481)
 +
 +Bugfixes
 +--------
 +- fix potential SERVFAIL deadlocks if net.ipv6 = false (#880)
 +- fix validation of RRsets around 64 KiB size; needs libknot >= 3.4 (!1497)
 +
 +
 +Knot Resolver 6.0.5 (2024-01-09)
 +================================
 +
 +6.0.x are "early access" versions,
 +not generally recommended for production use.
 +
 +6.0 contains biggest changes in the history of Knot Resolver releases.
 +You will have to rewrite your configuration.  See documentation, in particular:
 +https://www.knot-resolver.cz/documentation/latest/upgrading-to-6.html
 +
 +
 +
 +
 +5.x branch longterm support
 +~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +
+ Knot Resolver 5.7.3 (2024-0m-dd)
+ ================================
+ Improvements
+ ------------
+ - stats: add separate metrics for IPv6 and IPv4 (!1544)
  Knot Resolver 5.7.2 (2024-03-27)
  ================================
  
index 4f2fb405114117184cee5112e30a630fd48bb797,367961521b5d7c8a701b156cc7e595d53da1e86d..deed9c949a2ea71a5d2a5b1fe37be129a82987b0
@@@ -72,6 -65,28 +73,29 @@@ static struct const_metric_elm const_me
        CONST_METRICS(X)
        #undef X
  };
 -      const char *key;
+ /// These metrics are read-only views, each simply summing a pair of const_metrics items.
+ struct sum_metric {
 -              .key = "request." #proto, \
++      const char *sub_key;
+       const size_t *val1, *val2;
+ };
+ static const struct sum_metric sum_metrics[] = {
+       // We're using this to aggregate v4 + v6 pairs.
+       #define DEF(proto) { \
++              .sub_key = #proto, \
+               .val1 = &const_metrics[metric_request_ ## proto ## 4].val, \
+               .val2 = &const_metrics[metric_request_ ## proto ## 6].val, \
+       }
+       DEF(udp),
+       DEF(tcp),
+       DEF(xdp),
+       DEF(dot),
+       DEF(doh),
+       #undef DEF
+ };
+ static const size_t sum_metrics_len = sizeof(sum_metrics) / sizeof(sum_metrics[0]);
++#define SUM_METRICS_SUP_KEY "request"
  /** @endcond */
  
  /** @internal LRU hash of most frequent names. */
@@@ -335,6 -358,16 +367,16 @@@ static char* stats_get(void *env, struc
                        return str_value;
                }
        }
 -              if (strcmp(smi->key, args) == 0) {
+       /* Check if it exists in aggregate metrics. */
+       for (int i = 0; i < sum_metrics_len; ++i) {
+               const struct sum_metric *smi = &sum_metrics[i];
++              if (strcmp(smi->sub_key, args) == 0) {
+                       ret = asprintf(&str_value, "%zu", *smi->val1 + *smi->val2);
+                       if (ret < 0)
+                               return NULL;
+                       return str_value;
+               }
+       }
        /* Check in variable map */
        trie_val_t *val = trie_get_try(data->trie, args, strlen(args));
        if (!val)
@@@ -360,6 -393,6 +402,24 @@@ struct list_entry_context 
        size_t key_prefix_len;  /**< Prefix length. Prefix is a wildcard if zero. */
  };
  
++/** Ensures that the `root` node contains an object (and only an object - not a
++ * number, array, or anything else) with the specified `key`. If this cannot be
++ * ensured, fails on an assertion, or returns `NULL` if asserts are disabled. */
++static JsonNode *ensure_object(JsonNode *root, const char *key)
++{
++      JsonNode *node = json_find_member(root, key);
++      if (node) {
++              if (kr_fails_assert(node->tag == JSON_OBJECT))
++                      return NULL;
++      } else {
++              node = json_mkobject();
++              if (kr_fails_assert(node))
++                      return NULL;
++              json_append_member(root, key, node);
++      }
++      return node;
++}
++
  /** Inserts the entry with a matching key into the JSON object. */
  static int list_entry(const char *key, uint32_t key_len, trie_val_t *val, void *baton)
  {
        if (!key_matches_prefix(key, key_len, ctx->key_prefix, ctx->key_prefix_len))
                return 0;
        size_t number = (size_t)*val;
 -      auto_free char *key_nt = strndup(key, key_len);
 -      json_append_member(ctx->root, key_nt, json_mknumber((double)number));
 +
 +      uint32_t dot_index = 0;
 +      for (uint32_t i = 0; i < key_len; i++) {
 +              if (!key[i])
 +                      break;
 +              if (key[i] == '.') {
 +                      dot_index = i;
 +              }
 +      }
 +
 +      if (dot_index) {
 +              auto_free char *sup_key_nt = strndup(key, dot_index);
 +              auto_free char *sub_key_nt = strndup(key + dot_index + 1, key_len - dot_index - 1);
-               JsonNode *sup = json_find_member(ctx->root, sup_key_nt);
-               if (!sup) {
-                       sup = json_mkobject();
-                       if (kr_fails_assert(sup))
-                               return 0;
-                       json_append_member(ctx->root, sup_key_nt, sup);
-               }
-               if (kr_fails_assert(sup))
++              JsonNode *sup = ensure_object(ctx->root, sup_key_nt);
++              if (!sup)
 +                      return 0;
 +              json_append_member(sup, sub_key_nt, json_mknumber((double)number));
 +      } else {
 +              auto_free char *key_nt = strndup(key, key_len);
 +              json_append_member(ctx->root, key_nt, json_mknumber((double)number));
 +      }
        return 0;
  }
  
   */
  static char* stats_list(void *env, struct kr_module *module, const char *args)
  {
++      char *ret;
        JsonNode *root = json_mkobject();
        /* Walk const metrics map */
        size_t args_len = args ? strlen(args) : 0;
        for (unsigned i = 0; i < metric_const_end; ++i) {
                struct const_metric_elm *elm = &const_metrics[i];
 -              if (!args || strncmp(elm->key, args, args_len) == 0) {
 -                      json_append_member(root, elm->key, json_mknumber((double)elm->val));
 +              if (!args || strcmp(elm->sup_key, args) == 0) {
-                       JsonNode *sup = json_find_member(root, elm->sup_key);
++                      JsonNode *sup = ensure_object(root, elm->sup_key);
 +                      if (!sup) {
-                               sup = json_mkobject();
-                               if (kr_fails_assert(sup))
-                                       break;
-                               json_append_member(root, elm->sup_key, sup);
++                              ret = strdup("\"ERROR\"");
++                              goto exit;
 +                      }
-                       if (kr_fails_assert(sup))
-                               break;
 +                      json_append_member(sup, elm->sub_key, json_mknumber((double)elm->val));
                }
        }
 -              if (!args || strncmp(elm->key, args, args_len) == 0) {
++
++      /* Walk sum metrics map */
++      JsonNode *sum_sup = ensure_object(root, SUM_METRICS_SUP_KEY);
++      if (!sum_sup) {
++              ret = strdup("\"ERROR\"");
++              goto exit;
++      }
+       for (int i = 0; i < sum_metrics_len; ++i) {
+               const struct sum_metric *elm = &sum_metrics[i];
 -                      json_append_member(root, elm->key, json_mknumber(val));
++              if (!args || strncmp(elm->sub_key, args, args_len) == 0) {
+                       size_t val = *elm->val1 + *elm->val2;
++                      json_append_member(sum_sup, elm->sub_key, json_mknumber(val));
+               }
+       }
++
        struct list_entry_context ctx = {
                .root = root,
                .key_prefix = args,
        };
        struct stat_data *data = module->data;
        trie_apply_with_key(data->trie, list_entry, &ctx);
--      char *ret = json_encode(root);
++      ret = json_encode(root);
++exit:
        json_delete(root);
        return ret;
  }