SatCat5
coap_endpoint.cc
1 // Copyright 2024-2025 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/coap_endpoint.h>
7 #include <satcat5/coap_reader.h>
8 #include <satcat5/coap_writer.h>
9 #include <satcat5/log.h>
10 #include <satcat5/udp_dispatch.h>
11 
20 namespace log = satcat5::log;
21 
22 // Set verbosity level (0/1/2)
23 static const unsigned DEBUG_VERBOSE = 0;
24 
25 Endpoint::Endpoint(satcat5::net::Dispatch* iface)
26  : Protocol(satcat5::net::TYPE_NONE)
27  , m_iface(iface)
28  , m_prefer(nullptr)
29  , m_aux_ep(nullptr)
30 {
31  m_iface->add(this);
32 }
33 
34 #if SATCAT5_ALLOW_DELETION
35 Endpoint::~Endpoint() {
36  m_iface->remove(this);
37 }
38 #endif
39 
40 Connection* Endpoint::find_token(u32 token) const {
41  Connection* item = m_list.head();
42  while (item && item->get_proxy_token() != token) {
43  item = m_list.next(item);
44  }
45  return item;
46 }
47 
49  Connection* item = m_list.head();
50  while (item && !item->is_idle()) {
51  item = m_list.next(item);
52  }
53  return item;
54 }
55 
57  if (DEBUG_VERBOSE > 0) log::Log(log::DEBUG, "CoAP: frame_rcvd");
58 
59  // Any matches by remote address?
60  // * For CCSDS-SPP, this checks APID.
61  // * For UDP, this checks IP address + UDP port.
62  Connection* item = m_list.head();
63  while (item && !item->is_match_addr()) {
64  item = m_list.next(item);
65  }
66 
67  // Response without a matching request?
68  satcat5::coap::ReadSimple msg(&src);
69  if (msg.is_response() && !item) {
70  // Sender is confused and needs a reset.
71  reply(TYPE_RST, &msg); return;
72  }
73 
74  // If there's no address match, accept any idle connection.
75  // (i.e., Prioritize re-use of open Connection when possible.)
76  if (!item) item = get_idle_connection();
77 
78  // Parse and process the message...
79  if (item) {
80  // Deliver and process the message.
81  item->deliver(&msg);
82  } else {
83  // Unable to deliver because all connections are busy.
84  log::Log(log::WARNING, "CoAP: All connections busy.");
85  }
86 }
87 
88 bool Endpoint::reply(u8 type, const ReadHeader* rcvd) {
89  // Send empty ACK or RST reply in response to certain events.
90  // Does NOT alter connection state or working buffer contents.
91  if (DEBUG_VERBOSE > 0)
92  log::Log(log::DEBUG, "CoAP: Reply").write(rcvd->msg_id());
93 
94  // Construct the outgoing message in a temporary buffer.
95  // (This is easier than trying to predict the total length.)
96  // Do not echo the token in the empty message (Section 3).
98  satcat5::coap::Writer reply(&buff);
99  reply.write_header(type, CODE_EMPTY, rcvd->msg_id());
100  reply.write_finalize(); // Empty message (no options or data)
101 
102  // Send a reply directly through the UDP interface.
103  // TODO: This will result in out-of-order sequence IDs in CCSDS mode.
104  satcat5::io::Writeable* wr = m_iface->open_reply(
105  satcat5::net::TYPE_NONE, buff.written_len());
106  if (wr) wr->write_bytes(buff.written_len(), buff.buffer());
107  return wr && wr->write_finalize();
108 }
109 
111  : m_connection(coap, (satcat5::ccsds_spp::Dispatch*)coap->iface())
112 {
113  m_connection.connect(apid);
114  coap->set_filter(satcat5::net::Type(apid));
115 }
116 
118  : Endpoint(iface)
119  , ManageSpp(this, apid)
120 {
121  // Nothing else to initialize.
122 }
123 
124 EndpointSppFwd::EndpointSppFwd(
125  satcat5::ccsds_spp::Dispatch* iface, u16 apid,
126  satcat5::coap::Endpoint* backing_endpoint)
127  : EndpointSpp(iface, apid)
128  , m_endpoint(backing_endpoint)
129 {
130  // Link ourselves as an auxiliary endpoint.
131  // (e.g., Allows cross-protocol forwarding in proxy servers.)
132  m_endpoint->m_aux_ep = this;
133 }
134 
136  { m_endpoint->coap_request(obj, msg); }
138  { m_endpoint->coap_response(obj, msg); }
140  { m_endpoint->coap_error(obj); }
142  { m_endpoint->coap_ping(msg); }
143 
145  : m_endpoint(coap)
146 {
147  if (req_port.value) bind(req_port);
148 }
149 
152 }
153 
155  const satcat5::udp::Addr& dstaddr,
156  const satcat5::udp::Port& dstport,
157  const satcat5::udp::Port& srcport)
158 {
159  // This class requires that all attached Connection objects are
160  // actually ConnectionUdp, so this coersion should succeed.
163  if (udp) udp->connect(dstaddr, dstport, srcport);
164  return udp;
165 }
166 
168  : Endpoint(iface), ManageUdp(this, req_port)
169 {
170  // Nothing else to initialize.
171 }
Implemention of "net::Dispatch" API for CCSDS-SPP packets.
Definition: ccsds_spp.h:199
CoAP request/response handling for a single client-server connection.
bool is_match_addr() const
< Match reply endpoint?
bool is_idle() const
< Idle and ready for use?
u32 get_proxy_token() const
Query the most recent proxy token.
bool connect(u16 apid)
Set remote APID for later calls to open_request().
Variant of coap::Connection for UDP connections.
CoAP endpoint (i.e., client, server, or combined client+server).
Definition: coap_endpoint.h:41
void set_connection(satcat5::coap::Connection *obj)
Set the preferred connection for outgoing requests.
Definition: coap_endpoint.h:58
virtual void coap_request(Connection *obj, Reader *msg)
The Child class overrides these event-handlers:
satcat5::coap::Connection * get_idle_connection() const
Get the first idle connection, or null if all are busy.
void frame_rcvd(satcat5::io::LimitedRead &src) override
Dispatch calls frame_rcvd(...) for each incoming frame with with a matching net::Type value.
virtual void coap_error(Connection *obj)
The Child class overrides these event-handlers:
virtual void coap_ping(const Reader *msg)
The Child class overrides these event-handlers:
Connection * find_token(u32 token) const
Scan connections for a matching proxy-ID.
void set_filter(const satcat5::net::Type &filter)
Set network interface filter.
Definition: coap_endpoint.h:63
virtual void coap_response(Connection *obj, Reader *msg)
The Child class overrides these event-handlers:
EndpointSpp variant that forwards all requests to another Endpoint.
void coap_ping(const Reader *msg) override
The Child class overrides these event-handlers:
void coap_request(Connection *obj, Reader *msg) override
The Child class overrides these event-handlers:
void coap_error(Connection *obj) override
The Child class overrides these event-handlers:
void coap_response(Connection *obj, Reader *msg) override
The Child class overrides these event-handlers:
Variant of coap::Endpoint for a CCSDS-SPP client or server.
EndpointSpp(satcat5::ccsds_spp::Dispatch *iface, u16 apid)
Constructor and destructor should only be called by the child.
Variant for a UDP client or server with multiple active connections.
EndpointUdp(satcat5::udp::Dispatch *iface, satcat5::udp::Port req_port=satcat5::udp::PORT_NONE)
Constructor and destructor should only be called by the child.
Connection manager for CoAP endpoints using CCSDS-SPP.
satcat5::coap::ConnectionSpp m_connection
Point-to-point link, only one Connection is required.
ManageSpp(satcat5::coap::Endpoint *coap, u16 apid)
Constructor and destructor should only be called by the child.
Connection manager for CoAP endpoints using UDP.
satcat5::coap::ConnectionUdp * connect(const satcat5::udp::Addr &dstaddr, const satcat5::udp::Port &dstport=satcat5::udp::PORT_COAP, const satcat5::udp::Port &srcport=satcat5::udp::PORT_NONE)
Open a connection to the designated remote UDP endpoint.
ManageUdp(satcat5::coap::Endpoint *coap, satcat5::udp::Port req_port=satcat5::udp::PORT_NONE)
Constructor and destructor should only be called by the child.
satcat5::coap::Endpoint *const m_endpoint
Pointer to the associated Endpoint.
satcat5::udp::Dispatch * udp() const
Pointer to the parent's network interface.
void bind(satcat5::udp::Port req_port=satcat5::udp::PORT_COAP)
Begin accepting incoming requests on the designated UDP port.
Parser for CoAP message headers only.
Definition: coap_reader.h:80
bool is_response() const
< ACK request?
Definition: coap_reader.h:122
u16 msg_id() const
< Message ID
Definition: coap_reader.h:116
Wrapper for coap::ReadOptions that automatically parses options, rejecting any message with unrecogni...
Definition: coap_reader.h:234
Base-class for parsing CoAP message headers and options.
Definition: coap_reader.h:162
Message formatting for the Constrained Applications Protocol (CoAP).
Definition: coap_writer.h:26
unsigned written_len() const
Report total length after write_finalize() is called.
Definition: io_writeable.h:151
const u8 * buffer() const
Read-only access to the working buffer.
Definition: io_writeable.h:147
Thin wrapper for ArrayWrite with a built-in buffer.
Definition: io_writeable.h:169
Limited read of next N bytes.
Definition: io_readable.h:255
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
virtual void write_bytes(unsigned nbytes, const void *src)
Write 0 or more bytes from a buffer.
virtual bool write_finalize()
Mark end of frame and release temporary working data.
The Log class creates and formats one log message.
Definition: log.h:195
Log & write(const char *str)
Formatting methods for various data types.
Definition: log.cc:198
The "Dispatch" is a generic interface that knows how to read a designated protocol layer and sort inc...
Definition: net_dispatch.h:36
void add(satcat5::net::Protocol *proto)
Register a Protocol object.
Definition: net_dispatch.h:55
virtual satcat5::io::Writeable * open_reply(const satcat5::net::Type &type, unsigned len)=0
Open a reply to the sender of the most recent message by writing frame header(s) and returning a stre...
void remove(satcat5::net::Protocol *proto)
Unregister a Protocol object.
Definition: net_dispatch.h:59
Dispatcher sorts incoming UDP messages by port index.
Definition: udp_dispatch.h:20
T * next(const T *item) const
Fetch pointer to the next item.
Definition: list.h:261
Message parsing for the Constrained Applications Protocol (CoAP)
Diagnostic logging to UART and/or Ethernet ports.
IPv4 address is a 32-bit unsigned integer.
Definition: ip_core.h:15
UDP and TCP ports are both 16-bit unsigned integers.
Definition: ip_core.h:119
u16 value
Raw access to the underlying representation.
Definition: ip_core.h:121
Multipurpose filter for matching fields in network packets.
Definition: net_type.h:38