From ef1be9ed6e7c5f6b11b8a1cea0a20e78837a35f4 Mon Sep 17 00:00:00 2001
From: Martin Teichmann <martin.teichmann@gmail.com>
Date: Sun, 12 Feb 2023 10:45:21 +0000
Subject: [PATCH] avoid zero-lenght data in index datagram

the anybus client does not like our index datagram with zero
length data. While we are already at it, we also do proper padding
according to the ethercat standard. Hopefully this way we are
compatible with many more devices.
---
 ebpfcat/ebpfcat.py       | 18 +++++++--------
 ebpfcat/ethercat.py      |  7 ++++--
 ebpfcat/ethercat_test.py | 47 +++++++++++++++++++++++-----------------
 3 files changed, 41 insertions(+), 31 deletions(-)

diff --git a/ebpfcat/ebpfcat.py b/ebpfcat/ebpfcat.py
index a3a3c3d..7372199 100644
--- a/ebpfcat/ebpfcat.py
+++ b/ebpfcat/ebpfcat.py
@@ -260,36 +260,36 @@ class EtherXDP(XDP):
     counters = variables.globalVar("64I")
 
     rate = 0
+    DATA0 = 26
 
     def program(self):
         ETHERTYPE = 12
         CMD0 = 16
-        IDX0 = 17
         ADDR0 = 18
 
         with prandom(self.ebpf) & 0xffff < self.rate:
             self.dropcounter += 1
             self.ebpf.exit(XDPExitCode.DROP)
-        with self.packetSize > 24 as p, p.pH[ETHERTYPE] == 0xA488, \
+        with self.packetSize > 30 as p, p.pH[ETHERTYPE] == 0xA488, \
                 p.pB[CMD0] == 0:
             self.r3 = p.pI[ADDR0]  # use r3 for tail_call
             with self.counters.get_address(None, False, False) as (dst, _), \
                     self.r3 < FastEtherCat.MAX_PROGS:
                 self.r[dst] += 4 * self.r3
-                self.r4 = self.mB[self.r[dst]]
+                self.r4 = self.mH[self.r[dst]]
                 # we lost a packet
-                with p.pB[IDX0] == self.r4 as Else:
+                with p.pH[self.DATA0] == self.r4 as Else:
                     self.mI[self.r[dst]] += 1 + (self.r4 & 1)
                 # normal case: two packets on the wire
-                with Else, ((p.pB[IDX0] + 1 & 0xff) == self.r4) \
-                           | (p.pB[IDX0] == 0) as Else:
+                with Else, ((p.pH[self.DATA0] + 1 & 0xffff) == self.r4) \
+                           | (p.pH[self.DATA0] == 0) as Else:
                     self.mI[self.r[dst]] += 1
                     with self.r4 & 1:  # last one was active
-                        p.pB[IDX0] = self.mB[self.r[dst]]
+                        p.pH[self.DATA0] = self.mH[self.r[dst]]
                         self.exit(XDPExitCode.TX)
                 with Else:
                     self.exit(XDPExitCode.PASS)
-                p.pB[IDX0] = self.mB[self.r[dst]]
+                p.pH[self.DATA0] = self.mH[self.r[dst]]
                 self.r2 = self.get_fd(self.programs)
                 self.call(FuncId.tail_call)
         self.exit(XDPExitCode.PASS)
@@ -432,7 +432,7 @@ class FastSyncGroup(SyncGroupBase, XDP):
             await super().run()
 
     def update_devices(self, data):
-        if data[3] & 1:
+        if data[EtherXDP.DATA0 - Packet.ETHERNET_HEADER] & 1:
             self.current_data = data
         elif self.current_data is None:
             return self.asm_packet
diff --git a/ebpfcat/ethercat.py b/ebpfcat/ethercat.py
index b37b0a5..1e0b557 100644
--- a/ebpfcat/ethercat.py
+++ b/ebpfcat/ethercat.py
@@ -191,12 +191,13 @@ class Packet:
     """
     MAXSIZE = 1000  # maximum size we use for an EtherCAT packet
     ETHERNET_HEADER = 14
+    PACKET_HEADER = 16
     DATAGRAM_HEADER = 10
     DATAGRAM_TAIL = 2
 
     def __init__(self):
         self.data = []
-        self.size = 14
+        self.size = self.PACKET_HEADER
 
     def append(self, cmd, data, idx, *address):
         """Append a datagram to the packet
@@ -220,13 +221,15 @@ class Packet:
         An implicit empty datagram is added at the beginning of the packet
         that may be used as an identifier for the packet.
         """
-        ret = [pack("<HBBiHHH", self.size | 0x1000, 0, 0, index, 1 << 15, 0, 0)]
+        ret = [pack("<HBBiHHHH", (self.size-2) | 0x1000, 0, 0, index, 0x8002, 0, 0, 0)]
         for i, (cmd, data, *dgram) in enumerate(self.data, start=1):
             ret.append(pack("<BBhHHH" if len(dgram) == 3 else "<BBiHH",
                             cmd.value, *dgram,
                             len(data) | ((i < len(self.data)) << 15), 0))
             ret.append(data)
             ret.append(b"\0\0")
+        if self.size < 46:
+            ret.append(b"3" * (46 - self.size))
         return b''.join(ret)
 
     def __str__(self):
diff --git a/ebpfcat/ethercat_test.py b/ebpfcat/ethercat_test.py
index 5c921c4..e6b93b4 100644
--- a/ebpfcat/ethercat_test.py
+++ b/ebpfcat/ethercat_test.py
@@ -123,30 +123,34 @@ class Tests(TestCase):
         ec.results = [None, None]
         await ti.initialize(-1, 4)
         ai = AnalogInput(ti.channel1.value)
-        SyncGroup.packet_index = 1000
+        SyncGroup.packet_index = 0x66554433
         sg = SyncGroup(ec, [ai])
         self.task = sg.start()
         ec.expected = [
             H("2a10"  # EtherCAT Header, length & type
-              "0000e8030000008000000000"  # ID datagram
+              "0000334455660280000000000000"  # ID datagram
               # in datagram
-              "04000400801110000000000000000000000000000000000000000000"),
-            1000, # == 0x3e8, see ID datagram
+              "04000400801110000000000000000000000000000000000000000000"
+              "3333"), # padding
+            0x66554433, # index
             H("2a10"  # EtherCAT Header, length & type
-              "0000e8030000008000000000"  # ID datagram
+              "0000334455660280000000000000"  # ID datagram
               # in datagram
-              "04000400801110000000123456780000000000000000000000000000"),
-            1000,
+              "04000400801110000000123456780000000000000000000000000000"
+              "3333"),  # padding
+            0x66554433, # index
             ]
         ec.results = [
             H("2a10"  # EtherCAT Header, length & type
-              "0000e8030000008000000000"  # ID datagram
+              "0000334455660280000000000000"  # ID datagram
               # in datagram
-              "04000400801110000000123456780000000000000000000000000000"),
+              "04000400801110000000123456780000000000000000000000000000"
+              "3333"), # padding
             H("2a10"  # EtherCAT Header, length & type
-              "0000e8030000008000000000"  # ID datagram
+              "0000334455660280000000000000"  # ID datagram
               # in datagram
-              "04000400801110000000123456780000000000000000000000000000"),
+              "04000400801110000000123456780000000000000000000000000000"
+              "3333"), # padding
             ]
         self.future = Future()
         with self.assertRaises(CancelledError):
@@ -169,14 +173,15 @@ class Tests(TestCase):
         ec.results = [None, None]
         await ti.initialize(-2, 7)
         ao = AnalogOutput(ti.ch1_value)
-        SyncGroup.packet_index = 1000
+        SyncGroup.packet_index = 0x55443322
         sg = SyncGroup(ec, [ao])
         self.task = sg.start()
         ec.expected = [
             H("2210"  # EtherCAT Header, length & type
-              "0000e8030000008000000000"  # ID datagram
-              "0500070000110800000000000000000000000000"), # out datagram
-            1000, # == 0x3e8, see ID datagram
+              "0000223344550280000000000000"  # ID datagram
+              "0500070000110800000000000000000000000000" # out datagram
+              "33333333333333333333"), # padding
+            0x55443322,  # index
             ]
         ec.results = [
             (8, 0),  # return state 8, no error
@@ -187,14 +192,16 @@ class Tests(TestCase):
             await gather(self.future, self.task)
         ec.expected = [
             H("2210"  # EtherCAT Header, length & type
-              "0000e8030000008000000000"  # ID datagram
-              "0500070000110800000076980000000000000000"), # out datagram
-            1000,
+              "0000223344550280000000000000"  # ID datagram
+              "0500070000110800000076980000000000000000" # out datagram
+              "33333333333333333333"), # padding
+            0x55443322,  # index
             ]
         ec.results = [
             H("2210"  # EtherCAT Header, length & type
-              "0000e8030000008000000000"  # ID datagram
-              "0500070000110800000076980000000000000000"), # out datagram
+              "0000223344550280000000000000"  # ID datagram
+              "0500070000110800000076980000000000000000" # out datagram
+              "33333333333333333333"), # padding
             ]
         self.future = Future()
         with self.assertRaises(CancelledError):
-- 
GitLab