Summary
On WiFi/DaynaPORT builds, every outbound AppleTalk-over-802.3 (EtherTalk Phase 2)
frame the Mac transmits leaves BlueSCSI with an 802.3/LLC length field that has been
rounded up to a multiple of 4, instead of the true length (8 + DDP length).
For IP/Ethernet-II traffic those two bytes are an EtherType and the padded value is
ignored, so AFP-over-AppleTalk to a known/stored address still works (disks mount on
boot). But for 802.3 AppleTalk the field is a real length: AppleTalk routers and
atalkd/netatalk validate it against the DDP datagram's own length field, find they
disagree, and reject the datagram. The rejected frames are exactly the broadcast
ZIP GetNetInfo and NBP BrRq requests used for discovery, so:
- Chooser zone list is empty (ZIP GetNetInfo reply never comes back)
- AppleShare server list is empty (NBP LkUp replies never come back)
- Network control panels are slow (blocking on discovery timeouts)
This is a regression: older firmware transmitted the correct length and works fine.
Affected / not affected
- Broken: current firmware (tested up to the latest release), both Pico 1W and
Pico 2W (RP2040 and RP2350). So it is not board-specific.
- Works: v2024.10.26 and earlier.
- First broken: v2024.12.08.
Root cause (bisected)
Bisected to commit d3f3db2 "chore: update to pico-sdk 2.0.0" (the SDK 1.x → 2.0.0 /
framework e139b9c7 → v4.1.1-DaynaPORT bump). BlueSCSI's own network.c /
BlueSCSI_platform_network.cpp are byte-identical across the boundary; the behavior
change comes in with the new SDK/cyw43 TX path. The 802.3 length field ends up rounded up
to a 4‑byte boundary somewhere between platform_network_send() →
cyw43_send_ethernet() and the wire.
Evidence (on-the-wire captures)
Captured with tcpdump on the AppleTalk router (Raspberry Pi running tashrouter +
netatalk 4.4.2), filtering the BlueSCSI MAC. Comparing the exact same frames from the same
Mac, only the firmware changed:
| Firmware |
DDP length |
correct 802.3 len (8+ddp) |
actual 802.3 len field |
delta |
| v2024.10.26 (OLD, works) |
21 |
29 |
29 |
0 |
| v2024.10.26 (OLD, works) |
50 |
58 |
58 |
0 |
| SDK-2 (current, broken) |
21 |
29 |
32 |
+3 |
| SDK-2 (current, broken) |
37 |
45 |
48 |
+3 |
| SDK-2 (current, broken) |
50 |
58 |
60 |
+2 |
Formula: broken 802.3_len == round_up(8 + ddp_len, 4). Across a full session the OLD
firmware was correct on 15/15 AppleTalk frames; the SDK-2 firmware was padded on 36/36.
Same ATP request frame, first 16 bytes (note bytes 12–13, the 802.3 length):
OLD fw (correct): aa c7 61 cf ae e7 00 80 19 5f 00 5f 00 1d aa aa <- 0x001d = 29
NEW fw (padded): aa c7 61 cf ae e7 00 80 19 5f 00 5f 00 20 aa aa <- 0x0020 = 32
The DDP header's own length field is unchanged/correct (21) in both; only the outer
802.3 length is wrong on the SDK-2 build.
What the router sees
- Broken: Mac sends
nbp-brRq "=:AFPServer@EtherTalk Network" and repeated
at-#6 (ZIP GetNetInfo); the router drops them (length mismatch) and never fans them
out → 0 replies → Chooser stays empty; the Mac re-sends GetNetInfo indefinitely.
- Working (old fw or after workaround): the same requests are accepted, the router
replies (atp-resp carrying zone info), and zones/servers appear.
Confirmation / workaround
Patching the router to tolerate the padding immediately makes zones appear, which
confirms the length field is the sole cause. In tashrouter's datagram.py
(from_long_header_bytes / from_short_header_bytes) I changed the strict check:
# was:
if length != len(data):
raise ValueError(...)
# to (accept trailing padding, then trim to the declared length):
if length > len(data):
raise ValueError(...)
if length < len(data):
data = data[:length]
With this, both Pico 1W and Pico 2W on the latest firmware work. This is a router-side
workaround only; it is not appropriate upstream for tashrouter (its strict check is
spec-correct — the 802.3 length should equal the LLC payload length), and every other
AppleTalk router / real netatalk will still reject BlueSCSI's frames.
Suggested fix
BlueSCSI should transmit the true 802.3 length for outbound frames rather than a
4‑byte‑padded value — i.e. do not let the SDK/cyw43 TX byte-alignment leak into the
802.3 length field. (Ethernet minimum-frame padding is fine, but the 802.3 length field
must remain the real LLC payload length for AppleTalk to route.)
Environment
- Board: BlueSCSI Pico 1W and Pico 2W (DaynaPORT / WiFi)
- Firmware: current release (also reproduced on v2024.12.08); good on v2024.10.26
- Server: Raspberry Pi, netatalk 4.4.2 + tashrouter, EtherTalk Phase 2, zone "EtherTalk Network"
- Mac: System 6/7 via BlueSCSI DaynaPORT
Summary
On WiFi/DaynaPORT builds, every outbound AppleTalk-over-802.3 (EtherTalk Phase 2)
frame the Mac transmits leaves BlueSCSI with an 802.3/LLC length field that has been
rounded up to a multiple of 4, instead of the true length (
8 + DDP length).For IP/Ethernet-II traffic those two bytes are an EtherType and the padded value is
ignored, so AFP-over-AppleTalk to a known/stored address still works (disks mount on
boot). But for 802.3 AppleTalk the field is a real length: AppleTalk routers and
atalkd/netatalk validate it against the DDP datagram's own length field, find theydisagree, and reject the datagram. The rejected frames are exactly the broadcast
ZIP GetNetInfo and NBP BrRq requests used for discovery, so:
This is a regression: older firmware transmitted the correct length and works fine.
Affected / not affected
Pico 2W (RP2040 and RP2350). So it is not board-specific.
Root cause (bisected)
Bisected to commit
d3f3db2"chore: update to pico-sdk 2.0.0" (the SDK 1.x → 2.0.0 /framework
e139b9c7→v4.1.1-DaynaPORTbump). BlueSCSI's ownnetwork.c/BlueSCSI_platform_network.cppare byte-identical across the boundary; the behaviorchange comes in with the new SDK/cyw43 TX path. The 802.3 length field ends up rounded up
to a 4‑byte boundary somewhere between
platform_network_send()→cyw43_send_ethernet()and the wire.Evidence (on-the-wire captures)
Captured with
tcpdumpon the AppleTalk router (Raspberry Pi running tashrouter +netatalk 4.4.2), filtering the BlueSCSI MAC. Comparing the exact same frames from the same
Mac, only the firmware changed:
8+ddp)Formula: broken
802.3_len == round_up(8 + ddp_len, 4). Across a full session the OLDfirmware was correct on 15/15 AppleTalk frames; the SDK-2 firmware was padded on 36/36.
Same ATP request frame, first 16 bytes (note bytes 12–13, the 802.3 length):
The DDP header's own length field is unchanged/correct (21) in both; only the outer
802.3 length is wrong on the SDK-2 build.
What the router sees
nbp-brRq "=:AFPServer@EtherTalk Network"and repeatedat-#6(ZIP GetNetInfo); the router drops them (length mismatch) and never fans themout → 0 replies → Chooser stays empty; the Mac re-sends GetNetInfo indefinitely.
replies (
atp-respcarrying zone info), and zones/servers appear.Confirmation / workaround
Patching the router to tolerate the padding immediately makes zones appear, which
confirms the length field is the sole cause. In tashrouter's
datagram.py(
from_long_header_bytes/from_short_header_bytes) I changed the strict check:With this, both Pico 1W and Pico 2W on the latest firmware work. This is a router-side
workaround only; it is not appropriate upstream for tashrouter (its strict check is
spec-correct — the 802.3 length should equal the LLC payload length), and every other
AppleTalk router / real netatalk will still reject BlueSCSI's frames.
Suggested fix
BlueSCSI should transmit the true 802.3 length for outbound frames rather than a
4‑byte‑padded value — i.e. do not let the SDK/cyw43 TX byte-alignment leak into the
802.3 length field. (Ethernet minimum-frame padding is fine, but the 802.3 length field
must remain the real LLC payload length for AppleTalk to route.)
Environment