/src/pdns/pdns/dnsdistdist/dnsdist-opentelemetry.cc
Line | Count | Source |
1 | | /* |
2 | | * This file is part of PowerDNS or dnsdist. |
3 | | * Copyright -- PowerDNS.COM B.V. and its contributors |
4 | | * |
5 | | * This program is free software; you can redistribute it and/or modify |
6 | | * it under the terms of version 2 of the GNU General Public License as |
7 | | * published by the Free Software Foundation. |
8 | | * |
9 | | * In addition, for the avoidance of any doubt, permission is granted to |
10 | | * link this program with OpenSSL and to (re)distribute the binaries |
11 | | * produced as the result of such linking. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
21 | | */ |
22 | | |
23 | | #include "dnsdist-opentelemetry.hh" |
24 | | #include "misc.hh" |
25 | | #include "dnsdist-ecs.hh" |
26 | | |
27 | | #include <memory> |
28 | | #include <vector> |
29 | | |
30 | | #ifndef DISABLE_PROTOBUF |
31 | | #include "protozero-trace.hh" |
32 | | #endif |
33 | | |
34 | | namespace pdns::trace::dnsdist |
35 | | { |
36 | | |
37 | | #ifndef DISABLE_PROTOBUF |
38 | | static const KeyValue hostnameAttr{.key = "hostname", .value = {getHostname().value_or("")}}; |
39 | | #endif |
40 | | |
41 | | TracesData Tracer::getTracesData() |
42 | 0 | { |
43 | | #ifdef DISABLE_PROTOBUF |
44 | | return 0; |
45 | | #else |
46 | 0 | auto traceid = getTraceID(); |
47 | 0 | { |
48 | 0 | auto data = d_data.read_only_lock(); |
49 | 0 | auto otTrace = pdns::trace::TracesData{ |
50 | 0 | .resource_spans = { |
51 | 0 | {.resource = { |
52 | 0 | .attributes = { |
53 | 0 | {"service.name", {"dnsdist"}}, |
54 | 0 | }}, |
55 | 0 | .scope_spans = {{.scope = { |
56 | 0 | .name = "dnsdist/queryFromFrontend", |
57 | 0 | .version = PACKAGE_VERSION, |
58 | 0 | .attributes = {data->d_attributes.cbegin(), data->d_attributes.cend()}, |
59 | 0 | }, |
60 | 0 | .spans = {}}}}}}; |
61 | |
|
62 | 0 | otTrace.resource_spans.at(0).scope_spans.at(0).scope.attributes.push_back(hostnameAttr); |
63 | |
|
64 | 0 | for (auto const& span : data->d_spans) { |
65 | 0 | otTrace.resource_spans.at(0).scope_spans.at(0).spans.push_back( |
66 | 0 | { |
67 | 0 | .trace_id = traceid, |
68 | 0 | .span_id = span.span_id == data->d_oldAndNewRootSpanID.oldID ? data->d_oldAndNewRootSpanID.newID : span.span_id, |
69 | 0 | .parent_span_id = span.parent_span_id == data->d_oldAndNewRootSpanID.oldID ? data->d_oldAndNewRootSpanID.newID : span.parent_span_id, |
70 | 0 | .name = span.name, |
71 | 0 | .kind = pdns::trace::Span::SpanKind::SPAN_KIND_SERVER, |
72 | 0 | .start_time_unix_nano = span.start_time_unix_nano, |
73 | 0 | .end_time_unix_nano = span.end_time_unix_nano, |
74 | 0 | .attributes = span.attributes, |
75 | 0 | }); |
76 | 0 | } |
77 | 0 | return otTrace; |
78 | 0 | } |
79 | 0 | #endif |
80 | 0 | } |
81 | | |
82 | | std::string Tracer::getOTProtobuf() |
83 | 0 | { |
84 | | #ifdef DISABLE_PROTOBUF |
85 | | return 0; |
86 | | #else |
87 | | // TODO: Should we close all spans? |
88 | 0 | return getTracesData().encode(); |
89 | 0 | #endif |
90 | 0 | } |
91 | | |
92 | | SpanID Tracer::addSpan([[maybe_unused]] const std::string& name) |
93 | 0 | { |
94 | | #ifdef DISABLE_PROTOBUF |
95 | | return 0; |
96 | | #else |
97 | 0 | return addSpan(name, getLastSpanID()); |
98 | 0 | #endif |
99 | 0 | } |
100 | | |
101 | | SpanID Tracer::addSpan([[maybe_unused]] const std::string& name, [[maybe_unused]] const SpanID& parentSpanID) |
102 | 0 | { |
103 | | #ifdef DISABLE_PROTOBUF |
104 | | return 0; |
105 | | #else |
106 | 0 | auto spanID = pdns::trace::SpanID::getRandomSpanID(); |
107 | 0 | { |
108 | 0 | auto data = d_data.lock(); |
109 | 0 | data->d_spans.push_back({ |
110 | 0 | .name = name, |
111 | 0 | .span_id = spanID, |
112 | 0 | .parent_span_id = parentSpanID, |
113 | 0 | .start_time_unix_nano = pdns::trace::timestamp(), |
114 | 0 | .end_time_unix_nano = 0, |
115 | 0 | .attributes = {}, |
116 | 0 | }); |
117 | |
|
118 | 0 | data->d_spanIDStack.emplace_back(spanID); |
119 | 0 | } |
120 | 0 | return spanID; |
121 | 0 | #endif |
122 | 0 | } |
123 | | |
124 | | void Tracer::setTraceID([[maybe_unused]] const TraceID& traceID) |
125 | 0 | { |
126 | 0 | #ifndef DISABLE_PROTOBUF |
127 | 0 | d_data.lock()->d_traceid = traceID; |
128 | 0 | #endif |
129 | 0 | } |
130 | | |
131 | | void Tracer::setRootSpanID([[maybe_unused]] const SpanID& spanID) |
132 | 0 | { |
133 | 0 | #ifndef DISABLE_PROTOBUF |
134 | 0 | SpanID oldRootSpanID{pdns::trace::s_emptySpanID}; |
135 | 0 | { |
136 | 0 | auto data = d_data.read_only_lock(); |
137 | 0 | if (const auto& spans = data->d_spans; !spans.empty()) { |
138 | 0 | auto iter = std::find_if(spans.cbegin(), spans.cend(), [](const auto& span) { return span.parent_span_id == pdns::trace::s_emptySpanID; }); |
139 | 0 | if (iter != spans.cend()) { |
140 | 0 | oldRootSpanID = iter->span_id; |
141 | 0 | } |
142 | 0 | } |
143 | 0 | } |
144 | |
|
145 | 0 | if (oldRootSpanID == pdns::trace::s_emptySpanID) { |
146 | 0 | return; |
147 | 0 | } |
148 | | |
149 | 0 | { |
150 | 0 | auto data = d_data.lock(); |
151 | 0 | data->d_oldAndNewRootSpanID.oldID = oldRootSpanID; |
152 | 0 | data->d_oldAndNewRootSpanID.newID = spanID; |
153 | 0 | } |
154 | 0 | #endif |
155 | 0 | } |
156 | | |
157 | | // TODO: Figure out what to do with duplicate keys |
158 | | bool Tracer::setTraceAttribute([[maybe_unused]] const std::string& key, [[maybe_unused]] const AnyValue& value) |
159 | 0 | { |
160 | | #ifdef DISABLE_PROTOBUF |
161 | | // always successful |
162 | | return true; |
163 | | #else |
164 | 0 | d_data.lock()->d_attributes.push_back({key, value}); |
165 | 0 | return true; |
166 | 0 | #endif |
167 | 0 | } |
168 | | |
169 | | void Tracer::closeSpan([[maybe_unused]] const SpanID& spanID) |
170 | 0 | { |
171 | 0 | #ifndef DISABLE_PROTOBUF |
172 | 0 | auto data = d_data.lock(); |
173 | 0 | auto& spans = data->d_spans; |
174 | 0 | auto spanIt = std::find_if( |
175 | 0 | spans.rbegin(), |
176 | 0 | spans.rend(), |
177 | 0 | [&spanID](const miniSpan& span) { return span.span_id == spanID; }); |
178 | 0 | if (spanIt != spans.rend() && spanIt->end_time_unix_nano == 0) { |
179 | 0 | spanIt->end_time_unix_nano = pdns::trace::timestamp(); |
180 | | |
181 | | // Only closers are allowed, so this can never happen |
182 | 0 | assert(!data->d_spanIDStack.empty()); |
183 | 0 | assert(data->d_spanIDStack.back() == spanID); |
184 | 0 | data->d_spanIDStack.pop_back(); |
185 | 0 | } |
186 | 0 | #endif |
187 | 0 | } |
188 | | |
189 | | void Tracer::setRootSpanAttribute([[maybe_unused]] const std::string& key, [[maybe_unused]] const AnyValue& value) |
190 | 0 | { |
191 | 0 | #ifndef DISABLE_PROTOBUF |
192 | 0 | setSpanAttribute(getRootSpanID(), key, value); |
193 | 0 | #endif |
194 | 0 | } |
195 | | |
196 | | // TODO: Figure out what to do with duplicate keys |
197 | | void Tracer::setSpanAttribute([[maybe_unused]] const SpanID& spanid, [[maybe_unused]] const std::string& key, [[maybe_unused]] const AnyValue& value) |
198 | 0 | { |
199 | 0 | #ifndef DISABLE_PROTOBUF |
200 | 0 | auto data = d_data.lock(); |
201 | 0 | auto& spans = data->d_spans; |
202 | 0 | if (auto iter = std::find_if(spans.rbegin(), |
203 | 0 | spans.rend(), |
204 | 0 | [&spanid](const auto& span) { return span.span_id == spanid; }); |
205 | 0 | iter != spans.rend()) { |
206 | 0 | iter->attributes.push_back({key, value}); |
207 | 0 | } |
208 | 0 | #endif |
209 | 0 | } |
210 | | |
211 | | SpanID Tracer::getRootSpanID() |
212 | 0 | { |
213 | | #ifdef DISABLE_PROTOBUF |
214 | | return 0; |
215 | | #else |
216 | 0 | auto data = d_data.read_only_lock(); |
217 | 0 | if (data->d_oldAndNewRootSpanID.newID != pdns::trace::s_emptySpanID) { |
218 | 0 | return data->d_oldAndNewRootSpanID.newID; |
219 | 0 | } |
220 | | |
221 | 0 | if (auto& spans = data->d_spans; !spans.empty()) { |
222 | 0 | auto iter = std::find_if(spans.cbegin(), spans.cend(), [](const auto& span) { return span.parent_span_id == pdns::trace::s_emptySpanID; }); |
223 | 0 | if (iter != spans.cend()) { |
224 | 0 | return iter->span_id; |
225 | 0 | } |
226 | 0 | } |
227 | 0 | return pdns::trace::s_emptySpanID; |
228 | 0 | #endif |
229 | 0 | } |
230 | | |
231 | | SpanID Tracer::getLastSpanID() |
232 | 0 | { |
233 | | #ifdef DISABLE_PROTOBUF |
234 | | return 0; |
235 | | #else |
236 | 0 | auto data = d_data.read_only_lock(); |
237 | 0 | if (data->d_spanIDStack.empty()) { |
238 | 0 | return pdns::trace::s_emptySpanID; |
239 | 0 | } |
240 | 0 | if (data->d_spanIDStack.size() == 1 && data->d_spanIDStack.front() == data->d_oldAndNewRootSpanID.oldID) { |
241 | 0 | return data->d_oldAndNewRootSpanID.newID; |
242 | 0 | } |
243 | 0 | return data->d_spanIDStack.back(); |
244 | 0 | #endif |
245 | 0 | } |
246 | | |
247 | | SpanID Tracer::getLastSpanIDForName([[maybe_unused]] const std::string& name) |
248 | 0 | { |
249 | | #ifdef DISABLE_PROTOBUF |
250 | | return 0; |
251 | | #else |
252 | 0 | auto data = d_data.read_only_lock(); |
253 | 0 | if (auto& spans = data->d_spans; !spans.empty()) { |
254 | 0 | if (auto iter = std::find_if(spans.rbegin(), |
255 | 0 | spans.rend(), |
256 | 0 | [name](const miniSpan& span) { return span.name == name; }); |
257 | 0 | iter != spans.rend()) { |
258 | 0 | return iter->span_id; |
259 | 0 | } |
260 | 0 | } |
261 | 0 | return pdns::trace::s_emptySpanID; |
262 | 0 | #endif |
263 | 0 | } |
264 | | |
265 | | TraceID Tracer::getTraceID() |
266 | 0 | { |
267 | | #ifdef DISABLE_PROTOBUF |
268 | | return 0; |
269 | | #else |
270 | 0 | auto data = d_data.lock(); |
271 | 0 | auto& traceID = data->d_traceid; |
272 | 0 | if (traceID == pdns::trace::s_emptyTraceID) { |
273 | 0 | traceID.makeRandom(); |
274 | 0 | } |
275 | 0 | return traceID; |
276 | 0 | #endif |
277 | 0 | } |
278 | | |
279 | | Tracer::Closer Tracer::getCloser([[maybe_unused]] const SpanID& spanid) |
280 | 0 | { |
281 | | #ifdef DISABLE_PROTOBUF |
282 | | return Tracer::Closer(); |
283 | | #else |
284 | 0 | return {shared_from_this(), spanid}; |
285 | 0 | #endif |
286 | 0 | } |
287 | | |
288 | | Tracer::Closer Tracer::openSpan([[maybe_unused]] const std::string& name) |
289 | 0 | { |
290 | | #ifdef DISABLE_PROTOBUF |
291 | | return Tracer::Closer(); |
292 | | #else |
293 | 0 | auto spanid = addSpan(name); |
294 | 0 | return getCloser(spanid); |
295 | 0 | #endif |
296 | 0 | } |
297 | | |
298 | | Tracer::Closer Tracer::openSpan([[maybe_unused]] const std::string& name, [[maybe_unused]] const SpanID& parentSpanID) |
299 | 0 | { |
300 | | #ifdef DISABLE_PROTOBUF |
301 | | return Tracer::Closer(); |
302 | | #else |
303 | 0 | auto spanid = addSpan(name, parentSpanID); |
304 | 0 | return getCloser(spanid); |
305 | 0 | #endif |
306 | 0 | } |
307 | | |
308 | | SpanID Tracer::Closer::getSpanID() const |
309 | 0 | { |
310 | | #ifdef DISABLE_PROTOBUF |
311 | | return 0; |
312 | | #else |
313 | 0 | return d_spanID; |
314 | 0 | #endif |
315 | 0 | } |
316 | | |
317 | | void Tracer::Closer::setAttribute([[maybe_unused]] const std::string& key, [[maybe_unused]] const AnyValue& value) |
318 | 0 | { |
319 | | #ifdef DISABLE_PROTOBUF |
320 | | return; |
321 | | #else |
322 | 0 | return d_tracer->setSpanAttribute(d_spanID, key, value); |
323 | 0 | #endif |
324 | 0 | } |
325 | | |
326 | | std::vector<uint8_t> makeEDNSTraceParentOption(std::shared_ptr<Tracer> tracer) |
327 | 0 | { |
328 | 0 | std::vector<uint8_t> ret; |
329 | 0 | #ifndef DISABLE_PROTOBUF |
330 | 0 | if (tracer == nullptr) { |
331 | 0 | return ret; |
332 | 0 | } |
333 | 0 | ret.reserve(27); |
334 | 0 | ret.push_back(0); // Version |
335 | 0 | ret.push_back(0); // Reserved |
336 | 0 | auto traceId = tracer->getTraceID(); |
337 | 0 | ret.insert(ret.end(), traceId.begin(), traceId.end()); |
338 | 0 | auto spanId = tracer->getLastSpanID(); |
339 | 0 | ret.insert(ret.end(), spanId.begin(), spanId.end()); |
340 | 0 | ret.push_back(0); // Flags |
341 | 0 | #endif |
342 | 0 | return ret; |
343 | 0 | } |
344 | | |
345 | | bool addTraceparentEdnsOptionToPacketBuffer(PacketBuffer& origBuf, const std::shared_ptr<Tracer>& tracer, const size_t qnameWireLength, const size_t proxyProtocolPayloadSize, const uint16_t traceparentOptionCode, const bool isTCP) |
346 | 0 | { |
347 | 0 | #ifndef DISABLE_PROTOBUF |
348 | 0 | if (tracer == nullptr) { |
349 | 0 | return false; |
350 | 0 | } |
351 | | // buf contains the whole DNS query without PROXY protocol and TCP length header |
352 | 0 | PacketBuffer buf{origBuf.begin() + proxyProtocolPayloadSize + (isTCP ? 2 : 0), origBuf.end()}; |
353 | |
|
354 | 0 | uint16_t optRDPosition; |
355 | 0 | size_t remaining; |
356 | 0 | bool queryHadEdns = ::dnsdist::getEDNSOptionsStart(buf, qnameWireLength, &optRDPosition, &remaining) == 0; |
357 | 0 | if (queryHadEdns) { |
358 | 0 | size_t optLen = buf.size() - optRDPosition - remaining; |
359 | 0 | removeEDNSOptionFromOPT(reinterpret_cast<char*>(buf.data() + optRDPosition), &optLen, traceparentOptionCode); |
360 | 0 | } |
361 | |
|
362 | 0 | auto opt = pdns::trace::dnsdist::makeEDNSTraceParentOption(tracer); |
363 | 0 | bool ednsAdded{false}; |
364 | 0 | bool optionAdded{false}; |
365 | 0 | uint16_t maxEdnsSize = queryHadEdns ? (uint16_t)(buf.at(optRDPosition - 6) << 8) + buf.at(optRDPosition - 5) : 512; |
366 | 0 | setEDNSOption(buf, traceparentOptionCode, std::string(opt.begin(), opt.end()), isTCP ? std::numeric_limits<uint16_t>().max() : maxEdnsSize, ednsAdded, optionAdded); |
367 | |
|
368 | 0 | if (isTCP) { |
369 | 0 | const std::array<uint8_t, 2> sizeBytes{static_cast<uint8_t>(buf.size() / 256), static_cast<uint8_t>(buf.size() % 256)}; |
370 | 0 | buf.insert(buf.begin(), sizeBytes.begin(), sizeBytes.end()); |
371 | 0 | } |
372 | | |
373 | | // Resize the buffer to remove the existing packet, but keep any PROXYv2 data |
374 | 0 | origBuf.resize(proxyProtocolPayloadSize); |
375 | | // Insert the new query into the buffer |
376 | 0 | origBuf.insert(origBuf.end(), buf.begin(), buf.end()); |
377 | |
|
378 | 0 | return ednsAdded; |
379 | | #else |
380 | | return false; |
381 | | #endif |
382 | 0 | } |
383 | | |
384 | | } // namespace pdns::trace::dnsdist |