1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
#include <doctest/doctest.h>
#include <pcap.h>
#include <unistd.h>
#include <cstdio>
#include <string>
#include <vector>
#include "packeteer/capture_session.hpp"
#include "packeteer/pcapng/writer.hpp"
using namespace packeteer;
namespace {
// A real, named pcapng file on disk - CaptureSession::open() takes a
// path, not a FILE*, so a std::tmpfile() (unnamed) doesn't work here
// the way it does in test_pcapng.cpp. Cleans itself up via RAII.
struct TempPcapngFile {
std::string path;
explicit TempPcapngFile(int link_type, const std::vector<std::vector<unsigned char>>& packets) {
char path_template[] = "/tmp/packeteer_test_XXXXXX";
int fd = mkstemp(path_template);
REQUIRE(fd != -1);
path = path_template;
std::FILE* f = fdopen(fd, "wb");
REQUIRE(f != nullptr);
pcapng::Writer writer(f);
writer.write_section_header();
writer.write_interface_description(65535, static_cast<std::uint16_t>(link_type));
std::uint32_t ts = 1700000000;
for (const auto& packet : packets) {
writer.write_packet(0, ts++, 0, packet,
static_cast<std::uint32_t>(packet.size()));
}
std::fclose(f);
}
~TempPcapngFile() { std::remove(path.c_str()); }
};
} // namespace
TEST_CASE("is_supported_datalink accepts EN10MB and RAW, rejects others") {
CHECK(is_supported_datalink(DLT_EN10MB));
CHECK(is_supported_datalink(DLT_RAW));
CHECK_FALSE(is_supported_datalink(DLT_IEEE802_11));
}
TEST_CASE("CaptureSession::open reports an error for a nonexistent device, without needing root") {
CaptureSession session;
CaptureSessionOptions options;
options.device = "this-device-does-not-exist-0xdeadbeef";
auto err = session.open(options);
REQUIRE(err.has_value());
CHECK_FALSE(err->empty());
}
TEST_CASE("CaptureSession::stats returns nullopt before open()") {
CaptureSession session;
CHECK_FALSE(session.stats().has_value());
}
TEST_CASE("CaptureSession::open replays a pcapng file without needing root or a live device") {
TempPcapngFile file(DLT_EN10MB, {{0xDE, 0xAD}, {0xBE, 0xEF}});
CaptureSession session;
CaptureSessionOptions options;
options.replay_input_path = file.path;
CHECK_FALSE(session.open(options).has_value());
CHECK(session.is_replay());
CHECK(session.datalink() == DLT_EN10MB);
CHECK(session.device() == file.path);
CHECK_FALSE(session.stats().has_value()); // pcap_stats() needs a live handle; replay has none
}
TEST_CASE("CaptureSession::open rejects combining -r (replay) with -f (capture filter)") {
TempPcapngFile file(DLT_EN10MB, {{0x01}});
CaptureSession session;
CaptureSessionOptions options;
options.replay_input_path = file.path;
options.filter_expr = "tcp";
auto err = session.open(options);
REQUIRE(err.has_value());
CHECK(err->find("-r") != std::string::npos);
}
TEST_CASE("CaptureSession::open reports an error for a nonexistent replay file") {
CaptureSession session;
CaptureSessionOptions options;
options.replay_input_path = "/tmp/this-file-does-not-exist-0xdeadbeef.pcapng";
auto err = session.open(options);
REQUIRE(err.has_value());
CHECK_FALSE(err->empty());
}
TEST_CASE("CaptureSession::open reports an error for a pcapng file with no packets") {
TempPcapngFile file(DLT_EN10MB, {}); // just SHB + IDB, no EPBs
CaptureSession session;
CaptureSessionOptions options;
options.replay_input_path = file.path;
auto err = session.open(options);
REQUIRE(err.has_value());
}
TEST_CASE("replayed packets reach the CaptureQueue in order, and the thread stops on its own") {
TempPcapngFile file(DLT_RAW, {{0x01, 0x02}, {0x03, 0x04}, {0x05, 0x06}});
CaptureSession session;
CaptureSessionOptions options;
options.replay_input_path = file.path;
REQUIRE_FALSE(session.open(options).has_value());
CHECK(session.datalink() == DLT_RAW);
CaptureQueue queue(4096);
auto capture_thread = session.start_capture_thread(queue);
std::vector<unsigned char> first_bytes;
int count = 0;
while (auto packet = queue.pop()) {
if (count == 0) first_bytes = packet->data;
++count;
}
capture_thread.join();
CHECK(count == 3);
REQUIRE(first_bytes.size() == 2);
CHECK(first_bytes[0] == 0x01);
}
|