• Updated 2023-07-12: Hello, Guest! Welcome back, and be sure to check out this follow-up post about our outage a week or so ago.

Developing a portable user-land AppleTalk stack

cheesestraws

Well-known member
That sounds like a sensible approach to me.  It shouldn't at all matter which side of the bridge is responsible for that.  My prototype bridge (which hasn't made any progress :( ) had a separate component that was responsible for keeping track of what had been seen on what interface, but that's because I'm obsessive like that.

 

sfiera

Well-known member
I implemented it but haven’t gotten it working again yet. Some observations:

  • In EtherTalk (AARP), you can probe a request on any network, but that’s not possible under plain LocalTalk (LLAP) with an ENQ. I guess this isn’t that surprising, since ENQ is there to check for address collisions, and you can’t collide with an address on another network.
  • In the original abridge design, EtherTalk packets are sent verbatim, so hwaddrs are preserved across the bridge. This is fine since pcap is in promiscuous mode and will pick them up anyway. Now, I’m unwrapping and rewrapping them on each side, and so the reconstructed EtherTalk packets get marked with the hwaddr of the bridge. I think that sounds correct for how packets should be addressed through a router.
  • Previously, when EtherTalk packets were flowing through the bridge, I didn’t really think too much about the short vs. extended DDP distinction, since I always had to immediately convert to extended. Now I don’t, but maybe it would be better to require extended headers within the bridge, and only have the LToU adapter consider short headers.

I’m still confused about networks, such as how THIS-NET and THIS-NET-RANGE are chosen. My Quadra (7.5? 7.6?) picked 0xff00, which is what I’d expect (EtherTalk = extended network = pick from startup range 0xff00~0xfffe when no RTMP). Mini vMac picks 0 (LocalTalk = non-extended = 0 when no RTMP). I was testing today with an iBook (8.1) and it picked 0xa0f1, which I don’t understand.

 

sfiera

Well-known member
I think the iBook must just have previously lived on another network. Changed it to 0xff00 manually and set it back to automatic, and that sticks.

With some Wiresharking, I figured out part of why the Quadra wasn’t working as a client of Mini vMac. NBP responses include the address of the sender. I was rewriting the LToU packets to assign them to network 0xff00, but the NBP responses were telling the Quadra to contact network 0. However, just leaving the network as 0 doesn’t work for some reason… still investigating.

Here’s an in-progress Wireshark dissector. Short headers only so far.

Code:
ltou_protocol   = Proto("LToU",  "LocalTalk-over-UDP")
fieldSender     = ProtoField.uint32("ltou.sender", "sender", base.DEC)
fieldDstNode    = ProtoField.uint8("ltou.dstNode", "destination node", base.DEC)
fieldDstSocket  = ProtoField.uint8("ltou.dstSocket", "destination socket", base.DEC)
fieldSrcNode    = ProtoField.uint8("ltou.srcNode", "source node", base.DEC)
fieldSrcSocket  = ProtoField.uint8("ltou.srcSocket", "source socket", base.DEC)
fieldSize       = ProtoField.uint16("ltou.size", "size", base.DEC)
fieldOpcode     = ProtoField.uint8("ltou.opcode", "LLAP opcode", base.DEC)
fieldDDPType    = ProtoField.uint8("ltou.ddpType", "DDP type", base.DEC)

ltou_protocol.fields  = {
    fieldSender,
    fieldDstNode,
    fieldDstSocket,
    fieldSrcNode,
    fieldSrcSocket,
    fieldSize,
    fieldOpcode,
    fieldDDPType,
}

local ddp = DissectorTable.get("ddp.type")

function ltou_protocol.dissector(buffer, pinfo, tree)
  length = buffer:len()
  if length == 0 then return end

  pinfo.cols.protocol = ltou_protocol.name

  local subtree = tree:add(ltou_protocol, buffer(), "LocalTalk-over-UDP Data")

  subtree:add(fieldSender, buffer(0,4))
  subtree:add(fieldDstNode, buffer(4,1))
  subtree:add(fieldSrcNode, buffer(5,1))
  subtree:add(fieldOpcode, buffer(6,1))

  -- Short DDP Header
  subtree:add(fieldDstSocket, buffer(9,1))
  subtree:add(fieldSrcSocket, buffer(10,1))
  subtree:add(fieldSize, buffer(7,2))
  subtree:add(fieldDDPType, buffer(11,1))

  ddpType = buffer(11,1):uint()
  size = buffer(7,2):uint()
  ddp:try(ddpType, buffer(12, size - 5):tvb(), pinfo, tree)
end

local udp_port = DissectorTable.get("udp.port")
udp_port:add(1954, ltou_protocol)
 

sfiera

Well-known member
I was sure I had written a LToU dissector before, but I couldn’t find where I had put it. After a good while of struggling, I finally got far enough to understand the question I wanted to ask (how to delegate to the built-in DDP dissector), and Google immediately found this thread for me.

Anyway, here is an updated version in a place that I will hopefully find it next time: dissect-atalk / ltou.lua
 

robin-fo

Well-known member
I once made an attempt to write a LToUDP dissector in C. It might be a little be hacky (you have to modify packet-atalk.c), but it works surprisingly well.
 

Attachments

  • Wireshark LToUDP.zip
    29.9 KB · Views: 2

robin-fo

Well-known member
Btw. as some of you might know, I‘m working on a portable AppleTalk router stack written in modern C. It currently only supports Apple VMNet and LToUDP as Data Link layer but it should be easy to implement other interfaces. There are no dependencies except the standard library and what is needed for frame sending and reception. There is no foreign code except the timestamp calculation of the Timelord (was too lazy to write own code) and some modified code from the internet inside the LToUDP and VMNet source files. The stack has progressed to the point where is runs mostly stable and supports routing between LocalTalk (LToUDP) and native Ethernet. File Sharing between Mini vMac and UTM/Qemu (as well as real Macs) works beautifully! Seeding networks and zones is also possible. The following protocols are currently working and mostly or fully implemented:
LLAP
AARP
DDP
RTMP/RTMP Stub
ZIP/ZIP Stub
NBP
AEP
ATP
TimeLord (server only)

The following protocols are planned (no work done yet):
ASP
ADSP
PAP
AFP
Some new protocols (we will see 😉)


The code is on hosted GitHub, but not yet released to the public (PM me if interested).

Work is currently on hold as I started with a second, more lightweight AppleTalk node stack specifically for embedded devices.

Don‘t expect any wonders — I might terminate working on this at any time 😝
 

cheesestraws

Well-known member
supports routing between LocalTalk (LToUDP) and native Ethernet

Oh, this is great. I currently use an AirTalk hung off one of the LocalTalk ports of my router to do LToUDP<->EtherTalk, this sounds much better.

it should be easy to implement other interfaces

This is only a suggestion: Might be fun to implement @tashtari's TashTalk as well, for wired LocalTalk. You might be able to repurpose the TashTalk code from the AirTalk firmware here: https://github.com/cheesestraws/airtalk/blob/main/main/tashtalk.c (and its callers). I'd offer to write it but realistically I don't have time at the moment, alas...

The following protocols are currently working and mostly or fully implemented

This is very, very impressive. Well done.
 

tashtari

PIC Whisperer
This is where the TashTalk would come in. Did anybody before use it with DMA?
@cheesestraws would be the only person who might answer yes to that question, I've spoken to him and he says he used the stock UART driver on the ESP32...

I wonder what using DMA would look like on the host side. TashTalk's UART protocol (see here for details) basically just spits out frame after frame with a sentinel to indicate the end of what it believes to be a complete valid frame, but it's not a sequence you'd expect a UART to treat specially. But what would you expect a UART to treat specially other than perhaps a break character?
 

Arbee

Well-known member
There is no foreign code except the timestamp calculation of the Timelord (was too lazy to write own code) and some modified code from the internet inside the LToUDP and VMNet source files.
@robin-fo What VMNet mode are you using? We're currently using VMNet "shared" mode for MAME and Wireshark shows it blocking AppleTalk packets in both directions.
 
Top