Skip to content

fix: rewrite PCAP backend for correct bridged networking#1777

Merged
midwan merged 1 commit intoBlitterStudio:masterfrom
tbdye:fix/pcap-bridged-networking
Feb 11, 2026
Merged

fix: rewrite PCAP backend for correct bridged networking#1777
midwan merged 1 commit intoBlitterStudio:masterfrom
tbdye:fix/pcap-bridged-networking

Conversation

@tbdye
Copy link
Contributor

@tbdye tbdye commented Feb 11, 2026

Summary

The PCAP networking backend has five bugs that prevent the A2065 (Am7990 LANCE) emulation from working correctly, particularly on Linux bridge interfaces. With the worker thread deadlock (#1773) and config parsing (#1774) already fixed, these are the remaining issues needed to make PCAP bridged networking fully functional.

The Problems

1. uaenet_trigger() never transmits

uaenet_trigger() is called by the A2065 emulation when the Am7990 has a packet to send. Instead of actually transmitting, it posts queue_available — a semaphore used for receive processing. No packet is ever retrieved from the transmit ring buffer or sent via pcap.

Before:

A2065 do_transmit() → ethernet_trigger() → uaenet_trigger()
  → uae_sem_post(&queue_available)  // signals receive path, not transmit
  → packet is never retrieved or sent

Fix: Rewrite uaenet_trigger() to call getfunc() (which retrieves the packet from the Am7990 transmit ring buffer) followed by pcap_sendpacket().

2. gotfunc() called from wrong thread (race condition)

The worker thread called uaenet_checkpacket()gotfunc()gotfunc2(), which writes directly to Am7990 chip state (csr[], boardram[], interrupt registers). These are also accessed by the emulation thread with no synchronization, causing data corruption and missed interrupts.

Fix: Move packet delivery to the emulation thread. The worker thread now only queues received packets. A new uaenet_receive_poll() function, called from a2065_hsync_handler() on the emulation thread, dequeues and delivers them via gotfunc(). This follows the single-producer/single-consumer pattern — the worker thread produces (enqueues), the emulation thread consumes (dequeues and delivers).

3. uaenet_close() is a no-op

uaenet_close() searches uaenet_data[] for a matching pointer, but uaenet_data[] is never populated — uaenet_open() allocates the array but never stores anything in it. The loop finds no match, so the pcap handle and worker thread are leaked on every chip reinit.

Before:

uaenet_close(ud)
  → for (i = 0; i < uaenet_count; i++)
  →   if (uaenet_data[i] == ud)   // always false — array is all NULLs
  → nothing happens, handle and thread leaked

Fix: Call uaenet_close_driver_internal() directly. Remove the never-populated uaenet_data[] array, uaenet_count, and queue_available semaphore (dead code). Don't xfree(ud) — the caller (a2065.cpp) owns the sysdata buffer. Add a re-open guard in uaenet_open() that cleanly shuts down any existing state before reinitializing.

4. Non-promiscuous mode drops all unicast on bridge interfaces

pcap_open_live() was called with the promiscuous parameter from the caller, which is 0 for normal operation. On a Linux bridge interface, the bridge has its own MAC address (e.g., de:91:f6:91:1a:eb) which differs from the emulated NIC's MAC (00:80:10:00:00:00). Without promiscuous mode, the kernel only delivers frames addressed to the bridge's own MAC — unicast frames to the Amiga's MAC are silently discarded before reaching the pcap socket.

This bug was particularly difficult to diagnose because tcpdump temporarily enables promiscuous mode on the interface while it runs, so the emulated NIC would appear to work whenever diagnostic capture was active.

Fix: Always open pcap in promiscuous mode. The BPF filter (see below) already restricts capture to relevant packets, so promiscuous mode does not cause excessive CPU usage.

5. pcap_setdirection(PCAP_D_IN) drops broadcast/multicast on bridges

The original code used pcap_setdirection(PCAP_D_IN) to filter out transmitted packets. On Linux bridge interfaces, the kernel classifies broadcast and multicast frames as "outgoing" even when they arrive from the network, so PCAP_D_IN silently drops them all — ARP, DHCP, and any broadcast/multicast traffic never reaches the emulated NIC.

Fix: Replace pcap_setdirection() with a BPF source-exclusion filter:

(ether dst <amiga-mac> or ether broadcast or ether multicast)
and not ether src <amiga-mac>

This accepts all frames destined to the Amiga (unicast, broadcast, multicast) while rejecting transmit loopback, without relying on the kernel's direction classification.

Bonus: Semaphore inflation on re-open

uae_sem_init() on an already-initialized semaphore calls SDL_SemPost() instead of recreating it. When uaenet_open() is called a second time (chip reinit), queue_sem goes from value 1 to 2, breaking its use as a mutex.

Fix: Call uae_sem_destroy() before uae_sem_init() on re-open.

Files Changed

  • src/osdep/amiberry_uaenet.cpp — All backend fixes: transmit rewrite, receive poll, close fix, re-open guard, semaphore fix, promiscuous mode, BPF filter, dead code removal
  • src/a2065.cpp — Call ethernet_receive_poll() from a2065_hsync_handler() for thread-safe packet delivery
  • src/ethernet.cpp — Add ethernet_receive_poll() dispatch function
  • src/include/ethernet.h — Add ethernet_receive_poll() declaration
  • src/osdep/amiberry_uaenet.h — Add uaenet_receive_poll() declaration

Testing

Tested on Debian 13 (kernel 6.12) with an emulated A2065 NIC using PCAP on a Linux bridge interface (br0). The bridge connects the host to a VLAN where the Amiga gets a real IP via DHCP.

All tests pass:

  • Ping gateway (192.168.6.1) — sustained, no drops
  • Ping internet (8.8.8.8) — sustained, no drops
  • DNS resolution — working
  • FTP to ftp.aminet.net — directory listing and file download working (~8 KB/s, consistent with emulated Am7990 + AmigaOS TCP stack throughput)
  • Reachable from other hosts on the network
  • Survives Amiga reboot (chip reinit cycle)

🤖 Generated with Claude Code

Fix five bugs in the PCAP networking backend that prevented the A2065
(Am7990 LANCE) emulation from working on Linux bridge interfaces.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tbdye tbdye requested a review from midwan as a code owner February 11, 2026 04:48
@midwan midwan merged commit 55a07ef into BlitterStudio:master Feb 11, 2026
18 of 22 checks passed
@tbdye tbdye deleted the fix/pcap-bridged-networking branch February 12, 2026 02:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants