14template <
typename U
intType>
15auto read_le_or_be(std::span<const std::byte> data, std::size_t offset,
bool swapped) -> UintType {
17 std::memcpy(&value, data.data() + offset,
sizeof(UintType));
18 return swapped ? utils::swap_bytes(value) : value;
21template <
typename U
intType>
22auto read_net(std::span<const std::byte> data, std::size_t offset) -> UintType {
24 std::memcpy(&value, data.data() + offset,
sizeof(UintType));
25 return utils::from_big_endian(value);
29constexpr std::uint32_t LINKTYPE_ETHERNET = 1;
30constexpr std::uint32_t LINKTYPE_RAW = 101;
31constexpr std::uint32_t LINKTYPE_LINUX_SLL = 113;
33constexpr std::uint16_t ETHERTYPE_IPV4 = 0x0800;
34constexpr std::uint16_t ETHERTYPE_IPV6 = 0x86DD;
35constexpr std::uint16_t ETHERTYPE_VLAN = 0x8100;
37constexpr std::uint8_t IP_PROTOCOL_UDP = 17;
39constexpr std::size_t ETHERNET_HEADER_SIZE = 14;
40constexpr std::size_t VLAN_TAG_SIZE = 4;
41constexpr std::size_t LINUX_SLL_HEADER_SIZE = 16;
42constexpr std::size_t IPV6_HEADER_SIZE = 40;
43constexpr std::size_t UDP_HEADER_SIZE = 8;
45constexpr std::uint32_t PCAP_MAGIC = 0xA1B2C3D4;
46constexpr std::uint32_t PCAP_MAGIC_SWAPPED = 0xD4C3B2A1;
47constexpr std::uint32_t PCAP_MAGIC_NANO = 0xA1B23C4D;
48constexpr std::uint32_t PCAP_MAGIC_NANO_SWP = 0x4D3CB2A1;
49constexpr std::uint32_t PCAPNG_BLOCK_SHB = 0x0A0D0D0A;
54auto strip_link_layer(std::span<const std::byte> frame, std::uint32_t link_type)
55 -> std::optional<std::span<const std::byte>> {
56 if (link_type == LINKTYPE_ETHERNET) {
57 if (frame.size() < ETHERNET_HEADER_SIZE) {
60 std::size_t header = ETHERNET_HEADER_SIZE;
61 auto ethertype = read_net<std::uint16_t>(frame, 12);
62 while (ethertype == ETHERTYPE_VLAN && frame.size() >= header + VLAN_TAG_SIZE) {
63 ethertype = read_net<std::uint16_t>(frame, header + 2);
64 header += VLAN_TAG_SIZE;
66 if (ethertype != ETHERTYPE_IPV4 && ethertype != ETHERTYPE_IPV6) {
69 return frame.subspan(header);
71 if (link_type == LINKTYPE_LINUX_SLL) {
72 if (frame.size() < LINUX_SLL_HEADER_SIZE) {
75 return frame.subspan(LINUX_SLL_HEADER_SIZE);
77 if (link_type != LINKTYPE_RAW) {
88 if (capture.size() <
sizeof(std::uint32_t)) {
91 std::uint32_t magic {};
92 std::memcpy(&magic, capture.data(),
sizeof(magic));
94 if (magic == PCAP_MAGIC || magic == PCAP_MAGIC_NANO) {
95 return read_classic_pcap(capture,
false);
97 if (magic == PCAP_MAGIC_SWAPPED || magic == PCAP_MAGIC_NANO_SWP) {
98 return read_classic_pcap(capture,
true);
100 if (magic == PCAPNG_BLOCK_SHB) {
101 return read_pcapng(capture);
107 std::ifstream file {path, std::ios::binary};
111 std::vector<char> raw {std::istreambuf_iterator<char> {file}, {}};
112 std::vector<std::byte> bytes(raw.size());
113 std::memcpy(bytes.data(), raw.data(), raw.size());
114 return read(std::span<const std::byte> {bytes});
117auto PcapReader::read_classic_pcap(std::span<const std::byte> capture,
bool swapped) ->
bool {
118 constexpr std::size_t GLOBAL_HEADER_SIZE = 24;
119 constexpr std::size_t NETWORK_OFFSET = 20;
120 constexpr std::size_t RECORD_HEADER_SIZE = 16;
121 constexpr std::size_t INCL_LEN_OFFSET = 8;
123 if (capture.size() < GLOBAL_HEADER_SIZE) {
126 const auto link_type = read_le_or_be<std::uint32_t>(capture, NETWORK_OFFSET, swapped);
128 std::size_t offset = GLOBAL_HEADER_SIZE;
129 while (offset + RECORD_HEADER_SIZE <= capture.size()) {
130 const auto incl_len =
131 read_le_or_be<std::uint32_t>(capture, offset + INCL_LEN_OFFSET, swapped);
132 offset += RECORD_HEADER_SIZE;
133 if (offset + incl_len > capture.size()) {
136 handle_frame(capture.subspan(offset, incl_len), link_type);
142auto PcapReader::read_pcapng(std::span<const std::byte> capture) ->
bool {
143 constexpr std::size_t BLOCK_HEADER_SIZE = 8;
144 constexpr std::size_t BOM_OFFSET = 8;
145 constexpr std::uint32_t BOM_LITTLE = 0x1A2B3C4D;
146 constexpr std::uint32_t BLOCK_IDB = 0x00000001;
147 constexpr std::uint32_t BLOCK_SPB = 0x00000003;
148 constexpr std::uint32_t BLOCK_EPB = 0x00000006;
150 bool swapped =
false;
151 std::vector<std::uint32_t> interface_link_types;
153 std::size_t offset = 0;
154 while (offset + BLOCK_HEADER_SIZE <= capture.size()) {
155 std::uint32_t block_type {};
156 std::memcpy(&block_type, capture.data() + offset,
sizeof(block_type));
160 const bool block_swapped = (block_type == PCAPNG_BLOCK_SHB) ?
false : swapped;
162 const auto total_length =
163 read_le_or_be<std::uint32_t>(capture, offset +
sizeof(std::uint32_t), block_swapped);
164 if (total_length < BLOCK_HEADER_SIZE +
sizeof(std::uint32_t) ||
165 offset + total_length > capture.size()) {
169 if (block_type == PCAPNG_BLOCK_SHB) {
170 const auto bom = read_net<std::uint32_t>(capture, offset + BOM_OFFSET);
174 std::uint32_t bom_native {};
175 std::memcpy(&bom_native, capture.data() + offset + BOM_OFFSET,
sizeof(bom_native));
176 swapped = (bom_native != BOM_LITTLE);
178 interface_link_types.clear();
179 }
else if (block_type == BLOCK_IDB) {
181 const auto link_type =
182 read_le_or_be<std::uint16_t>(capture, offset + BLOCK_HEADER_SIZE, swapped);
183 interface_link_types.push_back(link_type);
184 }
else if (block_type == BLOCK_EPB) {
185 constexpr std::size_t EPB_IFACE_OFFSET = BLOCK_HEADER_SIZE;
186 constexpr std::size_t EPB_CAPLEN_OFFSET = BLOCK_HEADER_SIZE + 12;
187 constexpr std::size_t EPB_DATA_OFFSET = BLOCK_HEADER_SIZE + 20;
193 if (total_length >= EPB_DATA_OFFSET +
sizeof(std::uint32_t)) {
195 read_le_or_be<std::uint32_t>(capture, offset + EPB_IFACE_OFFSET, swapped);
197 read_le_or_be<std::uint32_t>(capture, offset + EPB_CAPLEN_OFFSET, swapped);
198 if (offset + EPB_DATA_OFFSET + cap_len <= capture.size() &&
199 iface < interface_link_types.size()) {
201 capture.subspan(offset + EPB_DATA_OFFSET, cap_len),
202 interface_link_types[iface]
206 }
else if (block_type == BLOCK_SPB) {
207 constexpr std::size_t SPB_DATA_OFFSET = BLOCK_HEADER_SIZE + 4;
209 if (total_length >= SPB_DATA_OFFSET +
sizeof(std::uint32_t) &&
210 !interface_link_types.empty()) {
211 const std::size_t data_len = total_length - SPB_DATA_OFFSET -
sizeof(std::uint32_t);
213 capture.subspan(offset + SPB_DATA_OFFSET, data_len),
214 interface_link_types.front()
219 offset += total_length;
224auto PcapReader::handle_frame(std::span<const std::byte> frame, std::uint32_t link_type) ->
void {
225 const auto network = strip_link_layer(frame, link_type);
226 if (!network.has_value()) {
229 const auto payload = extract_udp_payload(*network);
230 if (!payload.has_value()) {
234 m_mold.decode_packet(*payload);
237auto PcapReader::extract_udp_payload(std::span<const std::byte> network
238)
const -> std::optional<std::span<const std::byte>> {
239 if (network.empty()) {
244 const auto version =
static_cast<std::uint8_t
>(network[0]) >> 4;
245 std::uint8_t protocol {0};
246 std::size_t ip_header_len {0};
248 ip_header_len =
static_cast<std::size_t
>(
static_cast<std::uint8_t
>(network[0]) & 0x0F) * 4U;
249 if (network.size() < ip_header_len || ip_header_len < 20) {
252 protocol =
static_cast<std::uint8_t
>(network[9]);
253 }
else if (version == 6) {
254 if (network.size() < IPV6_HEADER_SIZE) {
257 protocol =
static_cast<std::uint8_t
>(network[6]);
258 ip_header_len = IPV6_HEADER_SIZE;
262 if (protocol != IP_PROTOCOL_UDP) {
266 const std::span<const std::byte> udp = network.subspan(ip_header_len);
267 if (udp.size() < UDP_HEADER_SIZE) {
270 const auto dst_port = read_net<std::uint16_t>(udp, 2);
271 if (m_port_filter.has_value() && dst_port != *m_port_filter) {
274 const auto udp_length = read_net<std::uint16_t>(udp, 4);
275 std::size_t payload_len = udp.size() - UDP_HEADER_SIZE;
276 if (udp_length >= UDP_HEADER_SIZE) {
277 payload_len = std::min<std::size_t>(payload_len, udp_length - UDP_HEADER_SIZE);
279 return udp.subspan(UDP_HEADER_SIZE, payload_len);
PcapReader(MessageCallback callback)
Constructs a reader that forwards each decoded ITCH message to callback.
auto read_file(const std::string &path) -> bool
Reads and decodes a capture file from disk.
auto read(std::span< const std::byte > capture) -> bool
Decodes an in-memory capture buffer.
std::function< void(const Message &)> MessageCallback
The signature for the callback function used in streaming parse methods.
In-house reader for classic pcap and pcapng capture files carrying MoldUDP64/ITCH traffic.