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 */
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
#include <protozero/pbf_writer.hpp>
#include "dns_random.hh"
+#include "ednsoptions.hh"
// See https://github.com/open-telemetry/opentelemetry-proto/tree/main/opentelemetry/proto
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
// 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);
};
{
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);
};
return pdns::trace::decode<KeyValueList, KeyValue>(reader);
}
-}
+void extractOTraceIDs(const EDNSOptionViewMap& map, pdns::trace::Span& span);
+
+} // namespace pdns::trace
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()) {
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;
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;
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()) {
#include "sstuff.hh"
#include "statbag.hh"
#include <boost/array.hpp>
+#include "protozero-trace.hh"
#ifdef HAVE_LIBCURL
#include "minicurl.hh"
"[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;
}
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)
}
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();
}
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++) {
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);
#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");
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
{
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;