FilterTable::evaluate() set final_verdict for matched rules but never
returned it — always returned the chain's default_policy instead.
This meant every firewall rule (ACCEPT/DROP/REJECT/LOG) was being
evaluated (correctly) but its verdict was DISCARDED.
Symptoms:
- 'DROP' rules had no effect — packets flowed through
- 'ACCEPT' rules had no effect — packets flowed through
- Only default_policy was ever applied
Fix: return final_verdict.unwrap_or(default_policy).
Now 'iptables-style' rules actually take effect:
- DROP rules block traffic
- ACCEPT rules allow traffic (before default)
- REJECT rules send ICMP unreachable
- LOG rules log and continue
This bug was introduced when the filter evaluation logic was
refactored and the return statement was left pointing at the
default policy instead of the verdict from the rule loop.
Confirmed via cargo build; warning 'value assigned to final_verdict
is never read' is now gone.
Conntrack now rate-limits ICMP/ICMPv6 echo requests per source:
- is_echo_request() detects Type 8 (ICMPv4) / Type 128 (ICMPv6)
- check_icmp_limit() enforces 20 echoes/sec per source IP
- Over-limit returns ConnState::OverLimit, increments over_limit_count
Mirrors existing SYN flood protection (100 SYN/sec per source).
Smaller threshold (20/sec) because ICMP is cheaper to generate.
Reuses same rate_limits map — TCP SYNs and ICMP echoes share the
budget. Side effect: 100 SYN + 20 echo = 120 packets/sec from one
source before any trigger, which is reasonable.
IfaceStats struct gains rx_errors, tx_errors, rx_dropped, tx_dropped.
Stats output now shows error/drop line when any of these are non-zero:
eth0 rx: ...B tx: ...B
errors: rx=N tx=N drops: rx=N tx=N
Mirrors Linux ifconfig output format.
Suppresses the extra line when all counters are 0 (clean state).
/scheme/netcfg/summary outputs:
interfaces: eth0=up lo=up
routes: 4
sockets: 7 (tcp=3 udp=4 icmp=0 raw=0)
ip_forward: 1
eth0 stats: rx=1234/16 tx=5678/12 err=0/0 drop=0/2
Single read aggregates:
- Link state of eth0 and loopback
- Total route count
- Per-protocol socket counts (TCP/UDP/ICMP/Raw)
- ip_forward toggle state
- Full MIB-II stats for eth0
Useful for at-a-glance monitoring without reading 6+ separate nodes.
LinkDevice trait gains is_promiscuous() and set_promiscuous().
EthernetLink stores promiscuous flag (default: false).
Receive path bypasses MAC check when promiscuous enabled —
captures all frames regardless of destination MAC.
netcfg/ifaces/eth0/promiscuous rw:
cat /scheme/netcfg/ifaces/eth0/promiscuous → 'off' or 'on'
echo on > .../promiscuous → enable promiscuous mode
echo off > .../promiscuous → disable
Accepts: on/off/1/0/yes/no/true/false.
Mirrors Linux 'ip link set dev promisc on/off'.
Required for packet capture tools that need to see all traffic.
Reads the default gateway from the route table:
cat /scheme/netcfg/route/gateway
→ 'default via 192.168.1.1\n'
or 'no default route\n' if no default gateway configured.
Looks up the route for 0.0.0.0 (the default route CIDR) and
returns its 'via' address. Mirrors Linux 'ip route show default'.
All 4 RFC 1213 counters now wired to real events:
- rx_errors: malformed Ethernet frame or ARP packet
- tx_errors: network_file.write_all() failed
- rx_dropped: ARP/NDP neighbor resolution failed or queue full
- tx_dropped: qdisc returned None (rate-limit kicked in)
Stats output now reflects actual error/drop conditions.
Mirrors Linux's ifconfig/sar 'errors' and 'dropped' fields.
The new rx_dropped counter is now incremented in real drop paths:
- drop_waiting_packets_v4: ARP/NDP failed after MAX_TRIES, packets dropped
- drop_waiting_packets_v6: same for IPv6 neighbor discovery
- handle_missing_neighbor: waiting queue full, incoming packet dropped
Net effect: stats now reflect actual packet loss conditions visible
via /scheme/netcfg/ifaces/eth0/stats:
rx_dropped=N (was always 0 before)
tx_dropped was already incremented by qdisc drops (R24).
LinkDevice trait gains is_enabled() and set_enabled() with defaults.
EthernetLink stores enabled flag (default: true). When disabled,
link_state() reports 'down' regardless of hardware_address presence.
netcfg/ifaces/eth0/enabled is rw:
cat /scheme/netcfg/ifaces/eth0/enabled → 'up' or 'down'
echo up > .../enabled → bring interface up
echo down > .../enabled → bring interface down
echo on/off/1/0/yes/no → also accepted
Mirrors Linux 'ip link set dev <state>'.
Pre-existing link state (hardware_address Some/None) unchanged;
the enabled flag adds a software override layer that can be
toggled without restarting the netstack.
Added verbs.rs with 200+ named constants ported from Linux 7.1
include/sound/hda_verbs.h. Replaces raw hex values (0xF00, 0xF01, etc.)
with named constants throughout device.rs.
Constants cover: widget types, GET/SET verbs, parameter IDs,
widget/pin/amplifier capabilities, pin control, power states,
PCM/stream format, digital converter bits, connection list.
Bug fix: read_node() was calling AC_PAR_NODE_COUNT (0x04) for
function_group_type query — corrected to AC_PAR_FUNCTION_TYPE (0x05).
The old code happened to work because the low byte matched on
the test codec, but was reading the wrong HDA parameter.
Router now captures packets flowing through the network stack:
- forward_packets(): capture all forwarded/local-delivered packets
- Observer injected via Router::new() from Smolnetd constructor
When /scheme/netcfg/capture/enable is written, all packets
traversing the router are captured into the ring buffer.
When disabled, zero overhead (AtomicBool check).
Replaced single I/O queue pair with dynamic allocation of up to 8 pairs
using NVMe Set Features command (Feature ID 0x07, Number of Queues).
Cross-referenced with Linux 7.1 nvme_set_queue_count() in drivers/nvme/host/core.c.
Controller advertises max SQ/CQ count; driver creates min(requested, allocated, 8)
queue pairs for parallel I/O submission. Each pair gets a unique interrupt vector
(round-robin across 4 MSI-X vectors).
Previous behavior: hardcoded qid=1 only. New behavior: qid 1..N based on
controller capabilities. Improves I/O throughput on multi-core systems
by enabling concurrent command submission across queues.
Bridge: statistics() aggregates rx/tx bytes+packets from all member ports.
arp_stats() delegates to each port, showing per-port breakdown.
Bond: statistics() aggregates from all slaves (active+standby).
arp_stats() delegates to each slave.
TUN: statistics() now returns live counters tracked during send()/recv().
rx_bytes, rx_packets, tx_bytes, tx_packets increment per-packet.
Previously these devices returned Stats::default() (all zeros).
Now netcfg /ifaces/*/stats shows real data for bridge, bond, tun.
TCP and UDP get_sock_opt/set_sock_opt had duplicate match arms
due to constant collisions (Linux-level values share namespace):
TCP: TCP_MAXSEG=2, IP_TTL=2, SO_REUSEADDR=2 → kept TCP_MAXSEG
UDP: IP_TTL=2, SO_REUSEADDR=2 → kept IP_TTL (more useful)
Removed unreachable arms with explanatory comments. The
collision is inherent — Linux uses different option levels
(SOL_SOCKET vs IPPROTO_TCP vs IPPROTO_IP) but Redox scheme
has a flat namespace. Applications that use multi-level
getsockopt() would need richer level dispatching.
Router gains Rc<Cell<bool>> ip_forward flag (default: true).
When false, forward_packets() returns immediately — no packets forwarded
between interfaces. Security best practice for non-router hosts.
netcfg scheme gains sysctl subtree:
/scheme/netcfg/sysctl/net/ipv4/ip_forward (rw: 0 or 1)
Read: echo /scheme/netcfg/sysctl/net/ipv4/ip_forward → 1
Write: echo 0 > /scheme/netcfg/sysctl/net/ipv4/ip_forward (disable)
Restore: echo 1 > /scheme/netcfg/sysctl/net/ipv4/ip_forward
Mirrors Linux /proc/sys/net/ipv4/ip_forward.
Shared via Rc<Cell<bool>> between Router and NetCfgScheme.
BridgeDevice STP hardening:
- send(): check STP blocking before unicast forwarding (host-originated)
- recv(): check STP blocking before unicast forwarding (switched frames)
- Previously only flood() checked STP; unicast forwarding bypassed it.
A blocked port must never forward any traffic — STP semantics now correct.
netcfg stats enhancement:
- Per-interface stats now include mtu= and link= fields alongside counters
- Applies to both eth0 and loopback
- Enables bandwidth monitoring tools to self-discover MTU/link state
- UDP port allocation now falls back to claim_port_reuse() (SO_REUSEADDR)
- SO_REUSEADDR get/set added to both UDP and TCP schemes
- SO_BROADCAST getter added to UDP (always returns 1)
- IP_TTL getter/setter added to UDP (get/set hop_limit)
- TCP: SO_REUSEADDR get/set added for API completeness
- All new options return known values for application compatibility
IMPROVEMENT-PLAN.md §10.1: validates P0 .unwrap→.expect safety fix.
4 tests validating the buffer size invariants documented in
the scsi/mod.rs SAFETY comment:
- all_command_structs_fit_in_command_buffer:
Verifies Inquiry, ModeSense6/10, RequestSense, ReadCapacity10,
Read16, Write16 all fit within the 16-byte command_buffer
- standard_inquiry_data_fits_in_inquiry_buffer:
Verifies StandardInquiryData (36 bytes) fits in inquiry_buffer (259)
- response_structs_match_expected_sizes:
Verifies ModeParamHeader6 (4), ModeParamHeader10 (8),
ReadCapacity10ParamData (8) fixed sizes
- plain_from_bytes_is_safe_for_buffers:
Round-trip verifies plain::from_bytes succeeds on properly
sized buffers — validates that the .expect() calls in the
res_* methods will never panic
All 4 tests pass. usbscsid now has 4 tests (was 0).
IMPROVEMENT-PLAN.md §10.2 item 4: medium priority fix.
Changed two crossbeam channels from unbounded to bounded:
- irq_reactor: 1024 events (transfer/command completions)
- device_enumerator: 64 events (port enumeration requests)
Unbounded channels can grow without limit if the consumer
(IRQ reactor) falls behind, causing OOM under heavy USB traffic.
Bounded channels provide natural backpressure — the sender
(scheme handler) blocks when the channel is full, causing
the USB client to back off.
Cross-referenced with Linux 7.1 xhci-ring.c producer/consumer
pattern where transfer rings are bounded by hardware limits.
IMPROVEMENT-PLAN.md §10.1: critical quirk enforcement.
Fresco Logic FL1009 and Etron EJ168 controllers have broken
stream support. When BROKEN_STREAMS quirk is active, force
usb_log_max_streams to None, which prevents stream context
array allocation in configure_endpoints_once(). Previously
the quirk was declared and logged at init but had no runtime
effect — streams were still allocated, causing crashes on
these controllers.
Cross-referenced with Linux 7.1 xhci-pci.c BROKEN_STREAMS
enforcement in xhci_alloc_streams().
IMPROVEMENT-PLAN.md §10.2 items 1-2: P1 correctness fixes.
BOS descriptor (scheme.rs:1900-1905):
- Uncommented fetch_bos_desc() call that was disabled with TODO
- Now reads Binary Object Store descriptor at device enumeration time
- Enables proper USB 3.x SuperSpeed detection via bos_capability_descs
(was hardcoded to supports_superspeed = false)
- Supports both SuperSpeed and SuperSpeedPlus capability detection
- Cross-referenced with Linux 7.1 drivers/usb/core/config.c:387-420
Event ring growth (irq_reactor.rs:551-575):
- Replaced "TODO: grow event ring" stub with ring-reset implementation
- On EventRingFull: resets all TRBs to Invalid with inverted cycle bit,
then writes ERDP back to ring base address
- Linux uses multi-segment ERST expansion; we use ring-reset which
achieves the same reliability benefit without segment management
- Includes ZERO_64B_REGS quirk-aware ERDP write ordering
- Cross-referenced with Linux 7.1 xhci-ring.c:570-590
IMPROVEMENT-PLAN.md §10.1.6: critical safety fix.
usbscsid main.rs had 3 runtime unwrap sites that would panic
the daemon on transient errors:
1. Line 106: debug block 0 read on init — now uses if-let to
skip the debug print if the read fails (disconnected device,
media error). The device still registers its scheme.
2. Line 144: event_queue event unwrap — now handles Err()
with eprintln + continue instead of panic.
3. Line 147: scheme.tick() unwrap — now handles Err()
with eprintln instead of panic.
Scheme tick failures propagate gracefully — the event loop
continues, the daemon survives. This matches the Linux 7.1
pattern of logging USB errors without crashing the daemon.
IMPROVEMENT-PLAN.md §10.1 item 2: critical safety fix.
The unsafe impl Send/Sync for Xhci<N> in mod.rs:310-311 is a
soundness claim with no supporting documentation. A future refactor
that adds a !Send/!Sync field would silently break thread-safety with
no compile-time indication.
Fix: add a SAFETY comment block enumerating each field with its
safety mechanism. This makes the invariant explicit and forces any
future maintainer to update the comment if they add a field.
The Xhci struct has no fields that lack interior mutability or
Send/Sync implementations. All shared mutable state is guarded by:
- CHashMap (port_states, handles, drivers)
- Mutex (op, ports, cmd, run, primary_event_ring)
- crossbeam_channel (irq_reactor_*_sender)
- Dma<...> (dev_ctx, scratchpad_buf_arr) -- has internal mutex
- Arc<Mutex<...>> (dbs)
cross-references IMPROVEMENT-PLAN.md §10.1.2
IMPROVEMENT-PLAN.md §10.1 item 1: critical safety fix.
usbscsid scsi/mod.rs had 17 plain::from_mut_bytes/from_bytes/slice_from_bytes
.unwrap() calls on compile-time-fixed-size buffers. A refactoring bug
in the buffer sizes or the SCSI command structs would cause immediate
kernel panic on every SCSI operation.
Fix: replace each .unwrap() with .expect() with a descriptive message
that includes the actual expected type and buffer size. The message makes
the invariant explicit in the source and surfaces the error clearly if
the invariant is ever broken (rather than an opaque 'called unwrap()').
Added ScsiError::BufferSizeMismatch variant as a fallback for future
use if any of these paths need to propagate the error instead of panicking
during refactoring. The 'panic' here is now intentional and safe — the
buffer sizes are compile-time fixed.
cross-references IMPROVEMENT-PLAN.md §10.1.1