Line data Source code
1 : /**
2 : * @file WIBEthPacketBuilder.cpp Implementation of the WIBEth packet builder.
3 : */
4 : #include "dpdklibs/wibeth/WIBEthPacketBuilder.hpp"
5 :
6 : #include "detdataformats/DAQEthHeader.hpp"
7 :
8 : #include <rte_byteorder.h>
9 : #include <rte_ether.h>
10 : #include <rte_ip.h>
11 : #include <rte_udp.h>
12 :
13 : #include <arpa/inet.h>
14 : #include <netinet/in.h>
15 :
16 : #include <cstdint>
17 : #include <cstring>
18 : #include <stdexcept>
19 : #include <string>
20 :
21 : namespace dunedaq::dpdklibs::wibeth {
22 :
23 : rte_be32_t
24 8 : parse_ipv4_addr(const std::string& addr)
25 : {
26 8 : in_addr parsed{};
27 8 : if (inet_pton(AF_INET, addr.c_str(), &parsed) != 1) {
28 0 : throw std::runtime_error("Invalid IPv4 address: " + addr);
29 : }
30 8 : return static_cast<rte_be32_t>(parsed.s_addr);
31 : }
32 :
33 : void
34 16 : patch_daq_header(void* wibeth_frame, const HeaderPatch& patch)
35 : {
36 : // The frame is at Ethernet offset 42, which does not satisfy the 8-byte
37 : // alignment of DAQEthHeader. Patch an aligned local copy and copy it back
38 : // rather than writing 64-bit bitfields through an unaligned pointer.
39 16 : dunedaq::detdataformats::DAQEthHeader daq_header;
40 16 : std::memcpy(&daq_header, wibeth_frame, sizeof(daq_header));
41 16 : if (patch.set_det_id) {
42 12 : daq_header.det_id = patch.det_id;
43 : }
44 16 : if (patch.set_seq_id) {
45 12 : daq_header.seq_id = patch.seq_id % 4096;
46 : }
47 16 : if (patch.force_block_length) {
48 15 : daq_header.block_length = kWIBEthBlockLength;
49 : }
50 16 : std::memcpy(wibeth_frame, &daq_header, sizeof(daq_header));
51 16 : }
52 :
53 : void
54 16 : construct_packet(void* ethernet_packet, const void* wibeth_frame, const PacketConfig& cfg, const HeaderPatch& patch)
55 : {
56 : // Build the 42-byte Ethernet/IPv4/UDP header in a local object and memcpy it
57 : // into place. The destination may have any alignment (mbuf data offset, test
58 : // buffer), so caller storage is never written through a typed pointer.
59 : // Value-initialization covers the whole object; the packed header type has no
60 : // padding.
61 16 : dunedaq::dpdklibs::udp::ipv4_udp_packet_hdr header{};
62 :
63 16 : header.eth_hdr.dst_addr = cfg.dst_mac;
64 16 : header.eth_hdr.src_addr = cfg.src_mac;
65 16 : header.eth_hdr.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
66 :
67 16 : header.ipv4_hdr.version_ihl = (4 << 4) | (kIPv4HeaderBytes / 4);
68 16 : header.ipv4_hdr.type_of_service = 0;
69 16 : header.ipv4_hdr.total_length = rte_cpu_to_be_16(static_cast<std::uint16_t>(kIPv4TotalBytes));
70 16 : header.ipv4_hdr.packet_id = rte_cpu_to_be_16(cfg.packet_id);
71 16 : header.ipv4_hdr.fragment_offset = rte_cpu_to_be_16(0);
72 16 : header.ipv4_hdr.time_to_live = 64;
73 16 : header.ipv4_hdr.next_proto_id = IPPROTO_UDP;
74 16 : header.ipv4_hdr.hdr_checksum = 0;
75 16 : header.ipv4_hdr.src_addr = cfg.src_ip;
76 16 : header.ipv4_hdr.dst_addr = cfg.dst_ip;
77 16 : header.ipv4_hdr.hdr_checksum = rte_ipv4_cksum(&header.ipv4_hdr);
78 :
79 16 : header.udp_hdr.src_port = rte_cpu_to_be_16(cfg.src_port);
80 16 : header.udp_hdr.dst_port = rte_cpu_to_be_16(cfg.dst_port);
81 16 : header.udp_hdr.dgram_len = rte_cpu_to_be_16(static_cast<std::uint16_t>(kUDPDatagramBytes));
82 16 : header.udp_hdr.dgram_cksum = 0;
83 :
84 16 : std::memcpy(ethernet_packet, &header, kPacketHeaderBytes);
85 :
86 16 : auto* frame = static_cast<std::uint8_t*>(ethernet_packet) + kPacketHeaderBytes;
87 16 : std::memcpy(frame, wibeth_frame, kWIBEthFrameBytes);
88 16 : patch_daq_header(frame, patch);
89 16 : }
90 :
91 : } // namespace dunedaq::dpdklibs::wibeth
|