10 #include <satcat5/udp_dispatch.h>
15 using satcat5::eth::MACADDR_BROADCAST;
20 using satcat5::ip::ADDR_BROADCAST;
21 using satcat5::ip::ADDR_NONE;
22 using satcat5::ip::DEFAULT_ROUTE;
29 using satcat5::udp::PORT_DHCP_CLIENT;
30 using satcat5::udp::PORT_DHCP_SERVER;
32 namespace log = satcat5::log;
35 static constexpr
unsigned DEBUG_VERBOSE = 0;
38 static constexpr
Type TYPE_CLIENT =
Type(PORT_DHCP_CLIENT.
value);
39 static constexpr
Type TYPE_SERVER =
Type(PORT_DHCP_SERVER.
value);
42 static constexpr u8 OP_REQUEST = 1;
43 static constexpr u8 OP_REPLY = 2;
46 static constexpr u32 DHCP_MAGIC = 0x63825363;
49 static constexpr u16 FLAG_BROADCAST = 0x8000;
52 static constexpr
unsigned MACADDR_LEN = 6;
53 static constexpr
unsigned CHADDR_LEN = 16;
54 static constexpr
unsigned LEGACY_BYTES = 192;
55 static constexpr
unsigned LEGACY_WORDS = LEGACY_BYTES / 4;
58 static constexpr u8 DHCP_DISCOVER = 1;
59 static constexpr u8 DHCP_OFFER = 2;
60 static constexpr u8 DHCP_REQUEST = 3;
61 static constexpr u8 DHCP_DECLINE = 4;
62 static constexpr u8 DHCP_ACK = 5;
63 static constexpr u8 DHCP_NAK = 6;
64 static constexpr u8 DHCP_RELEASE = 7;
65 static constexpr u8 DHCP_INFORM = 8;
69 static constexpr u8 OPTION_PAD = 0;
70 static constexpr u8 OPTION_SUBNET_MASK = 1;
71 static constexpr u8 OPTION_ROUTER = 3;
72 static constexpr u8 OPTION_DNS_SERVER = 6;
73 static constexpr u8 OPTION_DOMAIN_NAME = 15;
74 static constexpr u8 OPTION_REQUEST_IP = 50;
75 static constexpr u8 OPTION_LEASE_TIME = 51;
76 static constexpr u8 OPTION_MSG_TYPE = 53;
77 static constexpr u8 OPTION_SERVER_IP = 54;
78 static constexpr u8 OPTION_CLIENT_ID = 61;
79 static constexpr u8 OPTION_END = 255;
83 static constexpr u16 make_optlen(u8 opcode, u8 len)
84 {
return u16(256*opcode + len); }
85 static constexpr u16 OPTLEN_SUBNET_MASK = make_optlen(OPTION_SUBNET_MASK, 4);
86 static constexpr u16 OPTLEN_ROUTER = make_optlen(OPTION_ROUTER, 4);
87 static constexpr u16 OPTLEN_REQUEST_IP = make_optlen(OPTION_REQUEST_IP, 4);
88 static constexpr u16 OPTLEN_LEASE_TIME = make_optlen(OPTION_LEASE_TIME, 4);
89 static constexpr u16 OPTLEN_MSG_TYPE = make_optlen(OPTION_MSG_TYPE, 1);
90 static constexpr u16 OPTLEN_SERVER_IP = make_optlen(OPTION_SERVER_IP, 4);
93 static constexpr u32 TIME_INIT_FIRST = 3;
94 static constexpr u32 TIME_INIT_RETRY = 5;
95 static constexpr u32 TIME_LEASE_DEFAULT = 24 * 60 * 60;
96 static constexpr u32 TIME_LEASE_OFFER = 30;
97 static constexpr u32 TIME_WAIT_ARP = 3;
98 static constexpr u32 TIME_WAIT_OFFER = 5;
99 static constexpr u32 TIME_WAIT_RENEW = 30;
100 static constexpr u32 TIME_WAIT_REBIND = 30;
101 static constexpr u32 TIME_WAIT_REQUEST = 5;
104 static constexpr u32 CLIENT_NONE = 0;
105 static constexpr u32 CLIENT_RSVD = 1;
108 static constexpr
DhcpAddress LEASE_NONE = {CLIENT_NONE, 0};
111 inline bool lease_expired(
DhcpAddress* meta, u32 tref)
116 if (meta->
client != CLIENT_NONE) {
117 s32 rem = (s32)(meta->
timeout - tref);
125 static void log_dhcp_info(
const char* msg, u32 yiaddr)
129 : satcat5::net::Protocol(TYPE_CLIENT)
135 , m_ipaddr(ADDR_NONE)
138 , m_timeout(TIME_INIT_FIRST)
141 , m_xid(crc32(MACADDR_LEN, iface->macaddr().addr))
144 m_xid += SATCAT5_CLOCK->raw();
153 #if SATCAT5_ALLOW_DELETION
154 DhcpClient::~DhcpClient() {
159 send_message(DHCP_RELEASE);
164 if (DEBUG_VERBOSE > 1)
165 log::Log(log::DEBUG,
"DHCP client",
"User inform");
168 send_message(DHCP_RELEASE);
169 m_iface->iface()->
set_addr(new_addr);
172 if (new_addr != ADDR_NONE) {
173 m_state = DhcpState::INFORMING;
179 if (DEBUG_VERBOSE > 1)
180 log::Log(log::DEBUG,
"DHCP client",
"User release");
182 send_message(DHCP_RELEASE);
183 m_iface->iface()->
set_addr(new_addr);
187 if (DEBUG_VERBOSE > 1)
188 log::Log(log::DEBUG,
"DHCP client",
"User renew");
193 send_message(DHCP_REQUEST);
196 send_message(DHCP_DISCOVER);
202 if (
m_state == DhcpState::BOUND) {
203 return m_timeout + TIME_WAIT_REBIND + TIME_WAIT_RENEW;
204 }
else if (
m_state == DhcpState::RENEWING) {
206 }
else if (
m_state == DhcpState::REBINDING) {
214 if (DEBUG_VERBOSE > 1)
215 log::Log(log::DEBUG,
"DHCP client",
"arp_event");
219 if (ip ==
m_ipaddr && mac != m_iface->macaddr()) {
221 log::Log(log::WARNING,
"DHCP client",
"Address already claimed");
222 m_iface->arp()->
remove(
this);
223 send_message(DHCP_DECLINE);
228 if (DEBUG_VERBOSE > 1)
229 log::Log(log::DEBUG,
"DHCP client",
"frame_rcvd");
232 if (
m_state != DhcpState::SELECTING &&
233 m_state != DhcpState::REQUESTING &&
234 m_state != DhcpState::RENEWING &&
235 m_state != DhcpState::REBINDING &&
236 m_state != DhcpState::INFORMING)
return;
243 u32 xid = src.read_u32();
245 u32 yiaddr = src.read_u32();
247 u32 magic = src.read_u32();
251 if (op != OP_REPLY)
return;
252 if (htype != 1 || hlen != 6)
return;
253 if (xid !=
m_xid)
return;
254 if (magic != DHCP_MAGIC)
return;
267 if (typ == OPTION_PAD)
continue;
268 if (typ == OPTION_END)
break;
271 u16 optlen = make_optlen(typ, len);
272 if (optlen == OPTLEN_SUBNET_MASK) {
273 subnet = src.read_u32();
274 }
else if (optlen == OPTLEN_ROUTER) {
275 router = src.read_u32();
276 }
else if (optlen == OPTLEN_LEASE_TIME) {
277 lease_time = src.read_u32();
278 }
else if (optlen == OPTLEN_MSG_TYPE) {
280 }
else if (optlen == OPTLEN_SERVER_IP) {
281 server = src.read_u32();
288 if (DEBUG_VERBOSE > 0)
289 log::Log(log::DEBUG,
"DHCP client",
"Received").
write(opcode);
292 if (opcode == DHCP_OFFER &&
m_state == DhcpState::SELECTING) {
297 m_iface->iface()->
set_addr(ADDR_NONE);
302 m_iface->arp()->
add(
this);
304 }
else if (opcode == DHCP_ACK &&
m_state == DhcpState::INFORMING) {
306 log_dhcp_info(
"Information", yiaddr);
309 if (router && subnet)
311 }
else if (opcode == DHCP_ACK && server ==
m_server_id) {
313 if (lease_time > TIME_WAIT_RENEW + TIME_WAIT_REBIND
315 log_dhcp_info(
"Lease granted", yiaddr);
318 m_timeout = lease_time - TIME_WAIT_RENEW - TIME_WAIT_REBIND;
321 if (router && subnet)
325 log_dhcp_info(
"Lease invalid", yiaddr);
326 send_message(DHCP_RELEASE);
328 }
else if (opcode == DHCP_NAK && server ==
m_server_id) {
330 log::Log(log::WARNING,
"DHCP client",
"Request refused").
write(yiaddr);
334 m_iface->iface()->
set_addr(ADDR_NONE);
338 if (
m_state == DhcpState::BOUND ||
m_state == DhcpState::TESTING) {
341 m_iface->reply_ip(), m_iface->reply_mac(),
342 PORT_DHCP_SERVER, PORT_DHCP_CLIENT);
358 void DhcpClient::next_timer() {
359 if (DEBUG_VERBOSE > 1)
360 log::Log(log::DEBUG,
"DHCP client",
"next_timer");
363 case DhcpState::INIT:
364 case DhcpState::SELECTING:
365 case DhcpState::REBINDING:
366 case DhcpState::REQUESTING:
368 send_message(DHCP_DISCOVER);
370 case DhcpState::TESTING:
372 m_iface->arp()->
remove(
this);
373 send_message(DHCP_REQUEST);
375 case DhcpState::BOUND:
376 case DhcpState::RENEWING:
378 send_message(DHCP_REQUEST);
380 case DhcpState::INFORMING:
381 send_message(DHCP_INFORM);
387 void DhcpClient::send_message(u8 opcode) {
388 if (DEBUG_VERBOSE > 1)
389 log::Log(log::DEBUG,
"DHCP client",
"Sending opcode").
write(opcode);
392 if (opcode == DHCP_DISCOVER) {
393 m_state = DhcpState::SELECTING;
395 }
else if (opcode == DHCP_REQUEST &&
m_state == DhcpState::TESTING) {
396 m_state = DhcpState::REQUESTING;
398 }
else if (opcode == DHCP_REQUEST &&
m_state == DhcpState::BOUND) {
401 }
else if (opcode == DHCP_REQUEST) {
402 m_state = DhcpState::REBINDING;
404 }
else if (opcode == DHCP_DECLINE) {
407 }
else if (opcode == DHCP_RELEASE) {
410 }
else if (opcode == DHCP_INFORM) {
411 m_state = DhcpState::INFORMING;
413 }
else if (DEBUG_VERBOSE > 0) {
414 log::Log(log::ERROR,
"DHCP client",
"Unexpected command");
418 if (opcode == DHCP_DISCOVER || opcode == DHCP_INFORM ||
419 opcode == DHCP_DECLINE || opcode == DHCP_RELEASE)
423 if (opcode == DHCP_RELEASE &&
m_ipaddr == ADDR_NONE)
return;
427 if (opcode == DHCP_REQUEST && !
status())
429 if (opcode == DHCP_DISCOVER || opcode == DHCP_INFORM)
431 if (
m_state == DhcpState::REBINDING)
435 ADDR_BROADCAST, MACADDR_BROADCAST,
436 PORT_DHCP_SERVER, PORT_DHCP_CLIENT);
440 MacAddr macaddr = m_iface->macaddr();
441 u8 chaddr[CHADDR_LEN];
442 for (
unsigned a = 0 ; a < CHADDR_LEN ; ++a)
443 chaddr[a] = (a < MACADDR_LEN) ? macaddr.
addr[a] : 0;
447 u32 ciaddr = 0, reqaddr = 0;
448 if ((opcode == DHCP_RELEASE) || (opcode == DHCP_REQUEST &&
status() > 0)) {
452 }
else if (opcode == DHCP_INFORM) {
454 ciaddr = m_iface->ipaddr().
value;
464 if (opcode == DHCP_REQUEST &&
m_state == DhcpState::REQUESTING)
466 else if (opcode == DHCP_DECLINE || opcode == DHCP_RELEASE)
471 static constexpr
unsigned BUFF_SIZE = 64 + SATCAT5_DHCP_MAX_ID_LEN;
474 opt.write_u16(OPTLEN_MSG_TYPE);
478 opt.write_u16(OPTLEN_REQUEST_IP);
479 opt.write_u32(reqaddr);
482 if (opcode == DHCP_DISCOVER || opcode == DHCP_REQUEST) {
483 opt.write_u16(OPTLEN_LEASE_TIME);
484 opt.write_u32(TIME_LEASE_DEFAULT);
488 opt.write_u16(OPTLEN_SERVER_IP);
489 opt.write_u32(server);
492 if (SATCAT5_DHCP_MAX_ID_LEN >= 1 &&
493 SATCAT5_DHCP_MAX_ID_LEN <= 254 &&
494 m_client_id && m_client_id->id_len &&
495 m_client_id->id_len <= SATCAT5_DHCP_MAX_ID_LEN) {
496 opt.write_u16(make_optlen(OPTION_CLIENT_ID, m_client_id->id_len + 1));
498 opt.
write_bytes(m_client_id->id_len, m_client_id->id);
510 dst->write_u32(0x01010600);
511 dst->write_u32(
m_xid);
514 dst->write_u32(ciaddr);
519 for (
unsigned a = 0 ; a < LEGACY_WORDS ; ++a)
521 dst->write_u32(DHCP_MAGIC);
529 : satcat5::net::Protocol(TYPE_SERVER)
533 , m_max_lease(TIME_LEASE_DEFAULT)
538 , m_gateway(DEFAULT_ROUTE)
544 if (next) *next = LEASE_NONE;
555 #if SATCAT5_ALLOW_DELETION
556 DhcpServer::~DhcpServer() {
580 log_dhcp_info(
"Local request", addr.
value);
581 if (addr == ADDR_NONE)
582 return offer(CLIENT_RSVD, addr.
value, lease_seconds);
584 return reserve(CLIENT_RSVD, addr.
value, lease_seconds);
588 if (DEBUG_VERBOSE > 1)
589 log::Log(log::DEBUG,
"DHCP server",
"frame_rcvd");
592 static constexpr
unsigned MAX_OPTION = 255;
593 u8 chaddr[CHADDR_LEN];
594 u8 buffer[MAX_OPTION];
601 u32 xid = src.read_u32();
603 u16 flags = src.read_u16();
604 u32 ciaddr = src.read_u32();
606 u32 giaddr = src.read_u32();
609 u32 magic = src.read_u32();
613 if (op != OP_REQUEST)
return;
614 if (htype != 1 || hlen != 6)
return;
615 if (magic != DHCP_MAGIC)
return;
621 u32 client = crc32(CHADDR_LEN, chaddr);
625 bool opt_complete =
false;
627 u32 lease_time = TIME_LEASE_DEFAULT;
631 if (typ == OPTION_PAD)
continue;
632 if (typ == OPTION_END) {opt_complete =
true;
break;}
637 u16 optlen = make_optlen(typ, len);
638 if (optlen == OPTLEN_REQUEST_IP) {
640 ciaddr = src.read_u32();
641 }
else if (optlen == OPTLEN_LEASE_TIME) {
644 }
else if (optlen == OPTLEN_MSG_TYPE) {
647 }
else if (typ == OPTION_CLIENT_ID) {
650 client = crc32(len, buffer);
658 if (!opt_complete)
return;
661 if (DEBUG_VERBOSE > 1)
662 log::Log(log::DEBUG,
"DHCP server",
"Received opcode").
write(opcode);
665 if (client == CLIENT_NONE || client == CLIENT_RSVD) client = ~client;
668 const char* log_msg =
"Message ignored";
669 s8 log_typ = log::INFO;
671 Addr reply_addr(0), yiaddr(ciaddr);
672 if (opcode == DHCP_DISCOVER) {
674 log_msg =
"Discover";
675 yiaddr = offer(client, ciaddr, TIME_LEASE_OFFER);
676 if (yiaddr != ADDR_NONE) reply_type = DHCP_OFFER;
677 }
else if (opcode == DHCP_REQUEST) {
679 yiaddr = reserve(client, ciaddr, lease_time);
680 if (yiaddr != ADDR_NONE) {
681 log_msg =
"Request granted";
683 reply_type = DHCP_ACK;
684 }
else if (m_pool->
contains(ciaddr)) {
685 log_msg =
"Request refused";
686 log_typ = log::WARNING;
687 reply_addr = m_iface->reply_ip();
688 reply_type = DHCP_NAK;
690 }
else if (opcode == DHCP_DECLINE) {
694 log_msg =
"Lease declined";
695 log_typ = log::WARNING;
698 }
else if (opcode == DHCP_RELEASE) {
701 if (meta && client == meta->
client) {
702 log_msg =
"Release granted";
705 }
else if (opcode == DHCP_INFORM) {
707 log_msg =
"Information request";
708 reply_addr = m_iface->reply_ip();
709 reply_type = DHCP_ACK;
713 log::Log(log_typ,
"DHCP server", log_msg)
717 if (reply_type == 0)
return;
718 if (DEBUG_VERBOSE > 1)
719 log::Log(log::DEBUG,
"DHCP server",
"Sending opcode").
write(reply_type);
729 if (reply_type == DHCP_OFFER || reply_type == DHCP_ACK) {
747 u8 dlen = (u8)min_u32(32, strlen(
m_domain));
755 opt.write_u32(lease_time);
760 opt.write_u32(m_iface->ipaddr().
value);
768 (flags & FLAG_BROADCAST) ? ADDR_BROADCAST : reply_addr,
769 (flags & FLAG_BROADCAST) ? MACADDR_BROADCAST : m_iface->reply_mac(),
770 PORT_DHCP_CLIENT, PORT_DHCP_SERVER);
778 dst->write_u32(0x02010600);
781 dst->write_u16(flags);
783 dst->write_u32(yiaddr.
value);
785 dst->write_u32(giaddr);
787 for (
unsigned a = 0 ; a < LEGACY_WORDS ; ++a)
789 dst->write_u32(DHCP_MAGIC);
806 if (lease_expired(meta,
m_time))
815 Addr DhcpServer::offer(u32 client_id, u32 req_ipaddr, u32 req_lease) {
819 Addr tmp = reserve(client_id, req_ipaddr, req_lease);
820 if (tmp != ADDR_NONE)
return tmp;
835 }
else if (lease_expired(meta,
m_time)) {
837 *meta = {client_id,
m_time + req_lease};
850 Addr DhcpServer::reserve(u32 client_id, u32 req_ipaddr, u32 req_lease) {
857 }
else if (client_id == CLIENT_RSVD) {
858 *meta = {client_id,
m_time + req_lease};
859 return Addr(req_ipaddr);
860 }
else if (client_id == meta->
client || lease_expired(meta,
m_time)) {
861 *meta = {client_id,
m_time + req_lease};
862 return Addr(req_ipaddr);
void add(satcat5::eth::ArpListener *evt)
Register an event-listener.
void remove(satcat5::eth::ArpListener *evt)
Unregister an event-listener.
bool send_probe(const satcat5::ip::Addr &target, const satcat5::eth::VlanTag &vtag=satcat5::eth::VTAG_NONE)
Send a probe to test if a given address is occupied.
unsigned written_len() const
Report total length after write_finalize() is called.
bool write_finalize() override
Mark end of frame and release temporary working data.
const u8 * buffer() const
Read-only access to the working buffer.
Thin wrapper for ArrayWrite with a built-in buffer.
Limited read of next N bytes.
bool read_consume(unsigned nbytes) override
Read and discard 0 or more bytes.
bool read_bytes(unsigned nbytes, void *dst) override
Read 0 or more bytes into a buffer.
unsigned get_read_ready() const override
How many bytes can be read without blocking?
u8 read_u8()
One of many functions for reading integer/floating point values, see details.
Abstract API for writing byte-streams and packets.
virtual void write_bytes(unsigned nbytes, const void *src)
Write 0 or more bytes from a buffer.
void write_u8(u8 data)
One of many functions for writing integer/floating point values, see details.
virtual bool write_finalize()
Mark end of frame and release temporary working data.
DHCP Client for leasing an IP address from a server.
void inform(const satcat5::ip::Addr &new_addr)
Set a static IP and fetch other parameters from the server.
u32 m_timeout
Time to next action, in seconds.
void arp_event(const satcat5::eth::MacAddr &mac, const satcat5::ip::Addr &ip) override
Callback for any announced MAC/IP address pair.
void renew()
Request extension of current lease if held, otherwise request a new lease.
satcat5::ip::DhcpState m_state
Client state.
u32 m_server_id
Server-ID (may not match IP-addr)
satcat5::udp::Address m_server
Server IP+MAC address.
void timer_event() override
Child class MUST override this method.
void frame_rcvd(satcat5::io::LimitedRead &src) override
Dispatch calls frame_rcvd(...) for each incoming frame with with a matching net::Type value.
satcat5::ip::Addr m_ipaddr
Assigned IP address, if any.
void release(const satcat5::ip::Addr &new_addr=satcat5::ip::ADDR_NONE)
Relinquish the currently held lease, if one exists.
u32 m_xid
Client/server transaction-ID.
u32 status() const
Report remaining lease time, or zero if none is held.
u16 m_seconds
Seconds since start of process.
Generic container for a group of DhcpAddress objects.
virtual satcat5::ip::Addr idx2addr(unsigned idx) const =0
Fetch IP-address for Nth object in the pool.
virtual satcat5::ip::DhcpAddress * idx2meta(unsigned idx)=0
Fetch metadata for Nth object in the pool.or Returns NULL if the index is out of bounds.
satcat5::ip::DhcpAddress * addr2meta(const satcat5::ip::Addr &addr)
Two-step lookup of metadata from address.
bool contains(const satcat5::ip::Addr &addr)
Does this pool contain the designated address?
DHCP Server for managing leases to other clients.
unsigned m_next_timer
Next timer event.
u32 m_max_lease
Maximum lease in seconds.
unsigned m_next_lease
Next lease request.
u32 m_time
Arbitrary timescale (+1/sec)
satcat5::ip::Subnet m_gateway
Default gateway and subnet mask.
DhcpServer(satcat5::udp::Dispatch *iface, satcat5::ip::DhcpPool *pool)
Attach this DHCP server to a UDP-dispatch object and address pool.
satcat5::ip::Addr m_dns
DNS server, if one is available.
const char * m_domain
Domain name (human-readable)
void timer_event() override
Child class MUST override this method.
satcat5::ip::Addr request(u32 lease_seconds, const satcat5::ip::Addr &addr=satcat5::ip::ADDR_NONE)
Manually request/reserve an IP address for the next N seconds.
void frame_rcvd(satcat5::io::LimitedRead &src) override
Dispatch calls frame_rcvd(...) for each incoming frame with with a matching net::Type value.
void count_leases(unsigned &free, unsigned &taken) const
Report the number of active or open leases.
void set_addr(const satcat5::ip::Addr &addr)
Set the local IP-address.
bool route_simple(const satcat5::ip::Addr &gateway, const satcat5::ip::Mask &subnet=satcat5::ip::MASK_24)
Routing table shortcuts.
The Log class creates and formats one log message.
Log & write(const char *str)
Formatting methods for various data types.
void add(satcat5::net::Protocol *proto)
Register a Protocol object.
void remove(satcat5::net::Protocol *proto)
Unregister a Protocol object.
void timer_every(unsigned msec)
Configure a repeating notification every X milliseconds.
Implementation of "net::Address" for UDP Dispatch.
bool ready() const override
Is this address object ready for use? Child MUST override this method.
satcat5::io::Writeable * open_write(unsigned len) override
Open a new frame to the designated address and type.
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).
Dispatcher sorts incoming UDP messages by port index.
Inline Ethernet Checksum insertion and verification.
u32 crc32(unsigned nbytes, const void *data)
Directly calculate CRC32 on a block of data.
Dynamic Host Configuration Protocol (DHCP) client and server.
DhcpState
DHCP protocol state.
Diagnostic logging to UART and/or Ethernet ports.
An Ethernet MAC address (with serializable interface).
u8 addr[6]
Byte array in network order (Index 0 = MSB)
IPv4 address is a 32-bit unsigned integer.
u32 value
Raw access to the underlying representation.
bool is_unicast() const
Any normal single-destination address.
One address in the pool allocated to a DhcpServer.
u32 timeout
Lease expiration time.
u32 client
Hash of client-ID.
u16 value
Raw access to the underlying representation.
satcat5::ip::Mask mask
Subnet mask.
satcat5::ip::Addr addr
Base address.
Multipurpose filter for matching fields in network packets.
TimeRef and TimeVal define the API for monotonic timers.
Miscellaneous mathematical utility functions.
constexpr u32 min_u32(u32 a, u32 b)
Min and max functions.