SatCat5
udp_tftp.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/log.h>
7 #include <satcat5/udp_tftp.h>
8 #include <satcat5/utils.h>
9 
10 // Shortcuts for commonly used names.
12 using satcat5::net::Type;
13 using satcat5::udp::PORT_TFTP_SERVER;
22 
23 // Set verbosity level for debugging (0/1/2).
24 static constexpr unsigned DEBUG_VERBOSE = 0;
25 
26 // Define the type-filter for incoming server requests.
27 static constexpr Type TYPE_TFTP_SERVER = Type(PORT_TFTP_SERVER.value);
28 
29 // Define TFTP opcodes (RFC 1350, Section 5)
30 static constexpr u16 OPCODE_RRQ = 1; // Read request
31 static constexpr u16 OPCODE_WRQ = 2; // Write request
32 static constexpr u16 OPCODE_DATA = 3; // Data
33 static constexpr u16 OPCODE_ACK = 4; // Acknowledgement
34 static constexpr u16 OPCODE_ERROR = 5; // Error
35 
36 // Define TFTP error codes (RFC 1350, Appendix I)
37 // (Additional codes exist, but these are the ones we use.)
38 static constexpr u16 ERROR_TIMEOUT = 0; // Timeout (using code 0)
39 static constexpr u16 ERROR_NOFILE = 1; // File not found
40 static constexpr u16 ERROR_PROTOCOL = 4; // Illegal TFTP operation
41 
42 // Internal options and status flags.
43 static constexpr u16 FLAG_BUSY = 0x0001; // Transfer in progress
44 static constexpr u16 FLAG_EOF = 0x0002; // Transfer completed
45 static constexpr u16 FLAG_FIRST = 0x0004; // Waiting for first response
46 
47 // TFTP should only be used on a LAN, so set an aggressive timeout
48 // for the first timeout and double on every subsequent attempt.
49 static constexpr unsigned RETRY_MAX = 4;
50 static constexpr unsigned RETRY_MSEC = 100;
51 
52 // Convert TFTP error-code to a user-readable error string.
53 inline const char* error_lookup(u16 errcode) {
54  switch (errcode) {
55  case ERROR_TIMEOUT: return "Timeout";
56  case ERROR_NOFILE: return "File not found";
57  case ERROR_PROTOCOL: return "Illegal TFTP operation";
58  default: return "Unknown error";
59  }
60 }
61 
62 TftpTransfer::TftpTransfer(satcat5::udp::Dispatch* iface)
63  : satcat5::net::Protocol(satcat5::net::TYPE_NONE)
64  , m_addr(iface)
65  , m_src(0)
66  , m_dst(0)
67  , m_xfer_bytes(0)
68  , m_block_id(0)
69  , m_flags(0)
70  , m_retry_count(0)
71  , m_retry_len(0)
72 {
73  // Register for incoming UDP packets based on "m_filter",
74  // which we will adjust on the fly.
75  m_addr.udp()->add(this);
76 }
77 
78 TftpTransfer::~TftpTransfer() {
79  m_addr.udp()->remove(this);
80 }
81 
82 void TftpTransfer::reset(const char* msg) {
83  if (DEBUG_VERBOSE > 1) log::Log(log::DEBUG, "TftpTransfer::reset", msg);
84 
85  // Always clean up the source.
86  if (m_src) m_src->read_finalize();
87 
88  // Did we just complete a transfer?
89  if ((m_flags & FLAG_BUSY) && (m_flags & FLAG_EOF)) {
90  // Successful transfer requires no further cleanup.
91  log::Log(log::INFO, "TFTP", msg)
92  .write(m_src ? " Sent" : " Rcvd").write10(m_xfer_bytes);
93  } else {
94  // Failed transfer should revert if possible.
95  log::Log(log::WARNING, "TFTP", msg);
96  if (m_dst) m_dst->write_abort();
97  }
98 
99  // Force all internal state to idle.
100  m_addr.close();
101  m_filter = satcat5::net::TYPE_NONE;
102  m_src = 0;
103  m_dst = 0;
104  m_block_id = 0;
105  m_flags = 0;
106  m_xfer_bytes = 0;
107  m_retry_count = 0;
108  m_retry_len = 0;
109  timer_stop();
110 }
111 
113  const satcat5::ip::Addr& dstaddr,
114  u16 opcode, const char* filename)
115 {
116  if (DEBUG_VERBOSE > 1) log::Log(log::DEBUG, "TftpTransfer::request");
117 
118  // Open UDP socket on the next available source port.
119  // (This will usually issue an ARP request for MAC lookup.)
120  satcat5::udp::Port srcport = m_addr.udp()->next_free_port();
121  m_addr.connect(dstaddr, PORT_TFTP_SERVER, srcport);
122  m_filter = Type(srcport.value);
123 
124  // Write out the request packet (Section 5).
125  ArrayWrite pkt(m_retry_buff, sizeof(m_retry_buff));
126  pkt.write_u16(opcode);
127  pkt.write_str(filename);
128  pkt.write_u8(0);
129  pkt.write_str("octet");
130  pkt.write_u8(0);
131  pkt.write_finalize();
132 
133  // Queue outgoing packet, sent after receiving ARP response.
134  send_packet(pkt.written_len(), 0);
135 }
136 
138  // If we've got an open connection from the same endpoint,
139  // treat it as a duplicate and retransmit the first message.
140  satcat5::udp::Dispatch* iface = m_addr.udp();
141  bool duplicate = active()
142  && iface->reply_ip() == m_addr.dstaddr()
143  && iface->reply_mac() == m_addr.dstmac()
144  && iface->reply_src() == m_addr.dstport();
145  if (duplicate) send_packet(m_retry_len, 0);
146  return duplicate;
147 }
148 
150  // Sanity check: Close leftover I/O from previous sessions.
151  if (m_dst || m_src) reset("Transfer interrupted.");
152 
153  // Open UDP socket on the next available source port.
154  satcat5::udp::Port dstport = m_addr.udp()->reply_src();
155  satcat5::udp::Port srcport = m_addr.udp()->next_free_port();
156  m_addr.connect(
157  m_addr.udp()->reply_ip(),
158  m_addr.udp()->reply_mac(),
159  dstport, srcport);
160 
161  // Update the filter for incoming packets.
162  m_filter = Type(dstport.value, srcport.value);
163 
164  // Log the new connection.
165  log::Log(log::INFO, "TFTP: Connected to client")
166  .write(m_addr.udp()->reply_ip())
167  .write(dstport.value).write(srcport.value);
168 }
169 
171  if (DEBUG_VERBOSE > 1)
172  log::Log(log::DEBUG, "TftpTransfer::file_send").write10((u32)src->get_read_ready());
173 
174  // Reset transfer state.
175  m_src = src;
176  m_dst = 0;
177  m_block_id = 0;
178  m_flags = FLAG_BUSY;
179 
180  // Send the first data packet immediately?
181  if (now) {
182  // Server to client: Server immediately sends first data block.
183  send_data(1);
184  } else {
185  // Client to server: Client waits for ACK-0 confirmation.
186  set_mask_u16(m_flags, FLAG_FIRST);
187  }
188 }
189 
191  if (DEBUG_VERBOSE > 1) log::Log(log::DEBUG, "TftpTransfer::file_recv");
192 
193  // Reset transfer state.
194  m_src = 0;
195  m_dst = dst;
196  m_block_id = 0;
197  m_flags = FLAG_BUSY;
198 
199  // Send the first acknowledge packet immediately?
200  if (now) {
201  // Client to server: Server immediately sends ACK-0.
202  send_ack(0);
203  } else {
204  // Server to client: Client waits for first data block.
205  set_mask_u16(m_flags, FLAG_FIRST);
206  }
207 }
208 
210  // All valid TFTP packets start with the opcode.
211  u16 opcode = src.read_u16();
212  if (DEBUG_VERBOSE > 1)
213  log::Log(log::DEBUG, "TftpTransfer::frame_rcvd").write(opcode);
214 
215  // Ignore anything that's not from the expected IP address.
216  if (m_addr.udp()->reply_ip() != m_addr.dstaddr()) return;
217 
218  // If FIRST flag is set, lock in the sender's source port.
219  // (i.e., This is how the client learns the UDP destination port.)
220  if (m_flags & FLAG_FIRST) {
221  clr_mask_u16(m_flags, FLAG_FIRST);
222  // Update the destination for outgoing packets.
223  m_addr.save_reply_address();
224  // Update the filter for incoming packets.
225  m_filter = Type(m_addr.dstport().value, m_addr.srcport().value);
226  // Log the new connection.
227  log::Log(log::INFO, "TFTP: Connected to server")
228  .write(m_addr.dstaddr())
229  .write(m_addr.dstport().value)
230  .write(m_addr.srcport().value);
231  }
232 
233  // Take further action based on the opcode:
234  if (opcode == OPCODE_ERROR) {
235  // Received ERROR, abort transfer immediately.
236  read_error(src);
237  reset("Connection reset by peer.");
238  } else if (m_dst && opcode == OPCODE_DATA) {
239  // Received DATA, read it if applicable and send ACK.
240  u16 block_id = src.read_u16();
241  read_data(block_id, src);
242  send_ack(block_id);
243  } else if (m_src && opcode == OPCODE_ACK) {
244  // Received ACK, send next DATA packet if applicable.
245  u16 block_id = src.read_u16();
246  send_data(block_id + 1);
247  } else {
248  // Any other opcode is an error.
249  send_error(ERROR_PROTOCOL);
250  }
251 }
252 
254  if (DEBUG_VERBOSE > 1)
255  log::Log(log::DEBUG, "TftpTransfer::timer_event").write10((u32)m_retry_count);
256 
257  // Timeout waiting for remote response...
258  if (m_dst && (m_flags & FLAG_EOF)) {
259  // Delayed termination (RFC 1350, Section 6)
260  reset("Transfer completed.");
261  } else if (m_retry_count <= RETRY_MAX) {
262  // Retry last packet up to N times.
263  send_packet(m_retry_len, m_retry_count+1);
264  } else {
265  // Abort transfer.
266  send_error(ERROR_TIMEOUT);
267  }
268 }
269 
270 void TftpTransfer::read_data(u16 block_id, satcat5::io::LimitedRead& src) {
271  if (DEBUG_VERBOSE > 1)
272  log::Log(log::DEBUG, "TftpTransfer::read_data").write(block_id);
273 
274  // If we've already got end-of-file, ignore all subsequent data.
275  if (m_flags & FLAG_EOF) return;
276 
277  // Read contents only for the next expected block.
278  u16 predicted = u16(m_block_id & 0xFFFF) + 1;
279  if (block_id == predicted) {
280  // Update block counter.
281  ++m_block_id;
282  // Copy the newly-received data.
283  unsigned len = src.get_read_ready();
284  m_xfer_bytes += len;
285  if (len > 0) src.copy_to(m_dst);
286  // Last block in file?
287  if (len < 512) {
288  m_dst->write_finalize();
289  set_mask_u16(m_flags, FLAG_EOF);
290  }
291  }
292 }
293 
294 void TftpTransfer::read_error(satcat5::io::LimitedRead& src) {
295  // Unpack the error string into the internal buffer.
296  // (We're about to close the connection, so it's OK to overwrite.)
297  u16 errcode = src.read_u16();
298  char* errstr = (char*)m_retry_buff;
299  src.read_str(sizeof(m_retry_buff), errstr);
300 
301  // Log the error and abort connection.
302  log::Log(log::WARNING, "TFTP: Remote error")
303  .write(errcode).write(": ").write(errstr);
304 }
305 
306 void TftpTransfer::send_ack(u16 block_id) {
307  if (DEBUG_VERBOSE > 1)
308  log::Log(log::DEBUG, "TftpTransfer::send_ack").write(block_id);
309 
310  // Compare 16 LSBs of received block to expected value.
311  // (Careful arithmetic here allows for wraparound.)
312  s16 diff = s16(block_id - u16(m_block_id & 0xFFFF));
313  if (diff < 0) {
314  // Ignore stale DATA packets, no ACK needed.
315  } else if (diff == 0) {
316  // Write out the ACK packet (Section 5).
317  ArrayWrite pkt(m_retry_buff, sizeof(m_retry_buff));
318  pkt.write_u16(OPCODE_ACK);
319  pkt.write_u16(block_id);
320  pkt.write_finalize();
321  // Send the ACK packet.
322  send_packet(pkt.written_len(), 0);
323  } else {
324  // Out-of-sequence block ID from incoming DATA packet.
325  send_error(ERROR_PROTOCOL);
326  }
327 }
328 
329 void TftpTransfer::send_data(u16 block_id) {
330  if (DEBUG_VERBOSE > 1)
331  log::Log(log::DEBUG, "TftpTransfer::send_data").write(block_id);
332 
333  // Compare 16 LSBs of received block to expected value.
334  // (Careful arithmetic here allows for wraparound.)
335  s16 diff = s16(block_id - u16(m_block_id & 0xFFFF));
336  if (diff < 0) {
337  // Ignore stale requests.
338  } else if (diff == 0) {
339  // Request for the previous packet.
340  send_packet(m_retry_len, 0);
341  } else if (m_flags & FLAG_EOF) {
342  // Transfer completed, nothing left to send.
343  // Close connection immediately (RFC 1350, Section 6)
344  reset("Transfer completed.");
345  } else if (diff == 1) {
346  // Write the packet header.
347  write_be_u16(m_retry_buff + 0, OPCODE_DATA);
348  write_be_u16(m_retry_buff + 2, block_id);
349  // Copy the next block of data (max 512 bytes).
350  ++m_block_id;
351  unsigned len = min_unsigned(512, m_src->get_read_ready());
352  m_xfer_bytes += len;
353  if (len > 0) m_src->read_bytes(len, m_retry_buff + 4);
354  if (len < 512) set_mask_u16(m_flags, FLAG_EOF);
355  // Send the DATA packet.
356  send_packet(len + 4, 0);
357  } else {
358  // Invalid block ID from incoming ACK packet.
359  send_error(ERROR_PROTOCOL);
360  }
361 }
362 
363 void TftpTransfer::send_error(u16 errcode) {
364  if (DEBUG_VERBOSE > 1)
365  log::Log(log::DEBUG, "TftpTransfer::send_error").write(errcode);
366 
367  // Lookup the human-readable error message.
368  const char* errstr = error_lookup(errcode);
369 
370  // Write out the ERROR packet (Section 5).
371  ArrayWrite pkt(m_retry_buff, sizeof(m_retry_buff));
372  pkt.write_u16(OPCODE_ERROR);
373  pkt.write_u16(errcode);
374  pkt.write_str(errstr);
375  pkt.write_u8(0);
376  pkt.write_finalize();
377 
378  // Send the error packet and reset connection.
379  send_packet(pkt.written_len(), 0);
380  reset(errstr);
381 }
382 
383 void TftpTransfer::send_packet(unsigned len, u16 retry) {
384  if (DEBUG_VERBOSE > 1) {
385  u16 opcode = satcat5::util::extract_be_u16(m_retry_buff);
386  log::Log(log::DEBUG, "TftpTransfer::send_packet").write(opcode);
387  }
388 
389  // Sanity check on input length.
390  if (len > sizeof(m_retry_buff)) return;
391 
392  // Exponential timeout doubles after each failed attempt.
393  bool last_ack = m_dst && (m_flags & FLAG_EOF);
394  m_retry_len = (u16)len;
395  m_retry_count = retry;
396  u32 timeout = RETRY_MSEC * (1u << retry);
397 
398  // Extended timeout when sending the last ACK message.
399  // (This prevents the dangling ACK from RFC1350, Section 6.)
400  if (last_ack) timeout <<= RETRY_MAX;
401 
402  // Add 50% randomization to reduce lockstep retransmission.
403  timer_once(timeout + satcat5::util::prng.next(0, timeout/2));
404 
405  // Attempt to send the packet.
406  auto wr = m_addr.open_write(len);
407  if (wr) {
408  wr->write_bytes(len, m_retry_buff);
409  wr->write_finalize();
410  } else if (DEBUG_VERBOSE > 1) {
411  log::Log(log::DEBUG, "TftpTransfer: Transmission delayed...");
412  }
413 }
414 
416  : m_xfer(iface)
417 {
418  // No other initialization required.
419 }
420 
423  const satcat5::ip::Addr& server,
424  const char* filename)
425 {
426  if (DEBUG_VERBOSE > 0) log::Log(log::DEBUG, "TftpClient::begin_download");
427  m_xfer.request(server, OPCODE_RRQ, filename);
428  m_xfer.file_recv(dst, false); // Wait for DATA1
429 }
430 
433  const satcat5::ip::Addr& server,
434  const char* filename)
435 {
436  if (DEBUG_VERBOSE > 0) log::Log(log::DEBUG, "TftpClient::begin_upload");
437  m_xfer.request(server, OPCODE_WRQ, filename);
438  m_xfer.file_send(src, false); // Wait for ACK0
439 }
440 
442  : satcat5::net::Protocol(TYPE_TFTP_SERVER)
443  , m_iface(iface)
444  , m_xfer(iface)
445 {
446  // Register for incoming packets on the TFTP server port.
447  m_iface->add(this);
448 }
449 
450 TftpServerCore::~TftpServerCore() {
451  m_iface->remove(this);
452 }
453 
455  if (DEBUG_VERBOSE > 1) log::Log(log::DEBUG, "TftpServer::frame_rcvd");
456 
457  // De-duplicate requests: If we've already opened a connection,
458  // don't open a new one with a different port number.
459  if (m_xfer.is_duplicate_request()) return;
460 
461  // Only respond to read-requests and write-requests.
462  char filename[256];
463  u16 opcode = src.read_u16();
464  if (opcode == OPCODE_RRQ) {
465  // Read filename and get I/O object.
466  src.read_str(sizeof(filename), filename);
467  auto file_src = read(filename);
468  // Begin read transfer (server to client)
469  m_xfer.accept();
470  if (file_src) {
471  m_xfer.file_send(file_src, true);
472  } else {
473  m_xfer.send_error(ERROR_NOFILE);
474  }
475  } else if (opcode == OPCODE_WRQ) {
476  // Read filename and get I/O object.
477  src.read_str(sizeof(filename), filename);
478  auto file_dst = write(filename);
479  // Begin write transfer (client to server)
480  m_xfer.accept();
481  if (file_dst) {
482  m_xfer.file_recv(file_dst, true);
483  } else {
484  m_xfer.send_error(ERROR_NOFILE);
485  }
486  }
487 }
488 
490  satcat5::udp::Dispatch* iface,
493  : TftpServerCore(iface)
494  , m_src(src)
495  , m_dst(dst)
496 {
497  // Nothing else to initialize.
498 }
499 
501  { return m_src; }
502 
503 satcat5::io::Writeable* TftpServerSimple::write(const char* filename)
504  { return m_dst; }
Ephemeral Writeable interface for a simple array.
Definition: io_writeable.h:127
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.
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
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
unsigned read_str(unsigned dst_size, char *dst)
Safely read a null-terminated input string.
Definition: io_readable.cc:179
virtual bool read_bytes(unsigned nbytes, void *dst)
Read 0 or more bytes into a buffer.
Definition: io_readable.cc:190
virtual void read_finalize()
Consume any remaining bytes in this frame, if applicable.
Definition: io_readable.cc:270
unsigned copy_to(satcat5::io::Writeable *dst)
Copy data to a Writeable object, without finalizing.
Definition: io_readable.cc:214
virtual unsigned get_read_ready() const =0
How many bytes can be read without blocking?
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 void write_abort()
If possible, abort the current partially-written packet.
void write_u8(u8 data)
One of many functions for writing integer/floating point values, see details.
Definition: io_writeable.cc:20
void write_str(const char *str)
Write the contents of a null-terminated string.
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 & write10(s32 val)
Print integer as a decimal value with no leading zeros.
Definition: log.cc:261
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
void remove(satcat5::net::Protocol *proto)
Unregister a Protocol object.
Definition: net_dispatch.h:59
satcat5::net::Type m_filter
Incoming packet filter.
Definition: net_protocol.h:52
void timer_stop()
Stop all future notifications.
Definition: polling.cc:326
void timer_once(unsigned msec)
Configure a one-time notification after X milliseconds.
Definition: polling.cc:316
void save_reply_address() override
Bind this Address object to the parent interface's current reply address, as provided in net::Dispatc...
Definition: udp_core.cc:75
satcat5::io::Writeable * open_write(unsigned len) override
Open a new frame to the designated address and type.
Definition: udp_core.cc:71
void connect(const satcat5::udp::Addr &dstaddr, const satcat5::eth::MacAddr &dstmac, const satcat5::udp::Port &dstport, const satcat5::udp::Port &srcport=satcat5::udp::PORT_NONE, const satcat5::eth::VlanTag &vtag=satcat5::eth::VTAG_NONE)
Manual address resolution (user supplies IP + MAC).
Definition: udp_core.cc:30
void close() override
Close any open connections and revert to idle.
Definition: udp_core.h:102
Dispatcher sorts incoming UDP messages by port index.
Definition: udp_dispatch.h:20
satcat5::udp::Port next_free_port()
Get the next unclaimed dynamically-allocated port index.
Definition: udp_dispatch.cc:48
A TFTP client makes request(s) to a remote server.
Definition: udp_tftp.h:111
void begin_upload(satcat5::io::Readable *src, const satcat5::ip::Addr &server, const char *filename)
Upload data from a Readable stream to the server.
Definition: udp_tftp.cc:431
TftpClient(satcat5::udp::Dispatch *iface)
Attach this client to a network interface.
Definition: udp_tftp.cc:415
void begin_download(satcat5::io::Writeable *dst, const satcat5::ip::Addr &server, const char *filename)
Download a file from server to a Writeable stream.
Definition: udp_tftp.cc:421
ServerCore is the base class that handles TFTP network functions.
Definition: udp_tftp.h:150
void frame_rcvd(satcat5::io::LimitedRead &src) override
Dispatch calls frame_rcvd(...) for each incoming frame with with a matching net::Type value.
Definition: udp_tftp.cc:454
virtual satcat5::io::Readable * read(const char *filename)=0
Child class MUST override these methods.
TftpServerCore(satcat5::udp::Dispatch *iface)
Users cannot instantiate this class directly.
Definition: udp_tftp.cc:441
TFTP server with a simple streaming source and sink.
Definition: udp_tftp.h:180
satcat5::io::Readable * read(const char *filename) override
Child class MUST override these methods.
Definition: udp_tftp.cc:500
TftpServerSimple(satcat5::udp::Dispatch *iface, satcat5::io::Readable *src, satcat5::io::Writeable *dst)
Attach this server to a network interface, and to a source and sink for transfered data.
Definition: udp_tftp.cc:489
Transfer objects used by both TftpClient and TftpServer.
Definition: udp_tftp.h:31
void file_send(satcat5::io::Readable *src, bool now)
Begin transfer of a single file (DATA-ACK-DATA-ACK).
Definition: udp_tftp.cc:170
void request(const satcat5::ip::Addr &dstaddr, u16 opcode, const char *filename)
Issue a write-request or read-request.
Definition: udp_tftp.cc:112
bool is_duplicate_request()
Before calling accept, test if this is a duplicate request.
Definition: udp_tftp.cc:137
void timer_event() override
Child class MUST override this method.
Definition: udp_tftp.cc:253
void accept()
Accept remote connection and note reply address.
Definition: udp_tftp.cc:149
void frame_rcvd(satcat5::io::LimitedRead &src) override
Dispatch calls frame_rcvd(...) for each incoming frame with with a matching net::Type value.
Definition: udp_tftp.cc:209
void file_recv(satcat5::io::Writeable *dst, bool now)
Begin transfer of a single file (DATA-ACK-DATA-ACK).
Definition: udp_tftp.cc:190
void send_error(u16 errcode)
Send an error message.
Definition: udp_tftp.cc:363
bool active() const
Is there a transfer in progress?
Definition: udp_tftp.h:38
void reset(const char *msg)
Immediately revert to the idle state.
Definition: udp_tftp.cc:82
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
Client and server for the Trivial File Transfer Protocol (TFTP)
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 write_be_u16(u8 *dst, u16 val)
Store fields into a big-endian byte array.
Definition: utils.cc:173
void set_mask_u16(u16 &val, u16 mask)
Set or clear bit masks.
Definition: utils.h:25