CRITICAL/MEDIUM BUGS FIXED:
1. ethernet.rs send_ndp_solicit: state corruption via recursive call
The function destructured self.ndp_state by value (target, tries,
silent_until) at the start and wrote back at the end. But between
destructuring and writing back, self.drop_waiting_packets_v6(target)
could recursively call self.send_ndp_solicit(Instant::ZERO) for a
different target. The recursive call updated self.ndp_state with the
new target's state. When control returned to the original frame, the
original write-back clobbered the recursive call's state, silently
losing neighbor discovery for the new target.
Fix: use scoped pattern matches to read target/tries/silent_until
from the live state right before they are needed, so the recursive
call's writes are preserved. This matches the pattern used by
send_arp (which uses ref mut and is correct).
2. scheme/socket.rs on_close: port double-free
close_file() was called before the refcount check, so the port was
released on every close. For a dup'd socket, the second close
tried to release the port again — double-free.
Fix: compute the new refcount first, only call close_file and
remove from socket_set when the count reaches 0 (last reference).
3. scheme/tcp.rs new_socket: port + socket leak on connect failure
If get_port() succeeded but connect() later failed, the port was
claimed and the socket was added to socket_set, but new_socket
returned Err. The caller (open_inner) did not insert the file
handle, so on_close was never called. Both the port and the socket
slot leaked.
Fix: when connect() fails, release the auto-allocated port before
returning. Explicit user-provided ports are still released by
on_close when the last file is dropped (preserves the existing
on_close-based release).
4. observer.rs capture: per-packet size limit not enforced
Vec::with_capacity only pre-allocates; extend_from_slice copies the
full packet. The intended per-packet limit (MAX_CAPTURE_BYTES /
max_packets = 256 bytes) was being bypassed, allowing a single
1500-byte packet to consume the full capture buffer.
Fix: truncate to per_packet_limit before extending.
5. observer.rs capture: short packets bypassed filter
Packets < 20 bytes returned true (capture anyway) regardless of
the user's filter. A filter like 'tcp port 80' would capture all
short packets, including non-TCP.
Fix: return false (no match) for short packets. Filter semantics
are now consistent: if a packet can't be matched, it's not captured.
All 29 existing tests still pass.