Repository: Team-P4RROT/P4RROT (language: python)

Issue:
Touch command
The Touch command pretends to update a variable but never changes its value. 
(It can be used to trick the compiler if needed.)


Discussion / hints:

Fix patch:
diff --git a/src/p4rrot/psa_ebpf/commands.py b/src/p4rrot/psa_ebpf/commands.py
new file mode 100644
index 0000000..2ef02dc
--- /dev/null
+++ b/src/p4rrot/psa_ebpf/commands.py
@@ -0,0 +1,98 @@
+from p4rrot.known_types import KnownType
+from typing import Dict, List, Tuple
+from p4rrot.standard_fields import *
+from p4rrot.generator_tools import *
+from p4rrot.checks import *
+import random
+
+class AssignRandomValue(Command):
+
+    def __init__(self, vname, min_value, max_value, env=None):
+        self.env = env
+        self.vname=vname
+        self.min_value = min_value
+        self.max_value = max_value
+
+        
+        if self.env != None:
+            self.check()
+
+
+    def check(self):
+        var_exists(self.vname, self.env)
+        assert self.env.get_varinfo(self.vname)['type'] in [ uint8_t, uint16_t ,uint32_t, uint64_t ], 'Not supported random generation'
+        target_type = self.env.get_varinfo(self.vname)['type']
+        is_writeable(self.vname,self.env)
+
+
+    def get_generated_code(self):
+        gc = GeneratedCode()
+        rng_name = "rng_"+UID.get()
+        target_type = self.env.get_varinfo(self.vname)['type']
+        rng_type = target_type.get_p4_type()
+        vi  = self.env.get_varinfo(self.vname)
+        gc.get_decl().writeln('Random< {} >(({}){},({}){}) {};'.format(
+            rng_type,
+            rng_type,
+            self.min_value,
+            rng_type,
+            self.max_value,
+            rng_name
+        ))
+        gc.get_apply().writeln('{} = {}.read();'.format(vi['handle'], rng_name))
+        return gc
+
+
+    def execute(self, test_env):
+        target_type = self.env.get_varinfo(self.vname)['type']
+        test_env[self.vname] = random.randint(target_type.cast_value(min_value),target_type.cast_value(max_value))
+
+
+class AssignHash(Command):
+    def __init__(self, t_name, s_name, hash_name, env=None):
+        self.env = env
+        self.t_name = t_name
+        self.s_name = s_name
+        self.hash_name = hash_name
+
+        if self.env != None:
+            self.check()
+
+    def check(self):
+        var_exists(self.t_name, self.env)
+        var_exists(self.s_name, self.env)
+        var_exists(self.hash_name, self.env)
+        is_writeable(self.t_name, self.env)
+
+    def get_generated_code(self):
+        gc = GeneratedCode()
+        target = self.env.get_varinfo(self.t_name)
+        source = self.env.get_varinfo(self.s_name)
+        gc.get_apply().writeln('{} = {}.get_hash({});'.format(target['handle'], self.hash_name, source['handle']))
+        return gc
+
+
+class GetTimestamp(Command):
+    def __init__(self, vname, env=None):
+        self.env = env
+        self.vname = vname
+
+        if self.env != None:
+            self.check()
+
+    
+    def check(self):
+        var_exists(self.vname, self.env)
+        assert self.env.get_varinfo(self.vname)['type'] == uint64_t
+        is_writeable(self.vname, self.env)
+
+
+    def get_generated_code(self):
+        gc = GeneratedCode()
+        vi = self.env.get_varinfo(self.vname)
+        gc.get_apply().writeln('{} = (bit<64>) istd.ingress_timestamp;'.format(vi['handle']))
+        return gc
+
+    
+    def execute(self, test_env):
+        pass
diff --git a/src/p4rrot/psa_ebpf/stateful.py b/src/p4rrot/psa_ebpf/stateful.py
new file mode 100644
index 0000000..49935c8
--- /dev/null
+++ b/src/p4rrot/psa_ebpf/stateful.py
@@ -0,0 +1,49 @@
+from p4rrot.known_types import KnownType
+from typing import Dict, List, Tuple
+from p4rrot.standard_fields import *
+from p4rrot.generator_tools import *
+from enum import Enum
+
+
+class SupportedHashAlgo(Enum):
+    IDENTITY="IDENTITY"
+    CRC32 = "CRC32"
+    CRC32_CUSTOM = "CRC32_CUSTOM"
+    CRC16 = "CRC16"
+    CRC16_CUSTOM = "CRC16_CUSTOM"
+    ONES_COMPLEMENT16 = "ONES_COMPLEMENT16"
+    TARGET_DEFAULT = "TARGET_DEFAULT"
+
+class HashGenerator(SharedElement):
+    def __init__(self, vname: str, vtype: KnownType, algo: SupportedHashAlgo, env=None):
+        self.vname = vname
+        self.vtype = vtype
+        self.algo = algo
+        self.properties = {
+            'algo': algo.value
+        }
+        if env != None:
+            self.check()
+
+    def check(self):
+        assert self.vtype in [ uint8_t, uint16_t ,uint32_t, uint64_t ], 'vtype needs to be an unsigned int'
+
+    def get_name(self):
+        return self.vname
+
+    def get_type(self):
+        return self.vtype
+
+    def get_properties(self):
+        return self.properties
+
+    def get_generated_code(self):
+        gc = GeneratedCode()
+        gc.get_decl().writeln('Hash<{}>(PSA_HashAlgorithm_t.{}) {};'.format(
+            self.vtype.get_p4_type(),
+            self.algo.value,
+            self.vname))
+        return gc
+
+    def execute(self):
+        raise NotImplementedError


Test patch:
diff --git a/tests/psa_ebpf/get_timestamp/.gitignore b/tests/psa_ebpf/get_timestamp/.gitignore
new file mode 100644
index 0000000..05cb342
--- /dev/null
+++ b/tests/psa_ebpf/get_timestamp/.gitignore
@@ -0,0 +1,4 @@
+*.p4
+*.bc
+*.c
+*.o
\ No newline at end of file
diff --git a/tests/psa_ebpf/get_timestamp/codegen.py b/tests/psa_ebpf/get_timestamp/codegen.py
new file mode 100644
index 0000000..770bfbe
--- /dev/null
+++ b/tests/psa_ebpf/get_timestamp/codegen.py
@@ -0,0 +1,29 @@
+import sys
+sys.path.append('../../../src/')
+
+from p4rrot.generator_tools import *
+from p4rrot.known_types import *  
+from p4rrot.standard_fields import *
+from p4rrot.core.commands import *  
+from p4rrot.psa_ebpf.commands import *
+from p4rrot.psa_ebpf.stateful import *
+
+fp = FlowProcessor(
+istruct = [],
+ostruct = [('o1',uint64_t)],
+)
+
+fp \
+.add(GetTimestamp('o1'))\
+.add(SendBack())
+
+fs = FlowSelector(
+        'IPV4_UDP',
+        [(UdpDstPort,5555)],
+        fp
+)
+
+solution = Solution()
+solution.add_flow_processor(fp)
+solution.add_flow_selector(fs)
+solution.get_generated_code().dump('.')
diff --git a/tests/psa_ebpf/hash/.gitignore b/tests/psa_ebpf/hash/.gitignore
new file mode 100644
index 0000000..05cb342
--- /dev/null
+++ b/tests/psa_ebpf/hash/.gitignore
@@ -0,0 +1,4 @@
+*.p4
+*.bc
+*.c
+*.o
\ No newline at end of file
diff --git a/tests/psa_ebpf/hash/codegen.py b/tests/psa_ebpf/hash/codegen.py
new file mode 100644
index 0000000..fb1c6ca
--- /dev/null
+++ b/tests/psa_ebpf/hash/codegen.py
@@ -0,0 +1,38 @@
+import sys
+sys.path.append('../../../src')
+
+from p4rrot.generator_tools import *
+from p4rrot.known_types import *  
+from p4rrot.standard_fields import *
+from p4rrot.core.commands import *
+from p4rrot.psa_ebpf.commands import *
+from p4rrot.psa_ebpf.stateful import *
+
+UID.reset()
+fp = FlowProcessor(
+        istruct = [('i_uint64', uint64_t)],
+        ostruct = [('o_uint32', uint32_t)],
+        state = [HashGenerator('hash_gen', uint32_t, SupportedHashAlgo.CRC32)]
+    )
+
+(
+fp
+.add(AssignHash('o_uint32', 'i_uint64', 'hash_gen'))
+.add(SendBack())
+)  
+
+
+
+fs = FlowSelector(
+        'IPV4_UDP',
+        # 8 becasue its the length of the UDP header, seems a little unnecessary
+        # for me or at least mythic to put a 8 there and not some sort of constant
+        [(UdpDstPort,5555), (UdpLen,8+ hdr_len(fp.istruct) )],
+        fp
+    )
+
+
+solution = Solution()
+solution.add_flow_processor(fp)
+solution.add_flow_selector(fs)
+solution.get_generated_code().dump('template')
\ No newline at end of file
diff --git a/tests/psa_ebpf/hash/io.json b/tests/psa_ebpf/hash/io.json
new file mode 100644
index 0000000..69c1c25
--- /dev/null
+++ b/tests/psa_ebpf/hash/io.json
@@ -0,0 +1,4 @@
+{
+    "input":["~&s6mIV3"],
+    "output":["6t`q"]
+}
\ No newline at end of file
diff --git a/tests/psa_ebpf/rng/.gitignore b/tests/psa_ebpf/rng/.gitignore
new file mode 100644
index 0000000..05cb342
--- /dev/null
+++ b/tests/psa_ebpf/rng/.gitignore
@@ -0,0 +1,4 @@
+*.p4
+*.bc
+*.c
+*.o
\ No newline at end of file
diff --git a/tests/psa_ebpf/rng/codegen.py b/tests/psa_ebpf/rng/codegen.py
new file mode 100644
index 0000000..038e5a4
--- /dev/null
+++ b/tests/psa_ebpf/rng/codegen.py
@@ -0,0 +1,29 @@
+import sys
+sys.path.append('../../../src/')
+
+from p4rrot.generator_tools import *
+from p4rrot.known_types import *  
+from p4rrot.standard_fields import *
+from p4rrot.core.commands import *  
+from p4rrot.psa_ebpf.commands import *
+from p4rrot.psa_ebpf.stateful import *
+
+fp = FlowProcessor(
+istruct = [],
+ostruct = [('o1',uint8_t)],
+)
+
+fp \
+.add(AssignRandomValue('o1', 0, 255))\
+.add(SendBack())
+
+fs = FlowSelector(
+        'IPV4_UDP',
+        [(UdpDstPort,5555)],
+        fp
+)
+
+solution = Solution()
+solution.add_flow_processor(fp)
+solution.add_flow_selector(fs)
+solution.get_generated_code().dump('.')
diff --git a/tests/psa_ebpf_tools/ProjectSetupHelper.py b/tests/psa_ebpf_tools/ProjectSetupHelper.py
index 1028a7e..2f4ea56 100644
--- a/tests/psa_ebpf_tools/ProjectSetupHelper.py
+++ b/tests/psa_ebpf_tools/ProjectSetupHelper.py
@@ -161,11 +161,13 @@ def send_dynamic_payload(self, json_path):
                 self.client_process.kill()
                 outs, errs = self.client_process.communicate()
 
+            self.logger.info(f"Received: {outs} (0x{int.from_bytes(outs, 'big'):08x})")
+
             try: 
                 assert outputs[i].encode('utf-8') ==outs
             except AssertionError:
                 self.logger.error(f"Expected {outputs[i].encode('utf-8')}")
-                self.logger.error(f"Got {outs}")
+                self.logger.error(f"Got {outs} (0x{int.from_bytes(outs, 'big'):08x})")
                 raise