When a UDP/IP datagram larger than the Ethernet MTU (i.e 1500 bytes) is sent to a destination whose MAC address is not yet in the ARP cache, the IP fragmentation layer in fnet_ip4.c submits all fragments to the ARP resolution path before any ARP reply arrives. The ARP entry (fnet_arp_entry_t) holds only a single pending packet pointer (entry->hold). Each subsequent fragment overwrites and frees the previous one, so only the last fragment survives. When the ARP reply arrives, only that last fragment is transmitted. The receiver cannot reassemble the datagram and silently discards it.
[Cause]
In _fnet_arp_resolve() (fnet_arp.c), entry->hold is a single fnet_netbuf_t * slot. When a new packet arrives for an IP address still pending ARP resolution, the existing held packet is freed and replaced:
if (entry->hold)
{
_fnet_netbuf_free_chain(entry->hold); // previous fragment is lost
entry->hold = NULL;
}
entry->hold = nb; // only the latest fragment survives

When a UDP/IP datagram larger than the Ethernet MTU (i.e 1500 bytes) is sent to a destination whose MAC address is not yet in the ARP cache, the IP fragmentation layer in fnet_ip4.c submits all fragments to the ARP resolution path before any ARP reply arrives. The ARP entry (fnet_arp_entry_t) holds only a single pending packet pointer (entry->hold). Each subsequent fragment overwrites and frees the previous one, so only the last fragment survives. When the ARP reply arrives, only that last fragment is transmitted. The receiver cannot reassemble the datagram and silently discards it.
[Cause]
In _fnet_arp_resolve() (fnet_arp.c), entry->hold is a single fnet_netbuf_t * slot. When a new packet arrives for an IP address still pending ARP resolution, the existing held packet is freed and replaced:
if (entry->hold)
{
_fnet_netbuf_free_chain(entry->hold); // previous fragment is lost
entry->hold = NULL;
}
entry->hold = nb; // only the latest fragment survives