]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
sdig adding OT IDs and rec incoming processing of OT IDs
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 26 May 2025 07:23:38 +0000 (09:23 +0200)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Thu, 12 Jun 2025 12:21:06 +0000 (14:21 +0200)
Signed-off-by: Otto Moerbeek <otto.moerbeek@open-xchange.com>
pdns/ednsoptions.hh
pdns/protozero-trace.cc [moved from pdns/recursordist/protozero-trace.cc with 91% similarity]
pdns/protozero-trace.hh [moved from pdns/recursordist/protozero-trace.hh with 94% similarity]
pdns/recursordist/pdns_recursor.cc
pdns/recursordist/rec-tcp.cc
pdns/sdig.cc

index fe6e4bf1dcd76eff1254d27babb48521e15797bd..101d1e03b0c9f9048ba62e86277e20d6f501aebd 100644 (file)
@@ -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 */
similarity index 91%
rename from pdns/recursordist/protozero-trace.cc
rename to pdns/protozero-trace.cc
index 5ab684da12828e8ad247cca2f6ba44201f22937b..141f79c103e102075dc0db55a87f263513da1dda 100644 (file)
@@ -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
similarity index 94%
rename from pdns/recursordist/protozero-trace.hh
rename to pdns/protozero-trace.hh
index 6cc4e11cb0f23d2e4e06ba91203a5ef312b689d2..cabe918d8438c96987fe027b53800f1c5bd79b66 100644 (file)
@@ -30,6 +30,7 @@
 #include <protozero/pbf_writer.hpp>
 
 #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<const char*>(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<KeyValueList, KeyValue>(reader);
 }
 
-}
+void extractOTraceIDs(const EDNSOptionViewMap& map, pdns::trace::Span& span);
+
+} // namespace pdns::trace
index 4657ecebc5e434b2e8ce326f48ea1dd6344b6bee..1e820abfdfaa131d85ec4af177619fee79dbdf5b 100644 (file)
@@ -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;
 
index 2dd833fad1678564cd3ed2d1056bbb01ad4b50b7..936f92200a4538192a5ac6b10d767733f93e193e 100644 (file)
@@ -307,7 +307,16 @@ static void doProcessTCPQuestion(std::unique_ptr<DNSComboWriter>& comboWriter, s
   boost::optional<uint32_t> 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<DNSComboWriter>& 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()) {
index 065e65e7878a77ae41d64a5190cd929d8ccd3106..92592fa508cbb0051a1f8a93498034ee8d4de7e3 100644 (file)
@@ -13,6 +13,7 @@
 #include "sstuff.hh"
 #include "statbag.hh"
 #include <boost/array.hpp>
+#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<std::pair<pdns::trace::TraceID, pdns::trace::SpanID>>;
+
 static std::unordered_set<uint16_t> s_expectedIDs;
 
 static void fillPacket(vector<uint8_t>& packet, const string& q, const string& t,
                        bool dnssec, const std::optional<Netmask>& ednsnm,
-                       bool recurse, QClass qclass, uint8_t opcode, uint16_t qid, const std::optional<string>& cookie)
+                       bool recurse, QClass qclass, uint8_t opcode, uint16_t qid, const std::optional<string>& 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<uint8_t>& 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<const char*>(otids->first.data()), otids->first.size()));
+      pdns::trace::random(otids->second);
+      opts.emplace_back(EDNSOptionCode::OTSPANID, std::string_view(reinterpret_cast<const char*>(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<string> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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;