From a0430d45b6f3c67b694bfb7dedfaedf3352e5eef Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Mon, 26 May 2025 09:23:38 +0200 Subject: [PATCH] sdig adding OT IDs and rec incoming processing of OT IDs Signed-off-by: Otto Moerbeek --- pdns/ednsoptions.hh | 2 +- pdns/{recursordist => }/protozero-trace.cc | 28 ++++++++++ pdns/{recursordist => }/protozero-trace.hh | 60 ++++++++++++++++++++-- pdns/recursordist/pdns_recursor.cc | 17 +++--- pdns/recursordist/rec-tcp.cc | 15 +++++- pdns/sdig.cc | 41 ++++++++++++--- 6 files changed, 142 insertions(+), 21 deletions(-) rename pdns/{recursordist => }/protozero-trace.cc (91%) rename pdns/{recursordist => }/protozero-trace.hh (94%) diff --git a/pdns/ednsoptions.hh b/pdns/ednsoptions.hh index fe6e4bf1dc..101d1e03b0 100644 --- a/pdns/ednsoptions.hh +++ b/pdns/ednsoptions.hh @@ -24,7 +24,7 @@ struct EDNSOptionCode { - enum EDNSOptionCodeEnum {NSID=3, DAU=5, DHU=6, N3U=7, ECS=8, EXPIRE=9, COOKIE=10, TCPKEEPALIVE=11, PADDING=12, CHAIN=13, KEYTAG=14, EXTENDEDERROR=15}; + enum EDNSOptionCodeEnum : uint16_t {NSID=3, DAU=5, DHU=6, N3U=7, ECS=8, EXPIRE=9, COOKIE=10, TCPKEEPALIVE=11, PADDING=12, CHAIN=13, KEYTAG=14, EXTENDEDERROR=15, OTTRACEID=65500, OTSPANID=65501}; }; /* extract the position (relative to the optRR pointer!) and size of a specific EDNS0 option from a pointer on the beginning rdLen of the OPT RR */ diff --git a/pdns/recursordist/protozero-trace.cc b/pdns/protozero-trace.cc similarity index 91% rename from pdns/recursordist/protozero-trace.cc rename to pdns/protozero-trace.cc index 5ab684da12..141f79c103 100644 --- a/pdns/recursordist/protozero-trace.cc +++ b/pdns/protozero-trace.cc @@ -506,4 +506,32 @@ KeyValue KeyValue::decode(protozero::pbf_reader& reader) return value; } +void extractOTraceIDs(const EDNSOptionViewMap& map, pdns::trace::Span& span) +{ + // traceid gets set from edns options (if available and well-formed), otherwise random + // parent_span_id gets set form edns options (if available and well-formed, otherwise it says cleared (no parent) + // span_id gets inited randomly + bool traceidset = false; + if (const auto& option = map.find(EDNSOptionCode::OTTRACEID); option != map.end()) { + if (option->second.values.size() > 0) { + if (option->second.values.at(0).size == span.trace_id.size()) { + traceidset = true; + pdns::trace::fill(span.trace_id, option->second.values.at(0).content, span.trace_id.size()); + } + } + } + if (!traceidset) { + random(span.trace_id); + } + if (const auto& option = map.find(EDNSOptionCode::OTSPANID); option != map.end()) { + if (option->second.values.size() > 0) { + if (option->second.values.at(0).size == span.parent_span_id.size()) { + pdns::trace::fill(span.parent_span_id, option->second.values.at(0).content, span.parent_span_id.size()); + } + } + // Empty parent span id indicated the client did not set one + } + random(span.span_id); } + +} // namespace pdns::trace diff --git a/pdns/recursordist/protozero-trace.hh b/pdns/protozero-trace.hh similarity index 94% rename from pdns/recursordist/protozero-trace.hh rename to pdns/protozero-trace.hh index 6cc4e11cb0..cabe918d84 100644 --- a/pdns/recursordist/protozero-trace.hh +++ b/pdns/protozero-trace.hh @@ -30,6 +30,7 @@ #include #include "dns_random.hh" +#include "ednsoptions.hh" // See https://github.com/open-telemetry/opentelemetry-proto/tree/main/opentelemetry/proto @@ -232,16 +233,42 @@ inline void random(SpanID& span) dns_random(span.data(), span.size()); } -inline void reset(TraceID& trace) +inline void clear(TraceID& trace) { memset(trace.data(), 0, trace.size()); } -inline void reset(SpanID& span) +inline void clear(SpanID& span) { memset(span.data(), 0, span.size()); } +inline void fill(TraceID& trace, const std::string& data) +{ + if (data.size() != trace.size()) { + throw std::runtime_error("TracID size mismatch"); + } + memcpy(trace.data(), data.data(), trace.size()); +} + +inline void fill(SpanID& span, const std::string& data) +{ + if (data.size() != span.size()) { + throw std::runtime_error("TracID size mismatch"); + } + memcpy(span.data(), data.data(), span.size()); +} + +inline void fill(TraceID& trace, const char* data, size_t size) +{ + fill(trace, std::string(data, size)); +} + +inline void fill(SpanID& span, const char* data, size_t size) +{ + fill(span, std::string(data, size)); +} + inline void encode(protozero::pbf_writer& writer, uint8_t field, const TraceID& value) { writer.add_bytes(field, reinterpret_cast(value.data()), value.size()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) it's the API @@ -291,6 +318,11 @@ struct Status // The status code. StatusCode code{StatusCode::STATUS_CODE_UNSET}; // = 3; + void clear() + { + message.clear(); + code = StatusCode::STATUS_CODE_UNSET; + } void encode(protozero::pbf_writer& writer) const; static Status decode(protozero::pbf_reader& reader); }; @@ -498,6 +530,26 @@ struct Span { end_time_unix_nano = timestamp(); } + + void clear() + { + pdns::trace::clear(trace_id); // 1 + pdns::trace::clear(span_id); // 2 + trace_state.clear(); // 3 + pdns::trace::clear(parent_span_id); // 4 + name.clear(); // 5 + kind = SpanKind::SPAN_KINUNSPECIFIED; // 6 + start_time_unix_nano = 0; // 7 + end_time_unix_nano = 0; // 8 + attributes.clear(); // 9 + dropped_attributes_count = 0; // 10 + events.clear(); // 11 + dropped_events_count = 0; // 12 + links.clear(); // 13 + dropped_links_count = 0; //14 + status.clear(); // 15 + flags = 0; // 16 + } void encode(protozero::pbf_writer& writer) const; static Span decode(protozero::pbf_reader& reader); }; @@ -630,4 +682,6 @@ inline KeyValueList KeyValueList::decode(protozero::pbf_reader& reader) return pdns::trace::decode(reader); } -} +void extractOTraceIDs(const EDNSOptionViewMap& map, pdns::trace::Span& span); + +} // namespace pdns::trace diff --git a/pdns/recursordist/pdns_recursor.cc b/pdns/recursordist/pdns_recursor.cc index 4657ecebc5..1e820abfdf 100644 --- a/pdns/recursordist/pdns_recursor.cc +++ b/pdns/recursordist/pdns_recursor.cc @@ -2266,11 +2266,16 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr ecsFound = false; getQNameAndSubnet(question, &qname, &qtype, &qclass, - ecsFound, &ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr, ednsVersion); + ecsFound, &ednssubnet, + (g_gettagNeedsEDNSOptions || (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_ot) != 0) ? &ednsOptions : nullptr, + ednsVersion); qnameParsed = true; ecsParsed = true; + if ((SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_ot) != 0) { + pdns::trace::extractOTraceIDs(ednsOptions, otTrace); + } if (t_pdl) { try { if (t_pdl->hasGettagFFIFunc()) { @@ -2490,17 +2495,9 @@ static void handleNewUDPQuestion(int fileDesc, FDMultiplexer::funcparam_t& /* va auto traceTS = pdns::trace::timestamp(); eventTrace.add(RecEventTrace::ReqRecv); if ((SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_ot) != 0) { + otTrace.clear(); otTrace.start_time_unix_nano = traceTS; - pdns::trace::TraceID traceid; - pdns::trace::random(traceid); - pdns::trace::SpanID spanid; - pdns::trace::random(spanid); - pdns::trace::SpanID parent; - pdns::trace::reset(parent); otTrace.name = "RecRequest"; - otTrace.trace_id = traceid; - otTrace.span_id = spanid; - otTrace.parent_span_id = parent; } firstQuery = false; diff --git a/pdns/recursordist/rec-tcp.cc b/pdns/recursordist/rec-tcp.cc index 2dd833fad1..936f92200a 100644 --- a/pdns/recursordist/rec-tcp.cc +++ b/pdns/recursordist/rec-tcp.cc @@ -307,7 +307,16 @@ static void doProcessTCPQuestion(std::unique_ptr& comboWriter, s boost::optional ednsVersion; comboWriter->d_eventTrace.setEnabled(SyncRes::s_event_trace_enabled != 0); + // evenTrace use monotonic time, while OpenTelemetry uses absolute time. setEnabled() + // estabslished the reference point, get an absolute TS as close as possible to the + // eventTrace start of trace time. + auto traceTS = pdns::trace::timestamp(); comboWriter->d_eventTrace.add(RecEventTrace::ReqRecv); + if ((SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_ot) != 0) { + comboWriter->d_otTrace.clear(); + comboWriter->d_otTrace.start_time_unix_nano = traceTS; + comboWriter->d_otTrace.name = "RecRequest"; + } auto luaconfsLocal = g_luaconfs.getLocal(); if (checkProtobufExport(luaconfsLocal)) { needEDNSParse = true; @@ -322,9 +331,13 @@ static void doProcessTCPQuestion(std::unique_ptr& comboWriter, s comboWriter->d_ecsParsed = true; comboWriter->d_ecsFound = false; getQNameAndSubnet(conn->data, &qname, &qtype, &qclass, - comboWriter->d_ecsFound, &comboWriter->d_ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr, ednsVersion); + comboWriter->d_ecsFound, &comboWriter->d_ednssubnet, + (g_gettagNeedsEDNSOptions || (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_ot) != 0) ? &ednsOptions : nullptr, ednsVersion); qnameParsed = true; + if ((SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_ot) != 0) { + pdns::trace::extractOTraceIDs(ednsOptions, comboWriter->d_otTrace); + } if (t_pdl) { try { if (t_pdl->hasGettagFFIFunc()) { diff --git a/pdns/sdig.cc b/pdns/sdig.cc index 065e65e787..92592fa508 100644 --- a/pdns/sdig.cc +++ b/pdns/sdig.cc @@ -13,6 +13,7 @@ #include "sstuff.hh" #include "statbag.hh" #include +#include "protozero-trace.hh" #ifdef HAVE_LIBCURL #include "minicurl.hh" @@ -43,7 +44,8 @@ static void usage() "[tcp] [dot] [insecure] [fastOpen] [subjectName name] [caStore file] [tlsProvider openssl|gnutls] " "[proxy UDP(0)/TCP(1) SOURCE-IP-ADDRESS-AND-PORT DESTINATION-IP-ADDRESS-AND-PORT] " "[cookie -/HEX] " - "[dumpluaraw] [opcode OPNUM]" + "[dumpluaraw] [opcode OPNUM] " + "[otid -/HEX]" << endl; } @@ -55,15 +57,18 @@ static const string nameForClass(QClass qclass, uint16_t qtype) return qclass.toString(); } +using OpenTelemetryData = std::optional>; + static std::unordered_set s_expectedIDs; static void fillPacket(vector& packet, const string& q, const string& t, bool dnssec, const std::optional& ednsnm, - bool recurse, QClass qclass, uint8_t opcode, uint16_t qid, const std::optional& cookie) + bool recurse, QClass qclass, uint8_t opcode, uint16_t qid, const std::optional& cookie, + OpenTelemetryData& otids) { DNSPacketWriter pw(packet, DNSName(q), DNSRecordContent::TypeToNumber(t), qclass, opcode); - if (dnssec || ednsnm || getenv("SDIGBUFSIZE") != nullptr || cookie) { // NOLINT(concurrency-mt-unsafe) we're single threaded + if (dnssec || ednsnm || getenv("SDIGBUFSIZE") != nullptr || cookie || otids) { // NOLINT(concurrency-mt-unsafe) we're single threaded char* sbuf = getenv("SDIGBUFSIZE"); // NOLINT(concurrency-mt-unsafe) we're single threaded int bufsize; if (sbuf) @@ -89,6 +94,11 @@ static void fillPacket(vector& packet, const string& q, const string& t } opts.emplace_back(EDNSOptionCode::COOKIE, cookieOpt.makeOptString()); } + if (otids) { + opts.emplace_back(EDNSOptionCode::OTTRACEID, std::string_view(reinterpret_cast(otids->first.data()), otids->first.size())); + pdns::trace::random(otids->second); + opts.emplace_back(EDNSOptionCode::OTSPANID, std::string_view(reinterpret_cast(otids->second.data()), otids->second.size())); + } pw.addOpt(bufsize, 0, dnssec ? EDNSOpts::DNSSECOK : 0, opts); pw.commit(); } @@ -245,6 +255,7 @@ try { string tlsProvider = "openssl"; bool dumpluaraw = false; std::optional cookie; + OpenTelemetryData otdata; // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic, concurrency-mt-unsafe) it's the argv API and we're single-threaded for (int i = 1; i < argc; i++) { @@ -348,6 +359,24 @@ try { else if (strcmp(argv[i], "dumpluaraw") == 0) { dumpluaraw = true; } + else if (strcmp(argv[i], "otid") == 0) { + if (argc < i + 2) { + cerr << "otid needs an argument" << endl; + exit(EXIT_FAILURE); + } + auto traceIDArg = std::string(argv[++i]); + pdns::trace::TraceID traceid{}; + if (traceIDArg == "-") { + pdns::trace::random(traceid); + } + else { + auto traceIDStr = makeBytesFromHex(traceIDArg); + traceIDStr.resize(traceid.size()); + pdns::trace::fill(traceid, traceIDStr); + } + pdns::trace::SpanID spanid{}; // don't fill in yet + otdata = std::make_pair(traceid, spanid); + } else { cerr << argv[i] << ": unknown argument" << endl; exit(EXIT_FAILURE); @@ -399,7 +428,7 @@ try { #ifdef HAVE_LIBCURL vector packet; s_expectedIDs.insert(0); - fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0, cookie); + fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0, cookie, otdata); MiniCurl mc; MiniCurl::MiniCurlHeaders mch; mch.emplace("Content-Type", "application/dns-message"); @@ -453,7 +482,7 @@ try { for (const auto& it : questions) { vector packet; s_expectedIDs.insert(counter); - fillPacket(packet, it.first, it.second, dnssec, ednsnm, recurse, qclass, opcode, counter, cookie); + fillPacket(packet, it.first, it.second, dnssec, ednsnm, recurse, qclass, opcode, counter, cookie, otdata); counter++; // Prefer to do a single write, so that fastopen can send all the data on SYN @@ -483,7 +512,7 @@ try { { vector packet; s_expectedIDs.insert(0); - fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0, cookie); + fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0, cookie, otdata); string question(packet.begin(), packet.end()); Socket sock(dest.sin4.sin_family, SOCK_DGRAM); question = proxyheader + question; -- 2.47.2