This module handles IPv4 packet encoding, decoding, fragmentation, and reassembly. It maintains a routing table (via ARPv4) to resolve destination IPv4 addresses to MAC addresses.
Zero-copy and payload types.
When an IPv4 packet arrives, it may or may not have been fragmented in transit. The payload type reflects this:
Slice: the packet was not fragmented. The payload is a zero-copy slice pointing directly into the original Ethernet frame buffer. This is the "happy path" and avoids any allocation.
String: the packet was reassembled from multiple fragments. Reassembly requires copying, so the result is an OCaml string.
Upper layers (TCP, UDP) handle both cases transparently.
Route discovery.
write may need to resolve the destination's MAC address via ARPv4. This involves sending an ARP request and waiting for a reply, which constitutes an "interruption" (the current Miou task may be suspended). An internal cache avoids repeated lookups. If you already know the MAC address, use write_directly which never interrupts.
When IPv4 sends logs, it can attach information such as the source IPv4 and the destination to which the logs relate. The user can obtain this information through the Logs API (and tags) and display it in order to better characterize the information that IPv4 can send (especially with regard to debugging).
The payload of a received IPv4 packet. See the module documentation for the distinction between Slice (zero-copy, non-fragmented) and String (reassembled from fragments).
It is morally possible for a unikernel to have several IPv4 addresses, each of which can communicate with certain destinations (depending on the routes discovered). In this case, the user is able to determine the source IPv4 address required to communicate with the given destination.
In practice, the implementation is configured to have only one IPv4 address. This assertion is therefore true:
let open Ipaddr in
let dst0 = V4.of_octets (Mirage_crypto_rng.generate 4) |> Result.get_ok in
let src0 = Mnet.IPv4.src ipv4 ~dst:dst0 in
let dst1 = V4.of_octets (Mirage_crypto_rng.generate 4) |> Result.get_ok in
let src1 = Mnet.IPv4.src ipv4 ~dst:dst1 in
assert (V4.compare src0 src1 = 0)
write_directly ipv4 ?ttl src (dst, macaddr) ~protocol w writes a new IPv4 packet weffectively (without interruption) (fragmented or not) to the specified destination macaddr.
write ipv4 ?ttl ?src dst ~protocol w writes a new IPv4 packet w (fragmented or not) to the specified destination dst. This function may have an interruption to discover the route to send the given packet to dst (an underlying cache exists for such discovery).
When a packet is received (and reassembled if it has been fragmented), the handler is called with the IPv4 information (source, destination and protocol, see packet) so that the upper layer (such as TCP) can process it. set_handler allows you to modify this handler in order to direct incoming packets to a specific process.