SatCat5
router2_deferfwd.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/ip_dispatch.h>
7 #include <satcat5/router2_deferfwd.h>
8 #include <satcat5/router2_dispatch.h>
9 
15 
16 // Set verbosity level for debugging (0/1/2)
17 static constexpr unsigned DEBUG_VERBOSE = 0;
18 
19 // Set default retry parameters:
20 #ifndef SATCAT5_R2_RETRY_MAX // Maximum number of retries
21 #define SATCAT5_R2_RETRY_MAX 4
22 #endif
23 
24 #ifndef SATCAT5_R2_RETRY_MSEC // Timeout for the first retry
25 #define SATCAT5_R2_RETRY_MSEC 10
26 #endif
27 
28 bool DeferPkt::read_meta(PluginPacket& meta) {
29  // Read the Ethernet and IPv4 headers.
30  bool ok = meta.read_from(pkt); // Read packet headers
31  meta.dst_mask = dst_mask; // Restore the destination mask
32  return ok;
33 }
34 
35 DeferFwd::DeferFwd(Dispatch* parent, DeferPkt* buff, unsigned bcount)
36  : m_parent(parent)
37  , m_arp(0)
38  , m_tref(SATCAT5_CLOCK->now())
39  , m_active()
40  , m_empty()
41 {
42  // Initialize the list of empty slots.
43  for (unsigned a = 0 ; a < bcount ; ++a) {
44  m_empty.add(buff + a);
45  }
46 
47  // Start the timer.
48  timer_every(3);
49 }
50 
51 #if SATCAT5_ALLOW_DELETION
52 DeferFwd::~DeferFwd() {
53  // Unregister ARP handler if applicable.
54  if (m_arp) m_arp->remove(this);
55 }
56 #endif
57 
58 bool DeferFwd::accept(const PluginPacket& meta) {
59  // First-time setup of the interface? Register for ARP callbacks.
60  // (This information may not be available during object creation.)
61  if (!m_parent->iface()) return false;
62  if (!m_arp) {
63  m_arp = m_parent->iface()->arp();
64  m_arp->add(this);
65  m_tref = SATCAT5_CLOCK->now();
66  }
67 
68  // Is there an empty slot ready?
69  DeferPkt* next = m_empty.pop_front();
70  if (next) {
71  // Store the new packet on the active list.
72  next->pkt = meta.pkt;
73  next->dst_ip = meta.ip.dst();
74  next->dst_mask = meta.dst_mask;
75  next->sent = 0;
76  next->trem = 0;
77  m_active.add(next);
78  // Attempt to send the first ARP request.
79  request_arp(next);
80  }
81  return !!next;
82 }
83 
84 void DeferFwd::arp_event(const MacAddr& mac, const satcat5::ip::Addr& ip) {
85  // Check the incoming MAC/IP pair against each pending packet.
86  // If we find a match, pass it back to the router for delivery.
87  DeferPkt* pkt = m_active.head();
88  while (pkt) {
89  pkt = (pkt->dst_ip == ip) ? request_fwd(pkt, mac) : m_active.next(pkt);
90  }
91 }
92 
94  // Sanity check that the parent has been fully configured.
95  if (!m_arp) return;
96 
97  // Elapsed time since last timer_event().
98  u32 elapsed = m_tref.increment_msec();
99 
100  // Decrement remaining time on each queued packet.
101  // When it reaches zero, send another ARP request or discard the packet.
102  DeferPkt* pkt = m_active.head();
103  while (pkt) {
104  if (pkt->trem < elapsed) {
105  pkt = request_arp(pkt);
106  } else {
107  pkt->trem -= elapsed;
108  pkt = m_active.next(pkt);
109  }
110  }
111 }
112 
113 DeferPkt* DeferFwd::request_arp(DeferPkt* pkt) {
114  // Note the "next" pointer before we mutate the list.
115  DeferPkt* next = m_active.next(pkt);
116 
117  // Check the number of previous attempts...
118  if (pkt->sent <= SATCAT5_R2_RETRY_MAX) {
119  // Exponential backoff when setting the next timeout.
120  pkt->trem = u16(SATCAT5_R2_RETRY_MSEC) << (pkt->sent++);
121  // Attempt to send the next ARP request.
122  // (OK if this fails, timeout is the same either way.)
123  m_arp->send_query(pkt->dst_ip);
124  } else {
125  // Retry limit exceeded, send an ICMP error.
126  PluginPacket meta;
127  if (pkt->read_meta(meta))
128  m_parent->icmp_reply(satcat5::ip::ICMP_UNREACHABLE_HOST, 0, meta);
129  // Discard the original packet and mark it as empty.
130  m_parent->free_packet(pkt->pkt);
131  m_active.remove(pkt);
132  m_empty.add(pkt);
133  }
134 
135  // Return the next item for continued processing.
136  return next;
137 }
138 
139 DeferPkt* DeferFwd::request_fwd(DeferPkt* pkt, const MacAddr& dst) {
140  // Note the "next" pointer before we mutate the list.
141  DeferPkt* next = m_active.next(pkt);
142 
143  // Reconstitute the packet and forward to the designated MAC address.
144  // (This packet has already been validated and had its TTL decremented.)
145  unsigned count = 0;
146  PluginPacket meta;
147  if (pkt->read_meta(meta)) {
148  m_parent->adjust_mac(dst, meta);
149  auto debug = m_parent->m_debug;
150  if (DEBUG_VERBOSE > 0 && debug) meta.pkt->copy_to(debug);
151  count = m_parent->deliver_offload(meta)
152  + m_parent->deliver_switch(meta);
153  }
154 
155  // If delivery failed, delete the packet buffer.
156  if (count == 0) m_parent->free_packet(pkt->pkt);
157 
158  // In all cases, mark the queue slot as empty.
159  m_active.remove(pkt);
160  m_empty.add(pkt);
161 
162  // Return the next item for continued processing.
163  return next;
164 }
bool send_query(const satcat5::ip::Addr &target, const satcat5::eth::VlanTag &vtag=satcat5::eth::VTAG_NONE)
Send a query for a given IP address.
Definition: eth_arp.cc:112
void add(satcat5::eth::ArpListener *evt)
Register an event-listener.
Definition: eth_arp.h:76
void remove(satcat5::eth::ArpListener *evt)
Unregister an event-listener.
Definition: eth_arp.h:79
unsigned deliver_switch(const satcat5::eth::PluginPacket &pkt)
Internal event-handlers called from deliver(...).
Definition: eth_switch.cc:248
void free_packet(satcat5::io::MultiPacket *packet)
Immediately free memory associated with this packet.
void timer_every(unsigned msec)
Configure a repeating notification every X milliseconds.
Definition: polling.cc:321
Deferred packet-forwarding system for the IPv4 router.
void timer_event() override
Child class MUST override this method.
DeferFwd(satcat5::router2::Dispatch *parent, satcat5::router2::DeferPkt *buff, unsigned bcount)
Constructor should only be accessed by children, and requires a backing array of empty DeferPkt objec...
bool accept(const satcat5::eth::PluginPacket &meta)
Accept this packet into the queue?
void arp_event(const satcat5::eth::MacAddr &mac, const satcat5::ip::Addr &ip) override
Callback for any announced MAC/IP address pair.
Packet-processing pipeline for the IPv4 router.
T * next(const T *item) const
Fetch pointer to the next item.
Definition: list.h:261
void add(T *item)
Add new item to front or back, whichever is simpler.
Definition: list.h:221
T * pop_front()
Remove the item at the head of the list.
Definition: list.h:265
void remove(T *item)
Remove the designated item from the list.
Definition: list.h:277
An Ethernet MAC address (with serializable interface).
Definition: eth_header.h:29
Ephemeral data structure provided to plugin callbacks.
Definition: eth_plugin.h:48
satcat5::io::MultiPacket * pkt
Complete packet contents.
Definition: eth_plugin.h:51
satcat5::ip::Header ip
Copy of additional header fields, if present.
Definition: eth_plugin.h:62
SATCAT5_PMASK_TYPE dst_mask
Destination mask for which port(s) receive this packet.
Definition: eth_plugin.h:75
bool read_from(satcat5::io::MultiPacket *packet)
Read metadata from a packet object.
Definition: eth_plugin.cc:20
bool copy_to(satcat5::io::Writeable *wr) const
Copy the packet contents to the specified destination.
Definition: multi_buffer.cc:36
IPv4 address is a 32-bit unsigned integer.
Definition: ip_core.h:15
constexpr satcat5::ip::Addr dst() const
< Destination address
Definition: ip_core.h:210
State information for a single deferred packet.
satcat5::ip::Addr dst_ip
Destination address.
satcat5::io::MultiPacket * pkt
Packet object.
SATCAT5_PMASK_TYPE dst_mask
Destination port-mask.
u16 trem
Remaining time in msec.
bool read_meta(satcat5::eth::PluginPacket &meta)
Reconstitute switch PacketMeta from this object.
u16 sent
Number of attempts so far.
unsigned increment_msec()
Measure elapsed time in milliseconds, then increment by the returned quantized value.
Definition: timeref.cc:39