srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/include/wireframe/packet_diagnostics.hpp
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-05-21 22:24:00 +0200
committersrdusr <[email protected]>2024-05-21 22:24:00 +0200
commitfbedc55d5aa861c381701c9f913b34ee7ab57ec4 (patch)
treed34c3648b61d417f2a5d8690e0eb4a76bd64c943 /include/wireframe/packet_diagnostics.hpp
parente0f4c701028aa81026a17cf9ebfb36112184f4bc (diff)
downloadpacketeer-fbedc55d5aa861c381701c9f913b34ee7ab57ec4.tar.gz
packeteer-fbedc55d5aa861c381701c9f913b34ee7ab57ec4.zip
Add GUI parity for -c/-a, mDNS/SSH dissectors, and two new fuzz harnesses
GUI parity: checksum_status()/reassembled_http_status() moved out of main.cpp into a shared wireframe/packet_diagnostics.hpp so the GUI can show the same -c/-a diagnostics for the selected packet without duplicating the Ethernet/IPv4/TCP walk. Visually verified under Xvfb with the same split-segment scenario used to verify -a on the CLI. Two new L7 dissectors: mDNS (reuses parse_dns outright - RFC 6762 keeps DNS's wire format, just a different port) and SSH's cleartext identification banner. Live-verified against this machine's real sshd and a real DNS-wire-format packet sent to port 5353. Two new fuzz harnesses (fuzz_checksum, fuzz_tcp_reassembly) covering code added here that the original nine harnesses never touched. All 12 run clean across ~90M executions with no crashes. NAMES.md and PLAN.md updated with this round's decisions and naming candidates.
Diffstat (limited to 'include/wireframe/packet_diagnostics.hpp')
-rw-r--r--include/wireframe/packet_diagnostics.hpp92
1 files changed, 92 insertions, 0 deletions
diff --git a/include/wireframe/packet_diagnostics.hpp b/include/wireframe/packet_diagnostics.hpp
new file mode 100644
index 0000000..4b9b0c6
--- /dev/null
+++ b/include/wireframe/packet_diagnostics.hpp
@@ -0,0 +1,92 @@
+#pragma once
+
+#include <optional>
+#include <pcap.h>
+#include <span>
+#include <string>
+
+#include "wireframe/l7/http.hpp"
+#include "wireframe/net/checksum.hpp"
+#include "wireframe/net/ethernet.hpp"
+#include "wireframe/net/ipv4.hpp"
+#include "wireframe/net/tcp.hpp"
+#include "wireframe/net/tcp_reassembly.hpp"
+
+// Checksum validation and TCP stream reassembly are both deliberately
+// kept out of summarize_packet()'s shared per-packet output - see
+// wireframe/net/checksum.hpp and wireframe/net/tcp_reassembly.hpp for
+// why each is opt-in (checksum offload false positives; reassembly's
+// per-flow state and extra per-packet work). Shared between the CLI
+// (-c/-a) and GUI frontends so they don't hand-roll two separate
+// Ethernet/IPv4/TCP walks down to the same byte spans - the same
+// reasoning wireframe::CaptureSession exists for at the setup layer.
+namespace wireframe {
+
+inline std::string checksum_status(std::span<const unsigned char> bytes, int datalink) {
+ std::span<const unsigned char> ip_bytes;
+ if (datalink == DLT_RAW) {
+ ip_bytes = bytes;
+ } else {
+ auto eth = net::parse_ethernet(bytes);
+ if (!eth || eth->header.ethertype != net::kEthertypeIPv4) return "";
+ ip_bytes = eth->payload;
+ }
+ if (ip_bytes.empty() || (ip_bytes[0] >> 4) != 4) return ""; // only IPv4 checksums, for now
+
+ auto ip = net::parse_ipv4(ip_bytes);
+ if (!ip) return "";
+
+ std::size_t header_len = static_cast<std::size_t>(ip->header.ihl) * 4;
+ std::string out = "checksums: IP=";
+ out += net::verify_ipv4_checksum(ip_bytes.first(header_len)) ? "ok" : "BAD";
+
+ using net::ChecksumResult;
+ if (ip->header.protocol == net::kProtoTcp) {
+ auto result = net::verify_tcp_checksum_ipv4(ip->header.src, ip->header.dst, ip->payload);
+ out += result == ChecksumResult::kValid ? " TCP=ok" : " TCP=BAD";
+ } else if (ip->header.protocol == net::kProtoUdp) {
+ auto result = net::verify_udp_checksum_ipv4(ip->header.src, ip->header.dst, ip->payload);
+ out += result == ChecksumResult::kValid ? " UDP=ok"
+ : result == ChecksumResult::kNotPresent ? " UDP=none"
+ : " UDP=BAD";
+ }
+ return out;
+}
+
+inline std::optional<std::string> reassembled_http_status(std::span<const unsigned char> bytes,
+ int datalink,
+ net::TcpReassembler& reassembler) {
+ std::span<const unsigned char> ip_bytes;
+ if (datalink == DLT_RAW) {
+ ip_bytes = bytes;
+ } else {
+ auto eth = net::parse_ethernet(bytes);
+ if (!eth || eth->header.ethertype != net::kEthertypeIPv4) return std::nullopt;
+ ip_bytes = eth->payload;
+ }
+ if (ip_bytes.empty() || (ip_bytes[0] >> 4) != 4) return std::nullopt; // IPv4 only, for now
+
+ auto ip = net::parse_ipv4(ip_bytes);
+ if (!ip || ip->header.protocol != net::kProtoTcp) return std::nullopt;
+
+ auto tcp = net::parse_tcp(ip->payload);
+ if (!tcp) return std::nullopt;
+
+ auto reassembled = reassembler.process_segment(ip->header.src, tcp->header.src_port,
+ ip->header.dst, tcp->header.dst_port,
+ tcp->header.seq, tcp->header.flags,
+ tcp->payload);
+ if (!reassembled) return std::nullopt;
+
+ auto http = net::parse_http(*reassembled);
+ if (!http) return std::nullopt;
+
+ std::string out = "reassembled ";
+ out += http->is_request ? "request: " : "response: ";
+ out += http->method_or_version + " " + http->target_or_status;
+ if (http->host) out += " Host: " + *http->host;
+ out += " (" + std::to_string(reassembled->size()) + " bytes so far)";
+ return out;
+}
+
+} // namespace wireframe