#pragma once #include #include #include #include #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 bytes, int datalink) { std::span 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(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 reassembled_http_status(std::span bytes, int datalink, net::TcpReassembler& reassembler) { std::span 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