Skip to content

Commit 05964bd

Browse files
authored
Add support to ignore MongoDB handshaking frames (#1776)
Summary: When end to end testing MongoDB tracing, the parser encountered `OP_MSG` handshaking frames and was not able to parse them. This was due to the old parser searching for a CRUD command in the `OP_MSG` frame's top level key in the payload and `OP_MSG` handshaking frames not containing a key the old parser could interpret. This PR adds support to parse top level handshaking keys and discards those frames at stitching time. It also correctly prefixes `const` variables in the `types.h` file. The motivation to parse these frames instead of immediately ignoring them at parsing time is to account for the different possibilities of responses for a handshaking request. A handshaking request could lead to a `more_to_come` response where each frame in the `more_to_come` response may not be identifiable as a handshaking frame. It's also possible that the response of a handshaking request may contain an `ok` key which can be misidentified as a non handshaking response and left stale in the map of response deques. Identifying all of the handshaking request/response frames at stitching time but not inserting it to the records would ensure that all handshaking frames are cleared from the map and not pushed to the data table. Related issues: #640 Type of change: /kind bug Test Plan: Added a stitcher test Signed-off-by: Kartik Pattaswamy <kpattaswamy@pixielabs.ai>
1 parent 56cc19e commit 05964bd

File tree

4 files changed

+67
-9
lines changed

4 files changed

+67
-9
lines changed

src/stirling/source_connectors/socket_tracer/protocols/mongodb/decode.cc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,16 @@ ParseState ProcessOpMsg(BinaryDecoder* decoder, Frame* frame) {
123123
// The type of all request commands and the response to all find command requests
124124
// will always be the first key.
125125
auto op_msg_type = doc.MemberBegin()->name.GetString();
126-
if ((op_msg_type == insert || op_msg_type == delete_ || op_msg_type == update ||
127-
op_msg_type == find || op_msg_type == cursor)) {
126+
if ((op_msg_type == kInsert || op_msg_type == kDelete || op_msg_type == kUpdate ||
127+
op_msg_type == kFind || op_msg_type == kCursor)) {
128128
frame->op_msg_type = op_msg_type;
129+
130+
} else if (op_msg_type == kHello || op_msg_type == kIsMaster ||
131+
op_msg_type == kIsMasterAlternate) {
132+
// The frame is a handshaking message.
133+
frame->op_msg_type = op_msg_type;
134+
frame->is_handshake = true;
135+
129136
} else {
130137
// The frame is a response message, find the "ok" key and its value.
131138
auto itr = doc.FindMember("ok");

src/stirling/source_connectors/socket_tracer/protocols/mongodb/stitcher.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ RecordsWithErrorCount<mongodb::Record> StitchFrames(
162162
resp_frame.consumed = true;
163163
FlattenSections(&req_frame);
164164
FlattenSections(&resp_frame);
165+
166+
// Ignore stitching the request/response if either one is a handshaking frame.
167+
if (req_frame.is_handshake || resp_frame.is_handshake) {
168+
req_frame.consumed = true;
169+
resp_frame.consumed = true;
170+
break;
171+
}
172+
165173
records.push_back({std::move(req_frame), std::move(resp_frame)});
166174
break;
167175
}

src/stirling/source_connectors/socket_tracer/protocols/mongodb/stitcher_test.cc

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,13 @@ using ::testing::SizeIs;
3737
class MongoDBStitchFramesTest : public ::testing::Test {};
3838

3939
Frame CreateMongoDBFrame(uint64_t ts_ns, int32_t request_id, int32_t response_to, bool more_to_come,
40-
std::string doc = "") {
40+
std::string doc = "", bool is_handshake = false) {
4141
mongodb::Frame frame;
4242
frame.timestamp_ns = ts_ns;
4343
frame.request_id = request_id;
4444
frame.response_to = response_to;
4545
frame.more_to_come = more_to_come;
46+
frame.is_handshake = is_handshake;
4647

4748
mongodb::Section section;
4849
section.documents.push_back(doc);
@@ -343,6 +344,39 @@ TEST_F(MongoDBStitchFramesTest, MissingTailFrameInNResponses) {
343344
EXPECT_THAT(state.stream_order, SizeIs(0));
344345
}
345346

347+
TEST_F(MongoDBStitchFramesTest, VerifyHandshakingMessages) {
348+
absl::flat_hash_map<mongodb::stream_id_t, std::deque<mongodb::Frame>> reqs;
349+
absl::flat_hash_map<mongodb::stream_id_t, std::deque<mongodb::Frame>> resps;
350+
351+
// Add requests to map.
352+
reqs[1].push_back(CreateMongoDBFrame(0, 1, 0, false));
353+
reqs[3].push_back(CreateMongoDBFrame(2, 3, 0, false));
354+
reqs[5].push_back(CreateMongoDBFrame(4, 5, 0, false, "", true)); // Request handshake frame.
355+
reqs[7].push_back(CreateMongoDBFrame(6, 7, 0, false));
356+
357+
// Add responses to map.
358+
resps[1].push_back(CreateMongoDBFrame(1, 2, 1, false));
359+
resps[3].push_back(CreateMongoDBFrame(3, 4, 3, false));
360+
resps[5].push_back(CreateMongoDBFrame(5, 6, 5, false, "", true)); // Response handshake frame.
361+
resps[7].push_back(CreateMongoDBFrame(7, 8, 7, false));
362+
363+
// Add the order in which the transactions's streamID's were found.
364+
State state = {};
365+
state.stream_order.push_back({1, false});
366+
state.stream_order.push_back({3, false});
367+
state.stream_order.push_back({5, false});
368+
state.stream_order.push_back({7, false});
369+
370+
RecordsWithErrorCount<mongodb::Record> result = mongodb::StitchFrames(&reqs, &resps, &state);
371+
EXPECT_EQ(result.error_count, 0);
372+
// There should be 3 records in vector since the stitcher ignores handshaking frames but will
373+
// still consume them successfully.
374+
EXPECT_THAT(result.records, SizeIs(3));
375+
EXPECT_TRUE(AreAllDequesEmpty(reqs));
376+
EXPECT_TRUE(AreAllDequesEmpty(resps));
377+
EXPECT_THAT(state.stream_order, SizeIs(0));
378+
}
379+
346380
} // namespace mongodb
347381
} // namespace protocols
348382
} // namespace stirling

src/stirling/source_connectors/socket_tracer/protocols/mongodb/types.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,17 @@ enum class SectionKind : uint8_t {
6868
};
6969

7070
// Types of OP_MSG requests/responses
71-
constexpr std::string_view insert = "insert";
72-
constexpr std::string_view delete_ = "delete";
73-
constexpr std::string_view update = "update";
74-
constexpr std::string_view find = "find";
75-
constexpr std::string_view cursor = "cursor";
76-
constexpr std::string_view ok = "ok";
71+
constexpr std::string_view kInsert = "insert";
72+
constexpr std::string_view kDelete = "delete";
73+
constexpr std::string_view kUpdate = "update";
74+
constexpr std::string_view kFind = "find";
75+
constexpr std::string_view kCursor = "cursor";
76+
constexpr std::string_view kOk = "ok";
77+
78+
// Types of top level keys for handshaking messages
79+
constexpr std::string_view kHello = "hello";
80+
constexpr std::string_view kIsMaster = "isMaster";
81+
constexpr std::string_view kIsMasterAlternate = "ismaster";
7782

7883
constexpr int32_t kMaxBSONObjSize = 16000000;
7984

@@ -116,6 +121,9 @@ constexpr int32_t kMaxBSONObjSize = 16000000;
116121
* https://github.com/mongodb/specifications/blob/e09b41df206f9efaa36ba4c332c47d04ddb7d6d1/source/message/OP_MSG.rst#command-arguments-as-payload
117122
*
118123
* There can be 0 or more documents in a section of kind 1 without a separator between them.
124+
*
125+
* Information about MongoDB handshaking messages can be found here:
126+
* https://github.com/mongodb/specifications/blob/022fbf64fb36c80b9295ba93acec150c94362767/source/mongodb-handshake/handshake.rst
119127
*/
120128

121129
struct Frame : public FrameBase {
@@ -135,6 +143,7 @@ struct Frame : public FrameBase {
135143
std::string op_msg_type;
136144
std::string frame_body;
137145
uint32_t checksum = 0;
146+
bool is_handshake = false;
138147

139148
bool consumed = false;
140149
size_t ByteSize() const override { return sizeof(Frame); }

0 commit comments

Comments
 (0)