srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/include/wireframe/packet_diagnostics.hpp
blob: 4b9b0c6533d7160b28a9373b77fd51de1edd1eef (plain) (blame)
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
#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