srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/include/wireframe/net/icmp.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/wireframe/net/icmp.hpp')
-rw-r--r--include/wireframe/net/icmp.hpp84
1 files changed, 84 insertions, 0 deletions
diff --git a/include/wireframe/net/icmp.hpp b/include/wireframe/net/icmp.hpp
new file mode 100644
index 0000000..af83916
--- /dev/null
+++ b/include/wireframe/net/icmp.hpp
@@ -0,0 +1,84 @@
+#pragma once
+
+#include <cstdint>
+#include <optional>
+#include <span>
+#include <string>
+
+#include "wireframe/byteio.hpp"
+
+// ICMPv4 (RFC 792) and ICMPv6 (RFC 4443) share the same first-4-byte
+// shape (Type, Code, Checksum) but a completely different type
+// namespace - the same numeric type means something different in each
+// - so they get separate parse functions and separate type-name
+// tables, sharing only the header struct shape. Neither protocol has
+// ports, so this doesn't fit L7Registry's port-keyed dispatch at all;
+// it's handled directly by protocol number in summarize.hpp instead.
+namespace wireframe::net {
+
+struct IcmpHeader {
+ std::uint8_t type;
+ std::uint8_t code;
+ std::optional<std::uint16_t> identifier; // echo request/reply only
+ std::optional<std::uint16_t> sequence; // echo request/reply only
+};
+
+inline std::optional<IcmpHeader> parse_icmpv4(std::span<const unsigned char> bytes) {
+ if (bytes.size() < 4) return std::nullopt;
+
+ IcmpHeader header{};
+ header.type = bytes[0];
+ header.code = bytes[1];
+ if ((header.type == 8 || header.type == 0) && bytes.size() >= 8) { // echo request/reply
+ header.identifier = read_be16(bytes, 4);
+ header.sequence = read_be16(bytes, 6);
+ }
+ return header;
+}
+
+inline std::string icmpv4_type_name(std::uint8_t type) {
+ switch (type) {
+ case 0: return "Echo Reply";
+ case 3: return "Destination Unreachable";
+ case 4: return "Source Quench";
+ case 5: return "Redirect";
+ case 8: return "Echo Request";
+ case 11: return "Time Exceeded";
+ case 12: return "Parameter Problem";
+ case 13: return "Timestamp Request";
+ case 14: return "Timestamp Reply";
+ default: return "type=" + std::to_string(type);
+ }
+}
+
+inline std::optional<IcmpHeader> parse_icmpv6(std::span<const unsigned char> bytes) {
+ if (bytes.size() < 4) return std::nullopt;
+
+ IcmpHeader header{};
+ header.type = bytes[0];
+ header.code = bytes[1];
+ if ((header.type == 128 || header.type == 129) && bytes.size() >= 8) { // echo request/reply
+ header.identifier = read_be16(bytes, 4);
+ header.sequence = read_be16(bytes, 6);
+ }
+ return header;
+}
+
+inline std::string icmpv6_type_name(std::uint8_t type) {
+ switch (type) {
+ case 1: return "Destination Unreachable";
+ case 2: return "Packet Too Big";
+ case 3: return "Time Exceeded";
+ case 4: return "Parameter Problem";
+ case 128: return "Echo Request";
+ case 129: return "Echo Reply";
+ case 133: return "Router Solicitation";
+ case 134: return "Router Advertisement";
+ case 135: return "Neighbor Solicitation";
+ case 136: return "Neighbor Advertisement";
+ case 137: return "Redirect";
+ default: return "type=" + std::to_string(type);
+ }
+}
+
+} // namespace wireframe::net