SatCat5
udp_dispatch.cc
1 // Copyright 2021-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/log.h>
8 #include <satcat5/udp_dispatch.h>
9 #include <satcat5/utils.h>
10 
12 using satcat5::ip::PROTO_UDP;
13 using satcat5::net::Type;
15 using satcat5::udp::HEADER_BYTES;
16 namespace log = satcat5::log;
17 
18 // Set verbosity level (0/1/2)
19 static const unsigned DEBUG_VERBOSE = 0;
20 
21 // Maximum number of dynamically assigned ports?
22 #ifndef SATCAT5_UDP_MAXDYN
23 #define SATCAT5_UDP_MAXDYN 16384
24 #endif
25 
26 // Reserved range for dynamically allocated UDP ports.
27 static_assert(SATCAT5_UDP_MAXDYN <= 49152);
28 static constexpr u16 DYNAMIC_PORT_MAX = 0xFFFF;
29 static constexpr u16 DYNAMIC_PORT_MIN = DYNAMIC_PORT_MAX + 1 - SATCAT5_UDP_MAXDYN;
30 
31 Dispatch::Dispatch(satcat5::ip::Dispatch* iface)
32  : satcat5::net::Protocol(Type(PROTO_UDP))
33  , m_iface(iface)
34  , m_next_port(DYNAMIC_PORT_MIN - 1)
35  , m_reply_src(satcat5::udp::PORT_NONE)
36  , m_reply_dst(satcat5::udp::PORT_NONE)
37 {
38  m_iface->add(this);
39 }
40 
41 #if SATCAT5_ALLOW_DELETION
42 Dispatch::~Dispatch()
43 {
44  m_iface->remove(this);
45 }
46 #endif
47 
49 {
50  // Increment previous port iteration and check for prior claims.
51  // (Inspired by lwIP; succeeds on the first try 99% of the time.)
52  // Keep trying until we find a free port or we're back where we started.
53  u16 wrap = m_next_port;
54  while (1) {
55  // Increment with wraparound.
56  m_next_port = (m_next_port < DYNAMIC_PORT_MAX)
57  ? (m_next_port + 1) : (DYNAMIC_PORT_MIN);
58  // Check if current hypothesis is free.
59  if (!bound(Type(m_next_port)))
60  return satcat5::udp::Port(m_next_port);
61  // Abort if we've searched the entire range.
62  if (m_next_port == wrap) {
63  log::Log(log::WARNING, "UdpDispatch: Ports full");
64  return satcat5::udp::PORT_NONE;
65  }
66  }
67 }
68 
70  const satcat5::net::Type& type, unsigned len)
71 {
72  satcat5::ip::Address addr(m_iface, PROTO_UDP);
73  addr.connect(
74  m_iface->reply_ip(),
75  m_iface->reply_mac(),
76  m_iface->reply_vtag());
77 
78  return open_write(addr, // Destination IP + MAC
79  m_reply_dst, // Source and destination ports
80  m_reply_src, // (Note swap from rcvd packet)
81  len); // User data length
82 }
83 
85  satcat5::ip::Address& addr, // Destination IP+MAC
86  const satcat5::udp::Port& src, // Source port
87  const satcat5::udp::Port& dst, // Destination port
88  unsigned len) // User data length
89 {
90  if (DEBUG_VERBOSE > 1)
91  log::Log(log::DEBUG, "UdpDispatch: open_write").write(dst.value);
92 
93  // Write out Ethernet and IPv4 headers.
94  unsigned total_len = len + HEADER_BYTES;
95  satcat5::io::Writeable* wr = addr.open_write(total_len);
96 
97  // Write the UDP frame header.
98  if (wr) {
99  wr->write_obj(src); // Source port
100  wr->write_obj(dst); // Destination port
101  wr->write_u16((u16)total_len); // Length (incl this header)
102  wr->write_u16(0); // Checksum disabled (optional)
103  }
104  return wr;
105 }
106 
108 {
109  if (DEBUG_VERBOSE > 1)
110  log::Log(log::DEBUG, "UdpDispatch: frame_rcvd").write((u16)src.get_read_ready());
111 
112  // Sanity-check on length before reading header.
113  if (src.get_read_ready() < HEADER_BYTES) return;
114 
115  // Read the UDP frame header.
116  src.read_obj(m_reply_src); // Source port
117  src.read_obj(m_reply_dst); // Destination port
118  u16 len = src.read_u16(); // Frame length (incl this header)
119  u16 chk = src.read_u16(); // Checksum (ignored)
120 
121  // Sanity-check on the reported length.
122  unsigned len_eff = len - HEADER_BYTES;
123  if ((len < HEADER_BYTES) || (len_eff > src.get_read_ready())) {
124  if (DEBUG_VERBOSE > 0)
125  log::Log(log::INFO, "UdpDispatch: Bad length").write(len);
126  return; // Invalid length parameter
127  }
128 
129  // Attempt delivery based on destination port only or source + destination.
130  // (Src + Dst matches with higher priority than Dst port alone.)
131  // Use length from UDP header to trim any padding from upper layers.
132  Type type1 = Type(m_reply_dst.value);
133  Type type2 = Type(m_reply_src.value, m_reply_dst.value);
134  bool ok = deliver(type2, &src, len_eff) || deliver(type1, &src, len_eff);
135 
136  if (DEBUG_VERBOSE > 0 && !ok)
137  log::Log(log::INFO, "UdpDispatch: No such port").write(m_reply_dst.value);
138  if (DEBUG_VERBOSE > 1 && ok)
139  log::Log(log::DEBUG, "UdpDispatch: Frame delivered").write(m_reply_dst.value);
140 
141  // No response to unicast packet? Send an ICMP error message.
142  satcat5::ip::Addr dst = m_iface->reply_hdr().dst();
143  if (!ok && dst.is_unicast()) {
144  // Reconstruct the first N bytes of the original message.
145  // (ICMP needs at least 8, which is equal to the UDP header size.)
147  wr.write_obj(m_reply_src);
148  wr.write_obj(m_reply_dst);
149  wr.write_u16(len);
150  wr.write_u16(chk);
151  src.copy_to(&wr);
152  wr.write_finalize();
153  // Forward that data to the ICMP block.
155  m_iface->icmp()->send_error(satcat5::ip::ICMP_UNREACHABLE_PORT, &rd);
156  }
157 }
Ephemeral Readable interface for a simple array.
Definition: io_readable.h:206
unsigned written_len() const
Report total length after write_finalize() is called.
Definition: io_writeable.h:151
bool write_finalize() override
Mark end of frame and release temporary working data.
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
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Definition: io_readable.cc:293
bool read_obj(T &t)
Templated wrapper for any object with the following method: bool read_from(satcat5::io::Readable* rd)...
Definition: io_readable.h:140
unsigned copy_to(satcat5::io::Writeable *dst)
Copy data to a Writeable object, without finalizing.
Definition: io_readable.cc:214
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
void write_obj(const T &obj)
Templated wrapper for any object with the following method: void write_to(satcat5::io::Writeable* wr)...
Definition: io_writeable.h:104
Connection metadata for an IPv4 address.
Definition: ip_address.h:38
void connect(const satcat5::ip::Addr &dstaddr, const satcat5::eth::VlanTag &vtag=satcat5::eth::VTAG_NONE)
Automatic address resolution using routing table + ARP.
Definition: ip_address.cc:54
satcat5::io::Writeable * open_write(unsigned len) override
Open a new frame to the designated address and type.
Definition: ip_address.cc:124
Protocol handler and dispatch unit for Internet Protocol v4 (IPv4).
Definition: ip_dispatch.h:43
bool send_error(u16 type, satcat5::io::Readable *src, uint32_t arg=0)
Send a specific error message.
Definition: ip_icmp.cc:120
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
void add(satcat5::net::Protocol *proto)
Register a Protocol object.
Definition: net_dispatch.h:55
bool deliver(const satcat5::net::Type &type, satcat5::io::Readable *src, unsigned len)
Deliver current message by calling Protocol::frame_rcvd(...).
Definition: net_dispatch.cc:25
void remove(satcat5::net::Protocol *proto)
Unregister a Protocol object.
Definition: net_dispatch.h:59
bool bound(const satcat5::net::Type &type) const
Check if a given net::Type has a matching Protocol.
Definition: net_dispatch.cc:14
Dispatcher sorts incoming UDP messages by port index.
Definition: udp_dispatch.h:20
satcat5::io::Writeable * open_write(satcat5::ip::Address &addr, const satcat5::udp::Port &src, const satcat5::udp::Port &dst, unsigned len)
Send a datagram to the designated UDP address and port.
Definition: udp_dispatch.cc:84
satcat5::io::Writeable * open_reply(const satcat5::net::Type &type, unsigned nbytes) override
Reply to the sender of the most recent received message.
Definition: udp_dispatch.cc:69
void frame_rcvd(satcat5::io::LimitedRead &src) override
Dispatch calls frame_rcvd(...) for each incoming frame with with a matching net::Type value.
satcat5::udp::Port next_free_port()
Get the next unclaimed dynamically-allocated port index.
Definition: udp_dispatch.cc:48
Diagnostic logging to UART and/or Ethernet ports.
IPv4 address is a 32-bit unsigned integer.
Definition: ip_core.h:15
bool is_unicast() const
Any normal single-destination address.
Definition: ip_core.cc:59
constexpr satcat5::ip::Addr dst() const
< Destination address
Definition: ip_core.h:210
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
Miscellaneous mathematical utility functions.