SatCat5
ip_core.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_core.h>
7 #include <satcat5/log.h>
8 #include <satcat5/utils.h>
9 
11 
13  // Extract individual bytes from the 32-bit IP-address.
14  u32 ip_bytes[] = {
15  (value >> 24) & 0xFF, // MSB-first
16  (value >> 16) & 0xFF,
17  (value >> 8) & 0xFF,
18  (value >> 0) & 0xFF,
19  };
20 
21  // Convention is 4 decimal numbers with "." delimiter.
22  // e.g., "192.168.1.42"
23  for (unsigned a = 0 ; a < 4 ; ++a) {
24  if (a) wr.wr_str(".");
25  wr.wr_dec(ip_bytes[a]);
26  }
27 }
28 
30  // Example: "192.168.1.42 / 255.255.255.0"
31  satcat5::ip::Addr base(addr.value & mask.value);
32  base.log_to(wr);
33  wr.wr_str(" / ");
34  mask.log_to(wr);
35 }
36 
38  return (value == 0xFFFFFFFFu); // Limited broadcast 255.255.255.255
39 }
40 
42  if (value == 0xFFFFFFFFu)
43  return true; // Limited broadcast (255.255.255.255 /32)
44  else if (0xE0000000u <= value && value <= 0xEFFFFFFFu)
45  return true; // IP multicast (224.0.0.0 /4)
46  else
47  return false; // All other addresses
48 }
49 
51  if (value <= 0x00FFFFFFu)
52  return true; // Reserved source (0.0.0.0 /8)
53  else if (0x7F000000u <= value && value <= 0x7FFFFFFFu)
54  return true; // Local loopback (127.0.0.0 /8)
55  else
56  return false; // All other addresses
57 }
58 
60  return value && !is_multicast();
61 }
62 
64  return value != 0;
65 }
66 
67 unsigned satcat5::ip::Mask::prefix() const {
68  return satcat5::util::popcount(value);
69 }
70 
71 // Calculate or verify checksum using algorithm from RFC 1071:
72 // https://datatracker.ietf.org/doc/html/rfc1071
73 u16 satcat5::ip::checksum(unsigned wcount, const u16* data, u16 prev) {
74  u32 sum = u32(~prev & 0xFFFF);
75  for (unsigned a = 0 ; a < wcount ; ++a)
76  sum += data[a];
77  while (sum >> 16)
78  sum = (sum & UINT16_MAX) + (sum >> 16);
79  return (u16)(~sum);
80 }
81 
83  // If the checksum is blank, calculate per RFC 1071 Section 4.1.
84  unsigned wcount = 2 * ihl(); // Header length (16-bit words)
85  u16 chk_tmp = data[5] ? data[5] : ip::checksum(wcount, data);
86  // Write each word in network order.
87  for (unsigned a = 0 ; a <= 4 ; ++a)
88  wr->write_u16(data[a]); // First part of header
89  wr->write_u16(chk_tmp); // Insert calculated checksum
90  for (unsigned a = 6 ; a < wcount ; ++a)
91  wr->write_u16(data[a]); // Remainder of header + options
92 }
93 
94 void Header::chk_incr16(u16 prev, u16 next) {
95  // Apply the ~m + m' method of RFC1624 Section 3.
96  u16 tmp[2] = {u16(~prev), next};
97  data[5] = satcat5::ip::checksum(2, tmp, chk());
98 }
99 
100 void Header::chk_incr32(u32 prev, u32 next) {
101  // Apply the ~m + m' method of RFC1624 Section 3.
102  u32 tmp[2] = {u32(~prev), next};
103  data[5] = satcat5::ip::checksum(4, (u16*)tmp, chk());
104 }
105 
107  unsigned wrpos = 2 * ihl(); // Safe to append?
108  if (wrpos + 2 > HDR_MAX_SHORTS) return;
109  data[0] += 0x0100; // Increment IHL
110  data[1] += 0x0004; // Increment Length
111  data[wrpos++] = 0x9404; // 1st word (type + len)
112  data[wrpos++] = 0x0000; // 2nd word (arguments)
113 }
114 
116  // Sanity check before we start.
117  if (rd->get_read_ready() < ip::HDR_MIN_BYTES) return false;
118 
119  // Read each word in the "core" header (i.e., first 20 bytes).
120  for (unsigned a = 0 ; a < ip::HDR_MIN_SHORTS ; ++a)
121  data[a] = rd->read_u16();
122 
123  // Sanity-check various header fields:
124  return (ver() == 4) && (ihl() >= 5) && (len_total() >= 4*ihl());
125 }
126 
128  // Attempt to read the initial header (first 20 bytes).
129  if (!read_core(rd)) return false;
130 
131  // Bytes remaining in header and in packet?
132  unsigned hdr = 2 * ihl(); // Header length (16-bit words)
133  unsigned rem = 2 * (hdr - ip::HDR_MIN_SHORTS);
134  unsigned len = len_inner(); // Length of contained data (bytes)
135 
136  // Sanity-check that we can read the rest of the packet.
137  if (rd->get_read_ready() < rem + len) return false;
138 
139  // Read extended header options, if any.
140  // (Required for checksum, ICMP echo requests, etc.)
141  for (unsigned a = ip::HDR_MIN_SHORTS ; a < hdr ; ++a)
142  data[a] = rd->read_u16();
143 
144  // Verify checksum over entire header, using algorithm from RFC 1071.
145  u16 chk = ip::checksum(hdr, data);
146  return (chk == 0);
147 }
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
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
Internal buffer used by the Log class.
Definition: log.h:134
void wr_str(const char *str)
Write a null-terminated UTF-8 string.
Definition: log.cc:297
void wr_dec(u32 val)
Legacy alias for wr_d32.
Definition: log.h:166
Diagnostic logging to UART and/or Ethernet ports.
IPv4 address is a 32-bit unsigned integer.
Definition: ip_core.h:15
u32 value
Raw access to the underlying representation.
Definition: ip_core.h:17
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
void log_to(satcat5::log::LogBuffer &wr) const
Format this IP address in standard form, e.g., "192.168.1.2".
Definition: ip_core.cc:12
bool is_valid() const
Any nonzero address.
Definition: ip_core.cc:63
bool is_reserved() const
Reserved blocks (0.*.*.* or 127.*.*.*)
Definition: ip_core.cc:50
bool is_broadcast() const
Limited broadcast (255.255.255.255)
Definition: ip_core.cc:37
Structure for holding an IPv4 Header, including options.
Definition: ip_core.h:180
constexpr unsigned len_total() const
< Total bytes including header
Definition: ip_core.h:194
void write_to(satcat5::io::Writeable *wr) const
Write IPv4 header to the designated stream.
Definition: ip_core.cc:82
constexpr unsigned len_inner() const
< Inner bytes excluding header
Definition: ip_core.h:200
constexpr u16 chk() const
< Checksum (incoming only)
Definition: ip_core.h:206
void chk_incr16(u16 prev, u16 next)
Incremental update to checksum when replacing a given field.
Definition: ip_core.cc:94
bool read_from(satcat5::io::Readable *rd)
Read an entire IPv4 header from the designated stream.
Definition: ip_core.cc:127
void option_alert()
}
Definition: ip_core.cc:106
constexpr unsigned ver() const
< Header version (i.e., "4")
Definition: ip_core.h:190
constexpr unsigned ihl() const
< Header length (4-byte words)
Definition: ip_core.h:192
bool read_core(satcat5::io::Readable *rd)
Read a partial IPv4 header from the designated stream.
Definition: ip_core.cc:115
u16 data[HDR_MAX_SHORTS]
Raw access to the underlying header contents.
Definition: ip_core.h:182
unsigned prefix() const
Convert this subnet-mask to a CIDR prefix length.
Definition: ip_core.cc:67
void log_to(satcat5::log::LogBuffer &wr) const
Format this subnet in CIDR form, e.g., "192.168.1.0/24".
Definition: ip_core.cc:29
Miscellaneous mathematical utility functions.