TashRouter: An AppleTalk Router

thecloud

Active member
The PowerBook still isn't seeing all zones, but a complete shutdown of both TashRouter and atalkd and then restart should clear that up. Its completely normal for routers to develop stale zone tables when you are starting up and shutting down routers one at a time.
Indeed, that was the case. Now that I could see the router actually working, I ended up assigning one zone name to the LtoudpPort instance and a different single zone name to the TapPort instance, so all the Mini vMac instances appear in the "Danger Zone" and the rest of the network is "EtherTalk Network". Of course I could have used the same zone name everywhere, but it's nice to see zones appear in the Chooser! :)

Code:
[atalkd.conf]
vioif0 -seed -phase 2 -net 2 -addr 2.22 -zone "EtherTalk Network"
tap0 -seed -phase 2 -net 3 -addr 3.33 -zone "EtherTalk Network"
#note: net range and zone for tap0 interface must exactly match tap0 configuration in TashRouter!

[TashRouter]
router = Router('router', ports=(
  LtoudpPort(seed_network=1, seed_zone_name=b'Danger Zone'),
  TapPort(tap_name='tap0', hw_addr=b'\xDE\xAD\xBE\xEF\xCA\xFE', seed_network_min=3, seed_network_max=3, seed_zone_names=[b'EtherTalk Network']),
))
#note: hw_addr must NOT match your actual tap0 interface address; use a fake MAC address like DE:AD:BE:EF:CA:FE here

Both the real and virtual Macs see two zones in this configuration, and can mount shared volumes from servers in either zone. This will make Mini vMac a lot more usable; previously, it was difficult to move files between its filesystem and a modern system.

More pictures!
 

Attachments

  • Zone for LToUDP network.png
    Zone for LToUDP network.png
    17.4 KB · Views: 16
  • Zone for main AppleTalk network.png
    Zone for main AppleTalk network.png
    17.1 KB · Views: 15
  • InterPoll on Mini vMac sees all devices.png
    InterPoll on Mini vMac sees all devices.png
    111.4 KB · Views: 15
Last edited:

thecloud

Active member
Here is a cleaner diff for the BSD support. With this change applied, TashRouter is working fine on NetBSD, and should behave as it did before on Linux.
Code:
diff --git a/tashrouter/port/ethertalk/tap.py b/tashrouter/port/ethertalk/tap.py
index eb0fba6..86223b2 100755
--- a/tashrouter/port/ethertalk/tap.py
+++ b/tashrouter/port/ethertalk/tap.py
@@ -40,8 +40,13 @@ class TapPort(EtherTalkPort):
   __repr__ = short_str
 
   def start(self, router):
-    self._fp = os.open('/dev/net/tun', os.O_RDWR)
-    ioctl(self._fp, self.TUNSETIFF, struct.pack('16sH22x', self._tap_name.encode('ascii') or b'', self.IFF_TAP | self.IFF_NO_PI))
+    try:
+      # open and configure Linux tap device
+      self._fp = os.open('/dev/net/tun', os.O_RDWR)
+      ioctl(self._fp, self.TUNSETIFF, struct.pack('16sH22x', self._tap_name.encode('ascii') or b'', self.IFF_TAP | self.IFF_NO_PI))
+    except:
+      # open BSD tap device if no Linux tap device
+      self._fp = os.open('/dev/' + self._tap_name, os.O_RDWR)
     super().start(router)
     self._reader_thread = Thread(target=self._reader_run)
     self._reader_thread.start()
 
Last edited:

superpete

Well-known member
I've written a rough pcap implementation for ethertalk.

I've tested it under Debian and Windows.

Under Linux you'll need libpcap installed. Under Windows you'll need NPCAP installed. You'll also need pypcap or pcap-ct installed in python.

Diff:
diff --git a/tashrouter/port/ethertalk/pcap.py b/tashrouter/port/ethertalk/pcap.py
new file mode 100644
index 0000000..d25cdd8
--- /dev/null
+++ b/tashrouter/port/ethertalk/pcap.py
@@ -0,0 +1,91 @@
+'''Port driver for EtherTalk using libpcap.'''
+
+from queue import Queue
+import logging
+import time
+from threading import Thread, Event
+
+try:
+    import pcap
+except ImportError:
+    print("[!] Missing modules pcap, try running 'pip install --upgrade pcap-ct'")
+
+from . import EtherTalkPort
+
+class PCapPort(EtherTalkPort):
+  '''Port driver for EtherTalk using libpcap.'''
+  
+
+  def __init__(self, interface_name, hw_addr, **kwargs):
+    super().__init__(hw_addr, **kwargs)
+    self._reader_thread = None
+    self._reader_started_event = Event()
+    self._reader_stop_requested = False
+    self._reader_stopped_event = Event()
+    self._interface_name = interface_name
+    self._writer_thread = None
+    self._writer_started_event = Event()
+    self._writer_stop_flag = object()
+    self._writer_stopped_event = Event()
+    self._writer_queue = Queue()
+
+  def short_str(self):
+    return self._interface_name
+
+  __str__ = short_str
+  __repr__ = short_str
+
+  def start(self, router):
+  
+    self._sniffer = pcap.pcap(name=self._interface_name, promisc=True, immediate=True, timeout_ms=250)
+  
+    # Todo, add a packet filter to only capture EtherTalk packets?
+  
+    super().start(router)
+    self._reader_thread = Thread(target=self._reader_run)
+    self._reader_thread.start()
+    self._writer_thread = Thread(target=self._writer_run)
+    self._writer_thread.start()
+    self._reader_started_event.wait()
+    self._writer_started_event.wait()
+
+  def stop(self):
+    self._reader_stop_requested = True
+    self._writer_queue.put(self._writer_stop_flag)
+    self._reader_stopped_event.wait()
+    self._writer_stopped_event.wait()
+  
+    self._sniffer.close()
+  
+    super().stop()
+
+  def send_frame(self, frame_data):
+    self._writer_queue.put(frame_data)
+
+  def _reader_run(self):
+    self._reader_started_event.set()
+
+    while not self._reader_stop_requested:
+      # Todo: make sure we're not dropping packets here
+      self._sniffer.dispatch(1, self._pcap_callback)
+    self._reader_stopped_event.set()
+  
+
+  def _writer_run(self):
+    self._writer_started_event.set()
+    while True:
+      frame_data = self._writer_queue.get()
+      if frame_data is self._writer_stop_flag: break
+      try:
+        self._sniffer.sendpacket(frame_data)
+      except OSError:
+        logging.warning("Couldn't send packet")
+        logging.debug(self._sniffer.geterr())
+        pass
+    self._writer_stopped_event.set()
+
+  def _pcap_callback(self, ts, pkt):
+    self.inbound_frame(pkt)
+
+  def get_interface_names():
+    pcap.findalldevs()
\ No newline at end of file

You'll need a line in your router to enable it, eg under Windows I have:

Python:
router = Router('router', ports=(
  LtoudpPort(seed_network=1, seed_zone_name=b'LToUDP Network'),
  PCapPort(interface_name='\\Device\\NPF_{B7D4E073-2185-4912-BBE8-3948C6636D02}', hw_addr=b'\xDE\xAD\xBE\xEF\xCA\xFE', seed_network_min=3, seed_network_max=5, seed_zone_names=[b'EtherTalk Network']),
))

Under Windows, you can get the interface name from the attached EthList.exe. Under Linux just use your interface name, eg eth0 or ens123.

If anyone tests this please let me know how you go.
 

Attachments

  • ethlist.zip
    10.6 KB · Views: 1
Last edited:
Top