From 57bbf2b05984ba5c0712cc40e9c283f41cec6ed1 Mon Sep 17 00:00:00 2001 From: Libor Peltan Date: Fri, 19 Dec 2025 10:43:34 +0100 Subject: [PATCH] libknot+kdig: support for EDNS DE flag (DELEG-aware signal) --- doc/man_kdig.rst | 3 +++ src/knot/nameserver/process_query.c | 2 +- src/libknot/packet/pkt.h | 9 +++++++++ src/libknot/probe/data.c | 1 + src/libknot/probe/data.h | 3 ++- src/libknot/rrset-dump.c | 2 ++ src/libknot/rrtype/opt.h | 13 +++++++++++++ src/utils/common/exec.c | 19 +++++++++++++++++-- src/utils/kdig/kdig_exec.c | 5 ++++- src/utils/kdig/kdig_params.c | 25 ++++++++++++++++++++++++- src/utils/kdig/kdig_params.h | 2 ++ 11 files changed, 78 insertions(+), 6 deletions(-) diff --git a/doc/man_kdig.rst b/doc/man_kdig.rst index f21e2871c..74669cb6d 100644 --- a/doc/man_kdig.rst +++ b/doc/man_kdig.rst @@ -177,6 +177,9 @@ Options **+**\ [\ **no**\ ]\ **dnssec** Same as **+**\ [\ **no**\ ]\ **doflag** +**+**\ [\ **no**\ ]\ **deflag** + Set the DE flag. + **+**\ [\ **no**\ ]\ **all** Show all packet sections. diff --git a/src/knot/nameserver/process_query.c b/src/knot/nameserver/process_query.c index 6f37928bf..18c89a203 100644 --- a/src/knot/nameserver/process_query.c +++ b/src/knot/nameserver/process_query.c @@ -261,7 +261,7 @@ static int answer_edns_init(const knot_pkt_t *query, knot_pkt_t *resp, /* Set DO bit if set (DNSSEC requested). */ if (knot_pkt_has_dnssec(query)) { knot_edns_set_do(&qdata->opt_rr); - } + } // TODO: also echo back DE bit if we are DELEG-aware? /* Append NSID if requested and available. */ if (knot_pkt_edns_option(query, KNOT_EDNS_OPTION_NSID) != NULL) { diff --git a/src/libknot/packet/pkt.h b/src/libknot/packet/pkt.h index 577ce47bb..27843fdcf 100644 --- a/src/libknot/packet/pkt.h +++ b/src/libknot/packet/pkt.h @@ -364,6 +364,15 @@ static inline bool knot_pkt_has_dnssec(const knot_pkt_t *pkt) return knot_pkt_has_edns(pkt) && knot_edns_do(pkt->opt_rr); } +/*! + * \brief Checks if DE bit is set in the packet's OPT RR. + */ +static inline bool knot_pkt_has_deleg_aware(const knot_pkt_t *pkt) +{ + assert(pkt); + return knot_pkt_has_edns(pkt) && knot_edns_de(pkt->opt_rr); +} + /*! * \brief Get specific EDNS option from a parsed packet. */ diff --git a/src/libknot/probe/data.c b/src/libknot/probe/data.c index 9b5a9dbf2..1bcb03bdd 100644 --- a/src/libknot/probe/data.c +++ b/src/libknot/probe/data.c @@ -85,6 +85,7 @@ int knot_probe_data_set(knot_probe_data_t *data, knot_probe_proto_t proto, data->query_edns.version = knot_edns_get_version(query->opt_rr); data->query_edns.present = 1; data->query_edns.flag_do = knot_edns_do(query->opt_rr); + data->query_edns.flag_de = knot_edns_de(query->opt_rr); if (query->edns_opts != NULL) { for (int i = 0; i <= KNOT_EDNS_MAX_OPTION_CODE; i++) { if (query->edns_opts->ptr[i] != NULL) { diff --git a/src/libknot/probe/data.h b/src/libknot/probe/data.h index 53a6f55e9..9a1bf0f14 100644 --- a/src/libknot/probe/data.h +++ b/src/libknot/probe/data.h @@ -72,7 +72,8 @@ typedef struct { uint8_t version; /*!< EDNS version. */ uint8_t present : 1; /*!< EDNS presence indication. */ uint8_t flag_do : 1; /*!< DO flag indication. */ - uint8_t reserved : 6; /*!< Unused. */ + uint8_t flag_de : 1; /*!< DE flag indication. */ + uint8_t reserved : 5; /*!< Unused. */ } query_edns; struct { diff --git a/src/libknot/rrset-dump.c b/src/libknot/rrset-dump.c index 7da9a601d..858476bf6 100644 --- a/src/libknot/rrset-dump.c +++ b/src/libknot/rrset-dump.c @@ -987,6 +987,8 @@ static void wire_ednsflags_to_str(rrset_dump_params_t *p) if ((mask & KNOT_EDNS_DO_MASK)) { dump_string(p, "DO"); + } else if ((mask & KNOT_EDNS_DE_MASK)) { + dump_string(p, "DE"); } else { dump_str_uint(p, "BIT", i); } diff --git a/src/libknot/rrtype/opt.h b/src/libknot/rrtype/opt.h index 3e33396d9..ed077888d 100644 --- a/src/libknot/rrtype/opt.h +++ b/src/libknot/rrtype/opt.h @@ -28,6 +28,7 @@ enum { /*! \brief Bit mask for DO bit. */ KNOT_EDNS_DO_MASK = (uint32_t)(1 << 15), + KNOT_EDNS_DE_MASK = (uint32_t)(1 << 13), /*! \brief Minimal UDP payload with EDNS enabled. */ KNOT_EDNS_MIN_UDP_PAYLOAD = 512, @@ -259,6 +260,12 @@ bool knot_edns_do(const knot_rrset_t *opt_rr) assert(opt_rr != NULL); return opt_rr->ttl & KNOT_EDNS_DO_MASK; } +static inline +bool knot_edns_de(const knot_rrset_t *opt_rr) +{ + assert(opt_rr != NULL); + return opt_rr->ttl & KNOT_EDNS_DE_MASK; +} /*! * \brief Sets the DO bit in the OPT RR. @@ -274,6 +281,12 @@ void knot_edns_set_do(knot_rrset_t *opt_rr) assert(opt_rr != NULL); opt_rr->ttl |= KNOT_EDNS_DO_MASK; } +static inline +void knot_edns_set_de(knot_rrset_t *opt_rr) +{ + assert(opt_rr != NULL); + opt_rr->ttl |= KNOT_EDNS_DE_MASK; +} /*! * \brief Add EDNS option into the package with empty (zeroed) content. diff --git a/src/utils/common/exec.c b/src/utils/common/exec.c index 23483cf92..f9254e206 100644 --- a/src/utils/common/exec.c +++ b/src/utils/common/exec.c @@ -336,9 +336,22 @@ static void print_section_opt(const knot_pkt_t *packet, const style_t *style) ercode_str = unknown_ercode; } - printf(";; Version: %u; flags: %s; UDP size: %u B; ext-rcode: %s\n", + char flags[96] = " ", *flend = flags; + for (uint32_t mask = 1 << 15, bit = 1; mask; mask >>= 1, bit++) { + if (packet->opt_rr->ttl & mask) { + if (mask & KNOT_EDNS_DO_MASK) { + flend += snprintf(flend, 4, " do"); + } else if (mask & KNOT_EDNS_DE_MASK) { + flend += snprintf(flend, 4, " de"); + } else { + flend += snprintf(flend, 7, " bit%u", bit); + } + } + } + + printf(";; Version: %u; flags:%s; UDP size: %u B; ext-rcode: %s\n", knot_edns_get_version(packet->opt_rr), - (knot_edns_do(packet->opt_rr) != 0) ? "do" : "", + flags, knot_edns_get_payload(packet->opt_rr), ercode_str); @@ -851,6 +864,8 @@ static void json_print_edns(jsonw_t *w, const knot_pkt_t *pkt) if ((flags & mask)) { if ((mask & KNOT_EDNS_DO_MASK)) { jsonw_str(w, NULL, "DO"); + } else if ((mask & KNOT_EDNS_DE_MASK)) { + jsonw_str(w, NULL, "DE"); } else { (void)snprintf(tmp, sizeof(tmp), "BIT%d", i); jsonw_str(w, NULL, tmp); diff --git a/src/utils/kdig/kdig_exec.c b/src/utils/kdig/kdig_exec.c index d4090bfe7..d6e019655 100644 --- a/src/utils/kdig/kdig_exec.c +++ b/src/utils/kdig/kdig_exec.c @@ -242,6 +242,9 @@ static int add_query_edns(knot_pkt_t *packet, const query_t *query, uint16_t max if (query->flags.do_flag) { knot_edns_set_do(&opt_rr); } + if (query->flags.de_flag) { + knot_edns_set_de(&opt_rr); + } /* Append NSID. */ if (query->nsid) { @@ -356,7 +359,7 @@ static bool use_edns(const query_t *query) return query->edns > -1 || query->udp_size > -1 || query->nsid || query->zoneversion || query->subnet.family != AF_UNSPEC || query->flags.do_flag || query->cc.len > 0 || do_padding(query) || - !ednsopt_list_empty(&query->edns_opts); + query->flags.de_flag || !ednsopt_list_empty(&query->edns_opts); } static knot_pkt_t *create_query_packet(const query_t *query) diff --git a/src/utils/kdig/kdig_params.c b/src/utils/kdig/kdig_params.c index d3545c8e4..50b8907f3 100644 --- a/src/utils/kdig/kdig_params.c +++ b/src/utils/kdig/kdig_params.c @@ -40,7 +40,8 @@ static const flags_t DEFAULT_FLAGS_DIG = { .z_flag = false, .ad_flag = true, .cd_flag = false, - .do_flag = false + .do_flag = false, + .de_flag = false, }; static const style_t DEFAULT_STYLE_DIG = { @@ -275,6 +276,24 @@ static int opt_nodoflag(const char *arg, void *query) return KNOT_EOK; } +static int opt_deflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.de_flag = true; + + return KNOT_EOK; +} + +static int opt_nodeflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.de_flag = false; + + return KNOT_EOK; +} + static int opt_all(const char *arg, void *query) { query_t *q = query; @@ -1550,6 +1569,9 @@ static const param_t kdig_opts2[] = { { "dnssec", ARG_NONE, opt_doflag }, // Alias. { "nodnssec", ARG_NONE, opt_nodoflag }, + { "deflag", ARG_NONE, opt_deflag }, + { "nodeflag", ARG_NONE, opt_nodeflag }, + { "all", ARG_NONE, opt_all }, { "noall", ARG_NONE, opt_noall }, @@ -2377,6 +2399,7 @@ static void print_help(void) " +[no]cdflag Set CD flag.\n" " +[no]doflag Set DO flag.\n" " +[no]dnssec Same as +[no]doflag.\n" + " +[no]deflag Set DE flag.\n" " +[no]all Show all packet sections.\n" " +[no]qr Show query packet.\n" " +[no]header * Show packet header.\n" diff --git a/src/utils/kdig/kdig_params.h b/src/utils/kdig/kdig_params.h index 869ae12e7..941164724 100644 --- a/src/utils/kdig/kdig_params.h +++ b/src/utils/kdig/kdig_params.h @@ -48,6 +48,8 @@ typedef struct { bool cd_flag; /*!< DNSSEC OK flag. */ bool do_flag; + /*!< DELEG-aware flag. */ + bool de_flag; } flags_t; /*! \brief Basic parameters for DNS query. */