SatCat5
ip_address.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_address.h>
7 #include <satcat5/ip_dispatch.h>
8 
9 using satcat5::eth::MACADDR_BROADCAST;
10 using satcat5::eth::MACADDR_NONE;
13 using satcat5::eth::VTAG_NONE;
14 using satcat5::ip::Addr;
17 namespace ip = satcat5::ip;
18 
19 // Set default rate-limiter for outgoing ARP requests?
20 // (i.e., Minimum time between requests, in milliseconds.)
21 #ifndef SATCAT5_ARP_RETRY_MSEC
22 #define SATCAT5_ARP_RETRY_MSEC 100
23 #endif
24 
25 Address::Address(Dispatch* iface, u8 proto)
26  : m_iface(nullptr)
27  , m_proto(proto)
28  , m_ready(0)
29  , m_arp_tref(SATCAT5_CLOCK->now())
30  , m_dstmac(MACADDR_BROADCAST)
31  , m_dstaddr(ip::ADDR_NONE)
32  , m_gateway(ip::ADDR_NONE)
33  , m_vtag(VTAG_NONE)
34 {
35  init(iface);
36 }
37 
38 #if SATCAT5_ALLOW_DELETION
39 Address::~Address() {
40  if (m_iface) {
41  close();
42  m_iface->arp()->remove(this);
43  }
44 }
45 #endif
46 
47 void Address::init(Dispatch* iface) {
48  if (iface && !m_iface) {
49  m_iface = iface;
50  m_iface->arp()->add(this);
51  }
52 }
53 
54 void Address::connect(const Addr& dstaddr, const VlanTag& vtag) {
55  if (!m_iface) return;
56 
57  // Cleanup existing connection, if any.
58  close();
59 
60  // Set destination MAC to a placeholder for now.
61  auto route = m_iface->route_lookup(dstaddr);
62  m_dstaddr = dstaddr;
63  m_dstmac = route.dstmac;
64  m_gateway = route.gateway;
65  m_vtag = vtag;
66  m_arp_tref = SATCAT5_CLOCK->now();
67 
68  // Do we need to issue an ARP query?
69  if (m_dstmac.is_unicast() || m_gateway.is_broadcast()) {
70  m_ready = 1; // Cached MAC or broadcast = Ready
71  } else if (m_gateway.is_multicast()) {
72  m_ready = 1; // Multicast IP address = Ready + Join
73  m_igmp.join(m_iface->igmp(), m_gateway);
74  } else if (m_gateway.is_unicast()) {
75  m_ready = 0; // Unknown MAC = Start ARP query
76  m_iface->arp()->send_query(m_gateway);
77  } else {
78  m_ready = 0; // Invalid IP = Halt
79  }
80 }
81 
82 void Address::connect(const Addr& dstaddr, const MacAddr& dstmac, const VlanTag& vtag) {
83  // Cleanup existing connection, if any.
84  close();
85 
86  // User has provided all required parameters.
87  // Note: DHCP requires dstaddr = 0 for some edge-cases.
88  m_dstmac = dstmac;
89  m_dstaddr = dstaddr;
90  m_gateway = ip::ADDR_NONE;
91  m_vtag = vtag;
92  m_ready = (dstmac != MACADDR_NONE) ? 1 : 0;
93 
94  // Are we joining a multicast group?
95  if (m_dstaddr.is_multicast()) {
96  m_gateway = m_dstaddr;
97  m_igmp.join(m_iface->igmp(), m_gateway);
98  }
99 }
100 
102  // Retry ARP query (automatic address resolution only).
103  if (m_iface && !m_ready) {
104  m_iface->arp()->send_query(m_gateway, m_vtag);
105  }
106 }
107 
109  // If applicable, leave the multicast group.
110  if (m_iface && m_gateway.is_multicast()) {
111  m_igmp.leave(m_iface->igmp());
112  }
113 
114  // Reset to the idle state.
115  m_dstmac = MACADDR_BROADCAST;
116  m_dstaddr = ip::ADDR_NONE;
117  m_gateway = ip::ADDR_NONE;
118  m_ready = 0;
119 }
120 
122  { return m_iface; }
123 
125  if (m_ready) {
126  // Send the IP packet.
127  return m_iface->open_write(m_dstmac, m_vtag, m_dstaddr, m_proto, len);
128  } else if (m_iface && m_arp_tref.interval_msec(SATCAT5_ARP_RETRY_MSEC)) {
129  // If we haven't gotten an ARP reply, try again subject to rate-limit.
130  m_iface->arp()->send_query(m_gateway, m_vtag);
131  }
132  return 0; // Unable to send.
133 }
134 
135 bool Address::is_multicast() const {
136  return m_dstaddr.is_multicast();
137 }
138 
140  if (!m_iface) return false;
141  bool eth_match = m_dstmac.is_multicast() || m_dstmac == m_iface->reply_mac();
142  bool ip_match = m_dstaddr.is_multicast() || m_dstaddr == m_iface->reply_ip();
143  bool vid_check = m_iface->reply_vtag().vid() && m_vtag.vid();
144  bool vid_match = (!vid_check) || (m_iface->reply_vtag().vid() == m_vtag.vid());
145  return eth_match && ip_match && vid_match;
146 }
147 
149  return m_iface && m_iface->reply_is_multicast();
150 }
151 
153  if (m_iface) {
154  m_dstmac = m_iface->reply_mac();
155  m_dstaddr = m_iface->reply_ip();
156  m_gateway = ip::ADDR_NONE;
157  m_ready = 1;
158  }
159 }
160 
161 void Address::arp_event(const MacAddr& mac, const Addr& ip) {
162  // If we get a match, update the stored MAC address.
163  if (ip == m_gateway) {
164  m_dstmac = mac;
165  m_ready = 1;
166  }
167 }
168 
169 void Address::gateway_change(const Addr& dstaddr, const Addr& gateway) {
170  // Is this redirect relevant to our destination address?
171  if ((dstaddr == m_dstaddr) && (gateway != m_gateway)) {
172  // Immediately update the next-hop gateway address.
173  m_gateway = gateway;
174  // Is the new gateway in our MAC address cache?
175  // Otherwise, initiate an ARP query.
176  auto route = m_iface->route_lookup(dstaddr);
177  if (route.dstmac.is_valid()) m_dstmac = route.dstmac;
178  else m_iface->arp()->send_query(m_gateway);
179  // In either case, keep the socket open and ready. The previous
180  // gateway can keep forwarding packets until we get an ARP response.
181  }
182 }
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
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
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
void close() override
Close any open connections and revert to idle.
Definition: ip_address.cc:108
void arp_event(const satcat5::eth::MacAddr &mac, const satcat5::ip::Addr &ip) override
Callback for any announced MAC/IP address pair.
Definition: ip_address.cc:161
void save_reply_address() override
Bind this Address object to the parent interface's current reply address, as provided in net::Dispatc...
Definition: ip_address.cc:152
bool reply_is_multicast() const override
Was the parent interface's incoming message sent to a multicast address? (i.e., Could that message ha...
Definition: ip_address.cc:148
satcat5::net::Dispatch * iface() const override
Fetch a pointer to the underlying interface.
Definition: ip_address.cc:121
void gateway_change(const satcat5::ip::Addr &dstaddr, const satcat5::ip::Addr &gateway) override
Callback for changes to gateway configuration.
Definition: ip_address.cc:169
satcat5::io::Writeable * open_write(unsigned len) override
Open a new frame to the designated address and type.
Definition: ip_address.cc:124
bool matches_reply_address() const override
Does this Address object match the parent interface's current reply address? Multicast destinations s...
Definition: ip_address.cc:139
bool is_multicast() const override
Is the destination a broadcast or multicast address? Child MUST override these method.
Definition: ip_address.cc:135
void retry() override
If this Address is not in the ready() state, reattempt any steps required to do so,...
Definition: ip_address.cc:101
void init(satcat5::ip::Dispatch *iface)
Deferred initialization of the upstream interface.
Definition: ip_address.cc:47
Address(satcat5::ip::Dispatch *iface, u8 proto)
Create this object and bind it to a network interface.
Definition: ip_address.cc:25
Protocol handler and dispatch unit for Internet Protocol v4 (IPv4).
Definition: ip_dispatch.h:43
satcat5::io::Writeable * open_write(const satcat5::eth::MacAddr &mac, const satcat5::eth::VlanTag &vtag, const satcat5::ip::Addr &ip, u8 protocol, unsigned len)
Get Writeable object for sending to a specific IP/MAC address.
Definition: ip_dispatch.cc:55
satcat5::ip::Route route_lookup(const satcat5::ip::Addr &dstaddr) const
Routing table shortcuts.
Definition: ip_dispatch.h:108
The "Dispatch" is a generic interface that knows how to read a designated protocol layer and sort inc...
Definition: net_dispatch.h:36
An Ethernet MAC address (with serializable interface).
Definition: eth_header.h:29
bool is_multicast() const
Broadcast, L2 multicast, or L3 multicast.
Definition: eth_header.cc:57
bool is_unicast() const
Any normal single-destination address.
Definition: eth_header.cc:74
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
void join(satcat5::igmp::Client *igmp, const satcat5::ip::Addr &addr)
Join a multicast group.
Definition: igmp_client.h:162
void leave(satcat5::igmp::Client *igmp)
Leave a multicast group.
Definition: igmp_client.h:168
IPv4 address is a 32-bit unsigned integer.
Definition: ip_core.h:15
bool is_multicast() const
IP multicast (224.*.*.*)
Definition: ip_core.cc:41
bool is_unicast() const
Any normal single-destination address.
Definition: ip_core.cc:59
bool is_broadcast() const
Limited broadcast (255.255.255.255)
Definition: ip_core.cc:37
bool interval_msec(unsigned msec)
Test an interval measured in milliseconds.
Definition: timeref.cc:60