SatCat5
ptp_dispatch.cc
1 // Copyright 2023-2025 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/ip_dispatch.h>
7 #include <satcat5/ptp_client.h>
8 #include <satcat5/ptp_dispatch.h>
9 #include <satcat5/ptp_interface.h>
10 
15 using satcat5::ptp::DispatchTo;
18 
19 Dispatch::Dispatch(
22  : m_iface(iface)
23  , m_ip(ip)
24  , m_callback(0)
25  , m_reply_mac(satcat5::eth::MACADDR_NONE)
26  , m_stored_mac(satcat5::eth::MACADDR_NONE)
27  , m_reply_vtag(satcat5::eth::VTAG_NONE)
28  , m_stored_vtag(satcat5::eth::VTAG_NONE)
29  , m_reply_ip(satcat5::ip::ADDR_NONE)
30  , m_stored_ip(satcat5::ip::ADDR_NONE)
31 {
32  m_iface->ptp_callback(this);
33 }
34 
35 #if SATCAT5_ALLOW_DELETION
36 Dispatch::~Dispatch() {
37  m_iface->ptp_callback(0);
38 }
39 #endif
40 
42  DispatchTo addr, unsigned num_bytes, u8 ptp_msg_type)
43 {
44  // Calculate worst-case packet including Eth + IP headers.
45  unsigned max_bytes = num_bytes + 34;
46 
47  // Get the Writable object use to write the message
48  satcat5::io::Writeable* iface = m_iface->ptp_tx_write();
49  if (!iface || iface->get_write_space() < max_bytes) return 0;
50 
51  // Create and write the ethernet header
52  satcat5::eth::Header eth_header = get_dst_eth(addr);
53  eth_header.write_to(iface);
54 
55  // Write IPv4 and UDP headers if this is a L3 PTP message
56  if (eth_header.type == satcat5::eth::ETYPE_IPV4) {
57  satcat5::ip::Addr dst_ip = get_dst_ip(ptp_msg_type, addr);
58 
59  // Write IPv4 Header
60  unsigned udp_bytes = num_bytes + satcat5::udp::HEADER_BYTES;
61  satcat5::ip::Header ip_header =
62  m_ip->next_header(satcat5::ip::PROTO_UDP, dst_ip, udp_bytes);
63  ip_header.write_to(iface);
64 
65  // Write UDP Header
66  // Src and dst ports should match when sending messages.
67  // UDP length field includes both header + contents.
68  satcat5::udp::Port dst_port = get_dst_port(ptp_msg_type);
69  satcat5::udp::Header udp_header = {dst_port, dst_port, (u16)udp_bytes};
70  udp_header.write_to(iface);
71  }
72 
73  // Return the Writeable so the caller can write the PTP body
74  return iface;
75 }
76 
78  m_stored_mac = m_reply_mac;
79  m_stored_vtag = m_reply_vtag;
80  m_stored_ip = m_reply_ip;
81 }
82 
84  const MacAddr& mac, const satcat5::ip::Addr& ip, const VlanTag& vtag)
85 {
86  m_stored_mac = mac;
87  m_stored_vtag = vtag;
88  m_stored_ip = ip;
89 }
90 
92  satcat5::io::Readable* readable = m_iface->ptp_rx_read();
93 
94  // Read the Ethernet frame header and note L2 reply address.
95  satcat5::eth::Header eth_header;
96  eth_header.read_from(readable);
97  m_reply_mac = eth_header.src;
98  m_reply_vtag = eth_header.vtag;
99 
100  // If mac_type is IPv4, then store the L3 reply address.
101  if (eth_header.type == satcat5::eth::ETYPE_IPV4) {
102  // Read IPv4 header and note reply address.
103  satcat5::ip::Header ip_header = { 0 };
104  ip_header.read_from(readable);
105  m_reply_ip = ip_header.src();
106  // Read and discard the UDP header. We've already confirmed
107  // the port numbers in ptp::Interface::ptp_dispatch().
108  satcat5::udp::Header udp_header = satcat5::udp::HEADER_EMPTY;
109  udp_header.read_from(readable);
110  } else {
111  m_reply_ip = satcat5::ip::ADDR_NONE;
112  }
113 
114  // Notify callback with remaining contents, which are the PTP message.
115  if (m_callback) {
116  satcat5::io::LimitedRead limited_read(readable);
117  m_callback->ptp_rcvd(limited_read);
118  }
119 
120  readable->read_finalize();
121 }
122 
123 // A valid IPv4 address indicates an L3 packet; otherwise choose L2.
124 static constexpr MacType infer_etype(const satcat5::ip::Addr& addr) {
125  return (addr == satcat5::ip::ADDR_NONE)
126  ? satcat5::eth::ETYPE_PTP : satcat5::eth::ETYPE_IPV4;
127 }
128 
129 satcat5::eth::Header Dispatch::get_dst_eth(DispatchTo addr) const {
130  // Ethernet header fields = {dst, src, etype, vtag}
131  switch (addr) {
132  case DispatchTo::BROADCAST_L2:
133  return satcat5::eth::Header {
134  satcat5::eth::MACADDR_BROADCAST, m_ip->macaddr(),
135  satcat5::eth::ETYPE_PTP, satcat5::eth::VTAG_NONE};
136  case DispatchTo::BROADCAST_L3:
137  return satcat5::eth::Header {
138  satcat5::eth::MACADDR_BROADCAST, m_ip->macaddr(),
139  satcat5::eth::ETYPE_IPV4, satcat5::eth::VTAG_NONE};
140  case DispatchTo::REPLY:
141  return satcat5::eth::Header {
142  m_reply_mac, m_ip->macaddr(),
143  infer_etype(m_reply_ip), m_reply_vtag};
144  default: // DispatchTo::STORED
145  return satcat5::eth::Header {
146  m_stored_mac, m_ip->macaddr(),
147  infer_etype(m_stored_ip), m_stored_vtag};
148  }
149 }
150 
151 satcat5::udp::Port Dispatch::get_dst_port(u8 ptp_msg_type) const {
152  if (ptp_msg_type == satcat5::ptp::Header::TYPE_SYNC ||
153  ptp_msg_type == satcat5::ptp::Header::TYPE_DELAY_REQ ||
154  ptp_msg_type == satcat5::ptp::Header::TYPE_PDELAY_REQ ||
155  ptp_msg_type == satcat5::ptp::Header::TYPE_PDELAY_RESP) {
156  return satcat5::udp::PORT_PTP_EVENT;
157  } else {
158  return satcat5::udp::PORT_PTP_GENERAL;
159  }
160 }
161 
162 satcat5::ip::Addr Dispatch::get_dst_ip(u8 ptp_msg_type, DispatchTo addr) const {
163  switch (addr) {
164  case DispatchTo::REPLY: return m_reply_ip;
165  case DispatchTo::STORED: return m_stored_ip;
166  default: return (ptp_msg_type == satcat5::ptp::Header::TYPE_PDELAY_REQ)
167  ? satcat5::ip::ADDR_PTP_PDELAY : satcat5::ip::ADDR_PTP_PRIMARY;
168  }
169 }
Limited read of next N bytes.
Definition: io_readable.h:255
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
virtual void read_finalize()
Consume any remaining bytes in this frame, if applicable.
Definition: io_readable.cc:270
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
virtual unsigned get_write_space() const =0
How many bytes can be written without blocking?
Protocol handler and dispatch unit for Internet Protocol v4 (IPv4).
Definition: ip_dispatch.h:43
satcat5::ip::Header next_header(u8 protocol, const satcat5::ip::Addr &dst, unsigned inner_bytes, u8 ttl=SATCAT5_IP_TTL)
Create a basic IPv4 header with the specified information.
Definition: ip_dispatch.cc:79
void ptp_rcvd(satcat5::io::LimitedRead &rd)
Dispatch calls this method for each incoming packet.
Definition: ptp_client.cc:223
Dispatch and formatting for L2 and L3 PTP messages.
Definition: ptp_dispatch.h:46
void store_addr(const satcat5::eth::MacAddr &mac, const satcat5::ip::Addr &ip=satcat5::ip::ADDR_NONE, const satcat5::eth::VlanTag &vtag=satcat5::eth::VTAG_NONE)
Set the address for use with DispatchTo::STORED.
Definition: ptp_dispatch.cc:83
void store_reply_addr()
Set the address for use with DispatchTo::STORED.
Definition: ptp_dispatch.cc:77
satcat5::io::Writeable * ptp_send(satcat5::ptp::DispatchTo where, unsigned num_bytes, u8 ptp_msg_type)
Send a PTP message to the designated address(es).
Definition: ptp_dispatch.cc:41
void poll_demand() override
Deferred event handler, called after request().
Definition: ptp_dispatch.cc:91
Generic API for network ports that support PTP.
Definition: ptp_interface.h:27
virtual satcat5::io::Writeable * ptp_tx_write()=0
Return an object suitable for writing the next PTP frame.
void ptp_callback(satcat5::poll::OnDemand *obj)
Set callback object for PTP-related packet handling.
Definition: ptp_interface.h:30
virtual satcat5::io::Readable * ptp_rx_read()=0
Return an object suitable for reading the next PTP frame.
An Ethernet frame header.
Definition: eth_header.h:167
void write_to(satcat5::io::Writeable *wr) const
Write Ethernet header to the designated stream.
Definition: eth_header.cc:117
satcat5::eth::MacType type
Ethernet header fields.
Definition: eth_header.h:172
satcat5::eth::VlanTag vtag
Ethernet header fields.
Definition: eth_header.h:173
satcat5::eth::MacAddr src
Ethernet header fields.
Definition: eth_header.h:171
bool read_from(satcat5::io::Readable *rd)
Read Ethernet header from the designated stream.
Definition: eth_header.cc:128
An Ethernet MAC address (with serializable interface).
Definition: eth_header.h:29
EtherType field (uint16) is used a protocol-ID [1536..65535].
Definition: eth_header.h:96
Header contents for an 802.1Q Virtual-LAN tag.
Definition: eth_header.h:124
IPv4 address is a 32-bit unsigned integer.
Definition: ip_core.h:15
Structure for holding an IPv4 Header, including options.
Definition: ip_core.h:180
void write_to(satcat5::io::Writeable *wr) const
Write IPv4 header to the designated stream.
Definition: ip_core.cc:82
bool read_from(satcat5::io::Readable *rd)
Read an entire IPv4 header from the designated stream.
Definition: ip_core.cc:127
constexpr satcat5::ip::Addr src() const
< Source address
Definition: ip_core.h:208
UDP and TCP ports are both 16-bit unsigned integers.
Definition: ip_core.h:119
static constexpr u8 TYPE_SYNC
Message types (Section 13.3.2.3 / Table 36)
Definition: ptp_header.h:66
Struct used for sourcePortIdentity and requestingPortIdentity.
Definition: ptp_header.h:17
UDP header contents.
Definition: udp_core.h:48
void write_to(satcat5::io::Writeable *wr) const
Write UDP header to the designated stream.
Definition: udp_core.cc:83
bool read_from(satcat5::io::Readable *rd)
Read UDP header from the designated stream.
Definition: udp_core.cc:91