srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-05-28 01:55:00 +0200
committersrdusr <[email protected]>2024-05-28 01:55:00 +0200
commitf8fc8806401596b08779cf8c88da31408c9b0547 (patch)
tree36d873799695bb23ad6bb0989e1b0f05b734041d /tests
parentb565d7d9c47ca1ec5af0effd828431ee96027d60 (diff)
downloadpacketeer-f8fc8806401596b08779cf8c88da31408c9b0547.tar.gz
packeteer-f8fc8806401596b08779cf8c88da31408c9b0547.zip
Add ARP decoding and bring the TUI up to -c/-a parity
ARP had zero treatment until now - its ethertype just fell through summarize_packet's "not IPv4/IPv6" branch, on traffic that appears on essentially every real LAN capture. Scoped to Ethernet/IPv4 addressing (the case that covers virtually all real ARP traffic), with tcpdump's own "who-has X tell Y" / "X is-at Y" phrasing rather than inventing new wording. Live-verified by flushing this machine's real gateway ARP entry and capturing the resulting request/reply on wlp1s0. TUI -c/-a were being parsed into RenderOptions but silently did nothing: run_tui()'s consumer thread had its own loop that never read them, unlike plain-text mode's render_packet(). Fixed to match, and caught a real bug while doing it - pushing up to two rows per packet (summary + reassembly line) against a single pop_front() would let the row deque grow past its cap under sustained -a activity; needed a while loop instead. Verified under tmux against the same split-segment HTTP scenario used to verify -a on the CLI and GUI.
Diffstat (limited to 'tests')
-rw-r--r--tests/test_arp.cpp89
-rw-r--r--tests/test_summarize.cpp20
2 files changed, 106 insertions, 3 deletions
diff --git a/tests/test_arp.cpp b/tests/test_arp.cpp
new file mode 100644
index 0000000..247782b
--- /dev/null
+++ b/tests/test_arp.cpp
@@ -0,0 +1,89 @@
+#include <doctest/doctest.h>
+
+#include <vector>
+
+#include "packeteer/net/arp.hpp"
+
+using namespace packeteer::net;
+
+namespace {
+
+std::vector<unsigned char> arp_request() {
+ return {
+ 0x00, 0x01, // hardware type = Ethernet
+ 0x08, 0x00, // protocol type = IPv4
+ 0x06, // hardware len = 6
+ 0x04, // protocol len = 4
+ 0x00, 0x01, // opcode = request
+ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x01, // sender MAC
+ 10, 0, 0, 1, // sender IP
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target MAC (unknown, all zero)
+ 10, 0, 0, 2, // target IP
+ };
+}
+
+std::vector<unsigned char> arp_reply() {
+ return {
+ 0x00, 0x01,
+ 0x08, 0x00,
+ 0x06,
+ 0x04,
+ 0x00, 0x02, // opcode = reply
+ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x02, // sender MAC (the one who was asked)
+ 10, 0, 0, 2, // sender IP
+ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x01, // target MAC (the original requester)
+ 10, 0, 0, 1, // target IP
+ };
+}
+
+} // namespace
+
+TEST_CASE("parse_arp decodes a request with Ethernet/IPv4 addressing") {
+ auto arp = parse_arp(arp_request());
+ REQUIRE(arp.has_value());
+ CHECK(arp->header.opcode == kArpOpRequest);
+ REQUIRE(arp->sender_ip.has_value());
+ REQUIRE(arp->sender_mac.has_value());
+ REQUIRE(arp->target_ip.has_value());
+ CHECK(arp->sender_ip->bytes == std::array<unsigned char, 4>{10, 0, 0, 1});
+ CHECK(arp->target_ip->bytes == std::array<unsigned char, 4>{10, 0, 0, 2});
+ CHECK(arp->sender_mac->bytes == std::array<unsigned char, 6>{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x01});
+}
+
+TEST_CASE("parse_arp decodes a reply") {
+ auto arp = parse_arp(arp_reply());
+ REQUIRE(arp.has_value());
+ CHECK(arp->header.opcode == kArpOpReply);
+ REQUIRE(arp->sender_ip.has_value());
+ CHECK(arp->sender_ip->bytes == std::array<unsigned char, 4>{10, 0, 0, 2});
+}
+
+TEST_CASE("parse_arp rejects a truncated header") {
+ std::vector<unsigned char> bytes(5, 0);
+ CHECK_FALSE(parse_arp(bytes).has_value());
+}
+
+TEST_CASE("parse_arp decodes the header but skips addresses for non-Ethernet/IPv4") {
+ std::vector<unsigned char> bytes = {
+ 0x00, 0x06, // hardware type = IEEE 802 (arbitrary, just not the common case)
+ 0x08, 0x00,
+ 0x08, // hardware len = 8, not 6
+ 0x04,
+ 0x00, 0x01,
+ };
+ auto arp = parse_arp(bytes);
+ REQUIRE(arp.has_value());
+ CHECK(arp->header.hardware_len == 8);
+ CHECK_FALSE(arp->sender_ip.has_value());
+ CHECK_FALSE(arp->sender_mac.has_value());
+}
+
+TEST_CASE("parse_arp treats a header claiming Ethernet/IPv4 but too short to hold it as header-only") {
+ std::vector<unsigned char> bytes = {
+ 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01,
+ 0xaa, 0xbb, 0xcc, // truncated sender MAC
+ };
+ auto arp = parse_arp(bytes);
+ REQUIRE(arp.has_value());
+ CHECK_FALSE(arp->sender_ip.has_value());
+}
diff --git a/tests/test_summarize.cpp b/tests/test_summarize.cpp
index ac6f1c0..27e0dd3 100644
--- a/tests/test_summarize.cpp
+++ b/tests/test_summarize.cpp
@@ -163,13 +163,27 @@ TEST_CASE("summarize_packet reports a truncated Ethernet frame without decoding
CHECK(line == "[10 bytes] truncated ethernet frame");
}
-TEST_CASE("summarize_packet stops after the Ethernet line for a non-IP ethertype") {
+TEST_CASE("summarize_packet stops after the Ethernet line for an unhandled ethertype") {
std::vector<unsigned char> bytes = {
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
- 0x08, 0x06, // ARP, not IPv4/IPv6
+ 0x88, 0xCC, // LLDP, not IPv4/IPv6/ARP
};
auto line = packeteer::summarize_packet(bytes, DLT_EN10MB);
- CHECK(line == "ETH aa:bb:cc:dd:ee:ff -> 11:22:33:44:55:66 ethertype=0x0806");
+ CHECK(line == "ETH aa:bb:cc:dd:ee:ff -> 11:22:33:44:55:66 ethertype=0x88cc");
+}
+
+TEST_CASE("summarize_packet decodes an ARP request end to end") {
+ std::vector<unsigned char> bytes = {
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
+ 0x08, 0x06, // ARP
+ 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01,
+ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 10, 0, 0, 1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 10, 0, 0, 2,
+ };
+ auto line = packeteer::summarize_packet(bytes, DLT_EN10MB);
+ CHECK(line ==
+ "ETH aa:bb:cc:dd:ee:ff -> 11:22:33:44:55:66 ethertype=0x0806 | "
+ "ARP who-has 10.0.0.2 tell 10.0.0.1 (aa:bb:cc:dd:ee:ff)");
}
TEST_CASE("hex_dump_lines produces one line per 16 bytes, with the right byte count") {