SatCat5
net_tpipe.cc
1 // Copyright 2025 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/net_tpipe.h>
7 #include <satcat5/udp_dispatch.h>
8 #include <satcat5/utils.h>
9 
10 namespace eth = satcat5::eth;
11 namespace net = satcat5::net;
12 namespace udp = satcat5::udp;
16 
18  : BufferedIO(m_txbuff, MAX_WINDOW, 0, m_rxbuff, MAX_WINDOW, 0)
19  , Protocol(net::TYPE_NONE)
20  , m_iface(dst)
21  , m_retry(0)
22  , m_state(0)
23  , m_retransmit(500) // Default = 0.5 seconds
24  , m_timeout(30000) // Default = 30 seconds
25  , m_txpos(0)
26  , m_txref(0)
27  , m_rxpos(0)
28  , m_rxref(0)
29  , m_txbuff{}
30  , m_rxbuff{}
31 {
32  // Register for incoming packet callbacks.
33  m_iface->iface()->add(this);
34 }
35 
36 #if SATCAT5_ALLOW_DELETION
37 net::Tpipe::~Tpipe() {
38  m_iface->iface()->remove(this);
39 }
40 #endif
41 
43  // If a connection is open, let counterpart know it's closing.
44  set_mask_u16(m_state, STATE_CLOSING);
45  if (m_state & STATE_READY) send_block();
46  // Close the local connection and halt timer events.
47  m_iface->close();
48  timer_stop();
49 }
50 
51 bool net::Tpipe::completed() const {
52  // Have we acknowledged every byte in the transmit FIFO?
53  return (m_state & STATE_READY) && !m_tx.get_read_ready();
54 }
55 
57  m_timeout = 0;
58  m_state |= STATE_READY;
59  m_state |= STATE_TXONLY;
60 }
61 
63  // If we were previously idle, send the new data.
64  // (Otherwise ignore until reply or timeout.)
65  if (!(m_state & STATE_TXBUSY)) send_block();
66 }
67 
69  // Read the packet header.
70  u16 flags = src.read_u16();
71  u16 txpos = src.read_u16();
72  u16 rxpos = src.read_u16();
73 
74  // Sanity check on the reported data length.
75  unsigned rxlen = unsigned(flags & FLAG_LEN);
76  if (src.get_read_ready() < rxlen) return;
77  if (rxlen > MAX_WINDOW) return;
78 
79  // Opening a new connection?
80  bool send_reply = false;
81  if (flags & FLAG_START) {
82  // Remote endpoint requesting a new connection.
83  m_iface->save_reply_address();
84  m_state = STATE_READY;
85  send_reply = true;
86  // If we're in the middle of a session, check if this is
87  // a delayed duplicate of the original start-of-session
88  // request before we reset the session state.
89  bool dupe_request = (m_state & STATE_READY)
90  && (m_txref == rxpos) && (m_rxref == txpos);
91  if (!dupe_request) {
92  m_rx.clear();
93  m_txpos = rxpos;
94  m_txref = rxpos;
95  m_rxpos = txpos;
96  m_rxref = txpos;
97  }
98  } else if (m_state & STATE_OPENREQ) {
99  // Reply to our start-of-connection request.
100  m_rx.clear();
101  clr_mask_u16(m_state, STATE_OPENREQ);
102  set_mask_u16(m_state, STATE_READY);
103  } else {
104  // Normal packet, accept if there's an open connection.
105  if (!(m_state & STATE_READY)) return;
106  }
107 
108  // Any packet from the remote host resets the watchdog.
109  m_retry = 0;
110 
111  // Has the remote side acknowledged additional data?
112  u16 rxdiff = rxpos - m_txpos;
113  if (s16(rxdiff) > 0) {
114  // Update the transmit state.
115  m_tx.read_consume(rxdiff);
116  m_txpos += rxdiff;
117  clr_mask_u16(m_state, STATE_TXBUSY);
118  // Reply with next block of data.
119  send_reply = true;
120  }
121 
122  // Is there any new data in this packet?
123  unsigned skip = unsigned(m_rxpos - txpos);
124  if (rxlen > skip) {
125  // Skip ahead to the portion of interest.
126  // (We may have already received some data.)
127  unsigned rdlen = min_unsigned(rxlen - skip, m_rx.get_write_space());
128  src.read_consume(skip);
129  // Copy new data to the output FIFO.
130  u8 tmp[MAX_WINDOW];
131  src.read_bytes(rdlen, tmp);
132  m_rx.write_bytes(rdlen, tmp);
133  if (m_rx.write_finalize()) {
134  // Update receive state and send acknowledgement.
135  m_rxpos += rdlen;
136  send_reply = true;
137  }
138  }
139 
140  // If there's been any progress, send an immediate reply.
141  // Stale or duplicate messages must not send an acknolwedgement, to avoid
142  // "sorcerer's apprentice syndrome" as seen in early versions of TFTP.
143  if (flags & FLAG_STOP) {
144  // Remote endpoint is closing the connection.
145  m_tx.clear();
146  m_iface->close();
147  m_state = 0;
148  timer_stop();
149  } else if (send_reply) {
150  // Send acknowledgement and/or additional data.
151  send_block();
152  }
153 }
154 
156  // Timeout waiting for acknowledgement?
157  if ((m_retry < m_timeout) || (m_state & STATE_TXONLY)) {
158  send_block(); // Retry / keep-alive.
159  } else {
160  close(); // Close connection.
161  }
162 }
163 
165  // How much data can we send in this block?
166  unsigned txlen = min_unsigned(MAX_WINDOW, m_tx.get_peek_ready());
167 
168  // Is the network device ready to send?
169  // (Packet is next data block plus 6-byte header.)
170  auto wr = m_iface->open_write(txlen + 6);
171  if (wr) {
172  // Randomize next-packet timeout from 1.0 to 1.5x nominal,
173  // to reduce the number of crossing-in-transit messages.
174  unsigned timeout = m_retransmit + util::prng.next(0, m_retransmit/2);
175  // Update protocol state.
176  set_mask_u16(m_state, STATE_TXBUSY);
177  m_retry += timeout;
178  timer_once(timeout);
179  // Set header flags based on current state.
180  u16 flags = u16(txlen);
181  if (m_state & STATE_OPENREQ) flags |= FLAG_START;
182  if (m_state & STATE_CLOSING) flags |= FLAG_STOP;
183  // Write packet header and contents.
184  // Note: Do not consume data until transfer is acknowledged.
185  wr->write_u16(flags);
186  wr->write_u16(m_txpos);
187  wr->write_u16(m_rxpos);
188  if (txlen) wr->write_bytes(txlen, m_tx.peek(txlen));
189  bool sent = wr->write_finalize();
190  // If we're in Tx-only mode, consume data immediately.
191  // Otherwise, it's consumed by acknowledgement logic in `frame_rcvd`.
192  if (sent && (m_state & STATE_TXONLY)) {
193  m_tx.read_consume(txlen);
194  m_txpos += txlen;
195  }
196  } else {
197  // Rapid polling until device is ready to send.
198  // (This may be due to flow-control or due to ARP resolution.)
199  constexpr u16 POLL_MSEC = 10;
200  m_retry += POLL_MSEC;
201  timer_once(POLL_MSEC);
202  }
203 }
204 
206  // Randomizing initial parameters helps prevent pathological cases
207  // where we accidentally "resume" a previously-terminated session.
208  m_state = STATE_OPENREQ;
209  m_txpos = u16(util::prng.next());
210  m_rxpos = u16(util::prng.next());
211  // Attempt to send the first packet.
212  // (If unable, this also starts polling for follow-up.)
213  send_block();
214 }
215 
217  : eth::AddressContainer(iface)
218  , net::Tpipe(&m_addr)
219 {
220  // Nothing else to initialize.
221 }
222 
223 void eth::Tpipe::bind(const eth::MacType& etype, const eth::VlanTag& vtag) {
224  close(); // Close previous connection, if any.
225  m_filter = net::Type(vtag.vid(), etype.value);
226 }
227 
229  const eth::MacAddr& addr,
230  const eth::MacType& etype,
231  const eth::VlanTag& vtag)
232 {
233  close(); // Close previous connection, if any.
234  m_addr.connect(addr, etype, vtag);
235  m_filter = net::Type(vtag.vid(), etype.value);
236  send_start(); // Send request to open new connection.
237 }
238 
240  : udp::AddressContainer(iface)
241  , net::Tpipe(&m_addr)
242 {
243  // Nothing else to initialize.
244 }
245 
246 void udp::Tpipe::bind(const udp::Port& port) {
247  close(); // Close previous connection, if any.
248  m_filter = net::Type(port.value);
249 }
250 
252  const ip::Addr& dstaddr,
253  const udp::Port& dstport,
254  const eth::VlanTag& vtag)
255 {
256  close(); // Close previous connection, if any.
257  udp::Port srcport = m_addr.udp()->next_free_port();
258  m_addr.connect(dstaddr, dstport, srcport, vtag);
259  m_filter = net::Type(dstport.value, srcport.value);
260  send_start(); // Send request to open new connection.
261 }
Inheritable container for a eth::Address.
Definition: eth_address.h:60
Implemention of "net::Dispatch" for Ethernet frames.
Definition: eth_dispatch.h:21
Simple network pipe service over raw Ethernet.
Definition: net_tpipe.h:129
void connect(const satcat5::eth::MacAddr &addr, const satcat5::eth::MacType &etype, const satcat5::eth::VlanTag &vtag=satcat5::eth::VTAG_NONE)
Create an outgoing connection with the specified server.
Definition: net_tpipe.cc:228
Tpipe(satcat5::eth::Dispatch *iface)
Create an idle network pipe.
Definition: net_tpipe.cc:216
void bind(const satcat5::eth::MacType &etype, const satcat5::eth::VlanTag &vtag=satcat5::eth::VTAG_NONE)
Wait for incoming connections to the specified EtherType.
Definition: net_tpipe.cc:223
Limited read of next N bytes.
Definition: io_readable.h:255
bool read_consume(unsigned nbytes) override
Read and discard 0 or more bytes.
Definition: io_readable.cc:307
bool read_bytes(unsigned nbytes, void *dst) override
Read 0 or more bytes into a buffer.
Definition: io_readable.cc:296
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Definition: io_readable.cc:293
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
Defines a generic API for sending data to a specific destination, such as a MAC address,...
Definition: net_address.h:30
virtual satcat5::net::Dispatch * iface() const =0
Fetch a pointer to the underlying interface.
void add(satcat5::net::Protocol *proto)
Register a Protocol object.
Definition: net_dispatch.h:55
A net::Protocol is the counterpart to net::Dispatch that handles a particular data stream,...
Definition: net_protocol.h:28
bool completed() const
Has all queued data been acknowledged?
Definition: net_tpipe.cc:51
void timer_event() override
Child class MUST override this method.
Definition: net_tpipe.cc:155
void close()
Close the active connection.
Definition: net_tpipe.cc:42
void send_start()
Special case of send_block used to open a new connection.
Definition: net_tpipe.cc:205
Tpipe(satcat5::net::Address *dst)
Create link and set the transport service.
Definition: net_tpipe.cc:17
satcat5::net::Address *const m_iface
Network interface.
Definition: net_tpipe.h:109
void set_txonly()
Enable unidirectional transmission? Transmit-only endpoints do not wait for acknowledgements.
Definition: net_tpipe.cc:56
void data_rcvd(satcat5::io::Readable *src) override
The data_rcvd() callback is polled whenever data is available.
Definition: net_tpipe.cc:62
void frame_rcvd(satcat5::io::LimitedRead &src) override
Dispatch calls frame_rcvd(...) for each incoming frame with with a matching net::Type value.
Definition: net_tpipe.cc:68
void send_block()
Send a synchronization packet, with data if applicable.
Definition: net_tpipe.cc:164
Inheritable container for a udp::Address.
Definition: udp_core.h:149
Dispatcher sorts incoming UDP messages by port index.
Definition: udp_dispatch.h:20
Simple network pipe service over raw Ethernet.
Definition: net_tpipe.h:155
void bind(const satcat5::udp::Port &port)
Wait for incoming connections to the specified UDP port.
Definition: net_tpipe.cc:246
void connect(const satcat5::ip::Addr &addr, const satcat5::udp::Port &port, const satcat5::eth::VlanTag &vtag=satcat5::eth::VTAG_NONE)
Create an outgoing connection with the specified server.
Definition: net_tpipe.cc:251
Tpipe(satcat5::udp::Dispatch *iface)
Create an idle network pipe.
Definition: net_tpipe.cc:239
u32 next()
Range [0..2^32)
Definition: utils.cc:194
constexpr satcat5::net::Type TYPE_NONE
The TYPE_NONE mask blocks all incoming frames.
Definition: net_type.h:97
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
u16 value
The 16-bit value is stored in processor-native order.
Definition: eth_header.h:98
Header contents for an 802.1Q Virtual-LAN tag.
Definition: eth_header.h:124
u16 vid() const
Accessors for each individual field.
Definition: eth_header.h:135
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
Miscellaneous mathematical utility functions.
constexpr unsigned min_unsigned(unsigned a, unsigned b)
Min and max functions.
Definition: utils.h:111
void clr_mask_u16(u16 &val, u16 mask)
Set or clear bit masks.
Definition: utils.h:26
void set_mask_u16(u16 &val, u16 mask)
Set or clear bit masks.
Definition: utils.h:25