Skip to content

Commit

Permalink
Merge pull request #185 from rtobar/pcap-linux-cooked-mode
Browse files Browse the repository at this point in the history
Add Linux "cooked" mode support for pcap dump reader
  • Loading branch information
bmerry committed Jul 11, 2022
2 parents f2b0696 + 697d757 commit d560597
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 14 deletions.
29 changes: 29 additions & 0 deletions include/spead2/common_raw_packet.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,26 @@ class ethernet_frame : public packet_buffer
ipv4_packet payload_ipv4() const;
};

/* Wraps a block of contiguous data and provides access to the linux sll
* frame header fields.
*/
class linux_sll_frame : public packet_buffer
{
public:
static constexpr std::size_t min_size = 16;

linux_sll_frame() = default;
linux_sll_frame(void *ptr, std::size_t size);

SPEAD2_DECLARE_FIELD_BE(0, std::uint16_t, packet_type)
SPEAD2_DECLARE_FIELD_BE(2, std::uint16_t, arphrd_type)
SPEAD2_DECLARE_FIELD_BE(4, std::uint16_t, link_layer_address_length)
SPEAD2_DECLARE_FIELD(6, std::uint64_t, link_layer_address,)
SPEAD2_DECLARE_FIELD_BE(14, std::uint16_t, protocol_type)

ipv4_packet payload_ipv4() const;
};

#undef SPEAD2_DECLARE_FIELD

class packet_type_error : public std::runtime_error
Expand All @@ -203,6 +223,15 @@ class packet_type_error : public std::runtime_error
*/
packet_buffer udp_from_ethernet(void *ptr, size_t size);

/**
* Inspect a Linux SLL encapsulation frame (used when listening to the "any"
* device) to extract the UDP4 payload, with sanity checks.
*
* @throws length_error if any length fields are invalid
* @throws packet_type_error if there are other problems e.g. it is not an IPv4 packet
*/
packet_buffer udp_from_linux_sll(void *ptr, size_t size);

} // namespace spead2

#endif // SPEAD2_COMMON_RAW_PACKET_H
3 changes: 3 additions & 0 deletions include/spead2/recv_udp_pcap.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <cstdint>
#include <string>
#include <pcap/pcap.h>
#include <spead2/common_raw_packet.h>
#include <spead2/recv_reader.h>
#include <spead2/recv_udp_base.h>
#include <spead2/recv_stream.h>
Expand All @@ -42,7 +43,9 @@ namespace recv
class udp_pcap_file_reader : public udp_reader_base
{
private:
using udp_unpacker = packet_buffer (*)(void *, size_t);
pcap_t *handle;
udp_unpacker udp_from_frame;

void run();

Expand Down
35 changes: 31 additions & 4 deletions src/common_raw_packet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,14 +248,29 @@ ipv4_packet ethernet_frame::payload_ipv4() const
return ipv4_packet(data() + min_size, size() - min_size);
}

packet_buffer udp_from_ethernet(void *ptr, size_t size)
/////////////////////////////////////////////////////////////////////////////

linux_sll_frame::linux_sll_frame(void *ptr, std::size_t size)
: packet_buffer(ptr, size)
{
ethernet_frame eth(ptr, size);
if (eth.ethertype_be() != htobe(ipv4_packet::ethertype))
if (size < min_size)
throw std::length_error("packet is to small to be a Linux sll frame");
}

ipv4_packet linux_sll_frame::payload_ipv4() const
{
// TODO: handle VLAN tags
return ipv4_packet(data() + min_size, size() - min_size);
}

/////////////////////////////////////////////////////////////////////////////

static packet_buffer udp_from_ipv4(std::uint16_t ethertype_be, const ipv4_packet &ipv4)
{
if (ethertype_be != htobe(ipv4_packet::ethertype))
throw packet_type_error("Frame has wrong ethernet type (VLAN tagging?), discarding");
else
{
ipv4_packet ipv4 = eth.payload_ipv4();
if (ipv4.version() != 4)
throw packet_type_error("Frame is not IPv4, discarding");
else if (ipv4.is_fragment())
Expand All @@ -267,4 +282,16 @@ packet_buffer udp_from_ethernet(void *ptr, size_t size)
}
}

packet_buffer udp_from_ethernet(void *ptr, size_t size)
{
ethernet_frame eth(ptr, size);
return udp_from_ipv4(eth.ethertype_be(), eth.payload_ipv4());
}

packet_buffer udp_from_linux_sll(void *ptr, size_t size)
{
linux_sll_frame frame(ptr, size);
return udp_from_ipv4(frame.protocol_type_be(), frame.payload_ipv4());
}

} // namespace spead2
18 changes: 17 additions & 1 deletion src/recv_udp_pcap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <spead2/common_features.h>
#if SPEAD2_USE_PCAP
#include <cassert>
#include <cstdint>
#include <string>
#include <spead2/recv_reader.h>
Expand All @@ -29,6 +30,15 @@
#include <spead2/common_raw_packet.h>
#include <spead2/common_logging.h>

// These are defined in pcap/dlt.h for libpcap >= 1.8.0, but in pcap/bfp.h otherwise
// We define them here to avoid having to guess which file to include
#ifndef DLT_EN10MB
#define DLT_EN10MB 1
#endif
#ifndef DLT_LINUX_SLL
#define DLT_LINUX_SLL 113
#endif

namespace spead2
{
namespace recv
Expand Down Expand Up @@ -59,7 +69,7 @@ void udp_pcap_file_reader::run()
try
{
void *bytes = const_cast<void *>((const void *) pkt_data);
packet_buffer payload = udp_from_ethernet(bytes, h->len);
packet_buffer payload = udp_from_frame(bytes, h->len);
process_one_packet(state, payload.data(), payload.size(), payload.size());
}
catch (packet_type_error &e)
Expand Down Expand Up @@ -109,6 +119,12 @@ udp_pcap_file_reader::udp_pcap_file_reader(stream &owner, const std::string &fil
throw error;
}
pcap_freecode(&filter);
// The link type used to record this file
auto linktype = pcap_datalink(handle);
assert(linktype != PCAP_ERROR_NOT_ACTIVATED);
if (linktype != DLT_EN10MB && linktype != DLT_LINUX_SLL)
throw packet_type_error("pcap linktype is neither ethernet nor linux sll");
udp_from_frame = (linktype == DLT_EN10MB) ? udp_from_ethernet : udp_from_linux_sll;

// Process the file
get_io_service().post([this] { run(); });
Expand Down
37 changes: 28 additions & 9 deletions src/unittest_raw_packet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,23 @@ namespace spead2
namespace unittest
{

// A packet captured with tcpdump, containing "Hello world\n" in the UDP payload
static const std::uint8_t sample_packet[] =
// Two packets captured with tcpdump with the same "Hello world\n" UDP payload.
// One was captured directly exclusively from an ethernet device, the other from the "any" device.
static const std::uint8_t sample_ethernet_header[] =
{
0x01, 0x00, 0x5e, 0x66, 0xfe, 0x01, 0x1c, 0x1b, 0x0d, 0xe0, 0xd0, 0xfd, 0x08, 0x00, 0x45, 0x00,
0x00, 0x28, 0xae, 0xa3, 0x40, 0x00, 0x01, 0x11, 0xd0, 0xfe, 0x0a, 0x08, 0x02, 0xb3, 0xef, 0x66,
0xfe, 0x01, 0x87, 0x5d, 0x22, 0xb8, 0x00, 0x14, 0xe9, 0xb4, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20,
0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0a
0x01, 0x00, 0x5e, 0x66, 0xfe, 0x01, 0x1c, 0x1b, 0x0d, 0xe0, 0xd0, 0xfd, 0x08, 0x00
};
// Properties of this sample packet
static const std::uint8_t sample_sll_header[] =
{
0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x1c, 0x1b, 0x0d, 0xe0, 0xd0, 0xfd, 0x00, 0x60, 0x08, 0x00
};
static const std::uint8_t sample_ipv4_packet[] =
{
0x45, 0x00, 0x00, 0x28, 0xae, 0xa3, 0x40, 0x00, 0x01, 0x11, 0xd0, 0xfe, 0x0a, 0x08, 0x02, 0xb3,
0xef, 0x66, 0xfe, 0x01, 0x87, 0x5d, 0x22, 0xb8, 0x00, 0x14, 0xe9, 0xb4, 0x48, 0x65, 0x6c, 0x6c,
0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0a
};
// Properties of these sample packets
static const mac_address source_mac = {{0x1c, 0x1b, 0x0d, 0xe0, 0xd0, 0xfd}};
static const mac_address destination_mac = {{0x01, 0x00, 0x5e, 0x66, 0xfe, 0x01}};
static const auto source_address = boost::asio::ip::address_v4::from_string("10.8.2.179");
Expand All @@ -49,13 +57,18 @@ static const std::uint16_t ipv4_checksum = 0xd0fe;
static const std::uint16_t udp_checksum = 0xe9b4;
static const std::string sample_payload = "Hello world\n";


struct packet_data
{
std::array<std::uint8_t, sizeof(sample_packet)> data;
std::array<std::uint8_t, sizeof(sample_ethernet_header) + sizeof(sample_ipv4_packet)> data;
std::array<std::uint8_t, sizeof(sample_sll_header) + sizeof(sample_ipv4_packet)> sll_data;

packet_data()
{
std::memcpy(data.data(), sample_packet, sizeof(sample_packet));
std::memcpy(data.data(), sample_ethernet_header, sizeof(sample_ethernet_header));
std::memcpy(data.data() + sizeof(sample_ethernet_header), sample_ipv4_packet, sizeof(sample_ipv4_packet));
std::memcpy(sll_data.data(), sample_sll_header, sizeof(sample_sll_header));
std::memcpy(sll_data.data() + sizeof(sample_sll_header), sample_ipv4_packet, sizeof(sample_ipv4_packet));
}
};

Expand Down Expand Up @@ -205,6 +218,12 @@ BOOST_AUTO_TEST_CASE(udp_from_ethernet)
BOOST_CHECK_EQUAL(buffer_to_string(payload), sample_payload);
}

BOOST_AUTO_TEST_CASE(udp_from_linux_sll)
{
boost::asio::mutable_buffer payload = spead2::udp_from_linux_sll(sll_data.data(), sll_data.size());
BOOST_CHECK_EQUAL(buffer_to_string(payload), sample_payload);
}

BOOST_AUTO_TEST_CASE(udp_from_ethernet_not_ipv4)
{
ethernet_frame frame(data.data(), data.size());
Expand Down

0 comments on commit d560597

Please sign in to comment.