From 411429255067fa0f81072fae2c6e210513ed9763 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20De=20Keersmaeker?=
 <francois.dekeersmaeker@uclouvain.be>
Date: Tue, 3 Sep 2024 13:00:26 +0000
Subject: [PATCH] Decoupled firewall from translator

---
 .../firewall-test/translate_profiles.sh       |  17 -
 .../add_nft_rules.sh                          |   2 +-
 .../install_packages.sh                       |   0
 .../run_cppcheck.sh                           |   0
 .../run_exec.sh                               |   0
 .../run_tests.sh                              |   2 +-
 .../valgrind.supp                             |   0
 .github/workflows/cross-compile.yml           |  61 --
 .github/workflows/full-test.yaml              |  50 ++
 .github/workflows/test-firewall.yaml          | 115 ---
 .gitignore                                    |   3 -
 CMakeLists.txt                                |   4 +-
 requirements.txt                              |   5 -
 test/CMakeLists.txt                           |   6 +-
 test/device/CMakeLists.txt                    |  15 +
 test/device/firewall.nft                      |  39 +
 test/device/nfqueues.c                        | 689 ++++++++++++++++++
 test/translator/CMakeLists.txt                |   4 -
 test/translator/devices/.gitignore            |   4 -
 test/translator/devices/CMakeLists.txt        |   5 -
 .../devices/tplink-plug/profile.yaml          |  94 ---
 test/translator/translate.sh                  |  16 -
 test/{runtime => unit}/CMakeLists.txt         |   0
 test/{runtime => unit}/Vagrantfile            |   4 +-
 test/{runtime => unit}/rule_utils.c           |   0
 25 files changed, 801 insertions(+), 334 deletions(-)
 delete mode 100755 .ci_scripts/firewall-test/translate_profiles.sh
 rename .ci_scripts/{firewall-test => native-build}/add_nft_rules.sh (81%)
 rename .ci_scripts/{firewall-test => native-build}/install_packages.sh (100%)
 rename .ci_scripts/{firewall-test => native-build}/run_cppcheck.sh (100%)
 rename .ci_scripts/{firewall-test => native-build}/run_exec.sh (100%)
 rename .ci_scripts/{firewall-test => native-build}/run_tests.sh (88%)
 rename .ci_scripts/{firewall-test => native-build}/valgrind.supp (100%)
 delete mode 100644 .github/workflows/cross-compile.yml
 create mode 100644 .github/workflows/full-test.yaml
 delete mode 100644 .github/workflows/test-firewall.yaml
 delete mode 100644 requirements.txt
 create mode 100644 test/device/CMakeLists.txt
 create mode 100644 test/device/firewall.nft
 create mode 100644 test/device/nfqueues.c
 delete mode 100644 test/translator/CMakeLists.txt
 delete mode 100644 test/translator/devices/.gitignore
 delete mode 100644 test/translator/devices/CMakeLists.txt
 delete mode 100644 test/translator/devices/tplink-plug/profile.yaml
 delete mode 100755 test/translator/translate.sh
 rename test/{runtime => unit}/CMakeLists.txt (100%)
 rename test/{runtime => unit}/Vagrantfile (94%)
 rename test/{runtime => unit}/rule_utils.c (100%)

diff --git a/.ci_scripts/firewall-test/translate_profiles.sh b/.ci_scripts/firewall-test/translate_profiles.sh
deleted file mode 100755
index f7e34aa..0000000
--- a/.ci_scripts/firewall-test/translate_profiles.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-
-## CONSTANTS
-DEVICES_DIR="$GITHUB_WORKSPACE/test/translator/devices"
-TRANSLATOR_PATH="$GITHUB_WORKSPACE/src/translator/translator.py"
-
-# Ensure globbing expands to an empty list if no matches are found
-shopt -s nullglob
-
-# Loop over devices
-NFQ_BASE_ID=0
-for DEVICE in "$DEVICES_DIR"/*/; do
-    # Call translator over device profile
-    # Arguments $1 & $2 represent the verdict mode
-    python3 $TRANSLATOR_PATH "$DEVICE"profile.yaml $NFQ_BASE_ID $1 $2
-    ((NFQ_BASE_ID=NFQ_BASE_ID+100))
-done
diff --git a/.ci_scripts/firewall-test/add_nft_rules.sh b/.ci_scripts/native-build/add_nft_rules.sh
similarity index 81%
rename from .ci_scripts/firewall-test/add_nft_rules.sh
rename to .ci_scripts/native-build/add_nft_rules.sh
index 3bd0603..498850c 100755
--- a/.ci_scripts/firewall-test/add_nft_rules.sh
+++ b/.ci_scripts/native-build/add_nft_rules.sh
@@ -1,6 +1,6 @@
 EXITCODE=0
 
-for nft_script in $GITHUB_WORKSPACE/test/translator/devices/*/firewall.nft
+for nft_script in $GITHUB_WORKSPACE/test/device/firewall.nft
 do
     # Flush the ruleset before next device
     sudo nft flush ruleset
diff --git a/.ci_scripts/firewall-test/install_packages.sh b/.ci_scripts/native-build/install_packages.sh
similarity index 100%
rename from .ci_scripts/firewall-test/install_packages.sh
rename to .ci_scripts/native-build/install_packages.sh
diff --git a/.ci_scripts/firewall-test/run_cppcheck.sh b/.ci_scripts/native-build/run_cppcheck.sh
similarity index 100%
rename from .ci_scripts/firewall-test/run_cppcheck.sh
rename to .ci_scripts/native-build/run_cppcheck.sh
diff --git a/.ci_scripts/firewall-test/run_exec.sh b/.ci_scripts/native-build/run_exec.sh
similarity index 100%
rename from .ci_scripts/firewall-test/run_exec.sh
rename to .ci_scripts/native-build/run_exec.sh
diff --git a/.ci_scripts/firewall-test/run_tests.sh b/.ci_scripts/native-build/run_tests.sh
similarity index 88%
rename from .ci_scripts/firewall-test/run_tests.sh
rename to .ci_scripts/native-build/run_tests.sh
index 14c4924..e911e16 100755
--- a/.ci_scripts/firewall-test/run_tests.sh
+++ b/.ci_scripts/native-build/run_tests.sh
@@ -1,6 +1,6 @@
 EXITCODE=0
 PARSERS_DIR="$GITHUB_WORKSPACE/src/parsers"
-VALGRIND_SUPP="$GITHUB_WORKSPACE/.ci_scripts/firewall-test/valgrind.supp"
+VALGRIND_SUPP="$GITHUB_WORKSPACE/.ci_scripts/native-build/valgrind.supp"
 
 PREFIX=""
 for file in "$GITHUB_WORKSPACE"/bin/test/* "$PARSERS_DIR"/bin/test/*
diff --git a/.ci_scripts/firewall-test/valgrind.supp b/.ci_scripts/native-build/valgrind.supp
similarity index 100%
rename from .ci_scripts/firewall-test/valgrind.supp
rename to .ci_scripts/native-build/valgrind.supp
diff --git a/.github/workflows/cross-compile.yml b/.github/workflows/cross-compile.yml
deleted file mode 100644
index 7fbcf5b..0000000
--- a/.github/workflows/cross-compile.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-name: Verify cross-compilation on OpenWrt environment
-on: [push]
-
-
-jobs:
-
-  binary-verdict:
-    runs-on: ubuntu-latest
-    container: fdekeers/openwrt_tl-wdr4900_gha
-
-    steps:
-
-      - name: Checkout repository
-        uses: actions/checkout@v3
-        with:
-          submodules: recursive
-      
-      - name: Install Python packages
-        run: pip install -r $GITHUB_WORKSPACE/requirements.txt
-
-      - name: Translate profiles
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/translate_profiles.sh
-
-      - name: Run cross-compilation
-        run: $GITHUB_WORKSPACE/build.sh -d $GITHUB_WORKSPACE -t $GITHUB_WORKSPACE/openwrt/tl-wdr4900.cmake
-
-
-  rate-limit-verdict:
-    runs-on: ubuntu-latest
-    container: fdekeers/openwrt_tl-wdr4900_gha
-
-    steps:
-
-      - name: Checkout repository
-        uses: actions/checkout@v3
-        with:
-          submodules: recursive
-      
-      - name: Translate profiles
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/translate_profiles.sh -r 50
-      
-      - name: Run cross-compilation
-        run: $GITHUB_WORKSPACE/build.sh -d $GITHUB_WORKSPACE -t $GITHUB_WORKSPACE/openwrt/tl-wdr4900.cmake
-
-
-  random-verdict:
-    runs-on: ubuntu-latest
-    container: fdekeers/openwrt_tl-wdr4900_gha
-
-    steps:
-
-      - name: Checkout repository
-        uses: actions/checkout@v3
-        with:
-          submodules: recursive
-      
-      - name: Translate profiles
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/translate_profiles.sh -p 0.5
-
-      - name: Run cross-compilation
-        run: $GITHUB_WORKSPACE/build.sh -d $GITHUB_WORKSPACE -t $GITHUB_WORKSPACE/openwrt/tl-wdr4900.cmake
diff --git a/.github/workflows/full-test.yaml b/.github/workflows/full-test.yaml
new file mode 100644
index 0000000..db77846
--- /dev/null
+++ b/.github/workflows/full-test.yaml
@@ -0,0 +1,50 @@
+name: full-test
+on: [push]
+
+
+jobs:
+
+  native-build:
+    runs-on: ubuntu-latest
+    steps:
+    
+      - name: Checkout repository
+        uses: actions/checkout@v4
+        with:
+          submodules: recursive
+
+      - name: Install required packages
+        run: sudo $GITHUB_WORKSPACE/.ci_scripts/native-build/install_packages.sh
+
+      - name: Build project with CMake
+        run: $GITHUB_WORKSPACE/build.sh -d $GITHUB_WORKSPACE
+
+      - name: Run CUnit tests
+        run: $GITHUB_WORKSPACE/.ci_scripts/native-build/run_tests.sh
+
+      - name: Run Valgrind on CUnit tests
+        run: $GITHUB_WORKSPACE/.ci_scripts/native-build/run_tests.sh valgrind
+
+      - name: Run cppcheck on source files
+        run: $GITHUB_WORKSPACE/.ci_scripts/native-build/run_cppcheck.sh
+      
+      - name: Add NFTables rules
+        run:  $GITHUB_WORKSPACE/.ci_scripts/native-build/add_nft_rules.sh
+
+      - name: Run NFQueue executables
+        run: $GITHUB_WORKSPACE/.ci_scripts/native-build/run_exec.sh
+
+
+  cross-compile:
+    runs-on: ubuntu-latest
+    container: fdekeers/openwrt_tl-wdr4900_gha
+
+    steps:
+
+      - name: Checkout repository
+        uses: actions/checkout@v4
+        with:
+          submodules: recursive
+
+      - name: Run cross-compilation
+        run: $GITHUB_WORKSPACE/build.sh -d $GITHUB_WORKSPACE -t $GITHUB_WORKSPACE/openwrt/tl-wdr4900.cmake
diff --git a/.github/workflows/test-firewall.yaml b/.github/workflows/test-firewall.yaml
deleted file mode 100644
index ac35b53..0000000
--- a/.github/workflows/test-firewall.yaml
+++ /dev/null
@@ -1,115 +0,0 @@
-name: Test the whole system
-on: [push]
-
-
-jobs:
-
-  binary-verdict:
-    runs-on: ubuntu-latest
-    steps:
-    
-      - name: Checkout repository
-        uses: actions/checkout@v3
-        with:
-          submodules: recursive
-
-      - name: Install required packages
-        run: sudo $GITHUB_WORKSPACE/.ci_scripts/firewall-test/install_packages.sh
-
-      - name: Install Python packages
-        run: pip install -r $GITHUB_WORKSPACE/requirements.txt
-
-      - name: Translate profiles
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/translate_profiles.sh
-
-      - name: Build project with CMake
-        run: $GITHUB_WORKSPACE/build.sh -d $GITHUB_WORKSPACE
-
-      - name: Run CUnit tests
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_tests.sh
-
-      - name: Run Valgrind on CUnit tests
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_tests.sh valgrind
-
-      - name: Run cppcheck on source files
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_cppcheck.sh
-      
-      - name: Add NFTables rules
-        run:  $GITHUB_WORKSPACE/.ci_scripts/firewall-test/add_nft_rules.sh
-
-      - name: Run NFQueue executables
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_exec.sh
-
-
-  rate-limit-verdict:
-    runs-on: ubuntu-latest
-    steps:
-    
-      - name: Checkout repository
-        uses: actions/checkout@v3
-        with:
-          submodules: recursive
-
-      - name: Install required packages
-        run: sudo $GITHUB_WORKSPACE/.ci_scripts/firewall-test/install_packages.sh
-
-      - name: Install Python packages
-        run: pip install -r $GITHUB_WORKSPACE/requirements.txt
-
-      - name: Translate profiles
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/translate_profiles.sh -r 50
-
-      - name: Build project with CMake
-        run: $GITHUB_WORKSPACE/build.sh -d $GITHUB_WORKSPACE
-
-      - name: Run CUnit tests
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_tests.sh
-
-      - name: Run Valgrind on CUnit tests
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_tests.sh valgrind
-
-      - name: Run cppcheck on source files
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_cppcheck.sh
-      
-      - name: Add nftables rules
-        run:  $GITHUB_WORKSPACE/.ci_scripts/firewall-test/add_nft_rules.sh
-      
-      - name: Run NFQueue executables
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_exec.sh
-
-
-  random-verdict:
-    runs-on: ubuntu-latest
-    steps:
-    
-      - name: Checkout repository
-        uses: actions/checkout@v3
-        with:
-          submodules: recursive
-
-      - name: Install required packages
-        run: sudo $GITHUB_WORKSPACE/.ci_scripts/firewall-test/install_packages.sh
-
-      - name: Install Python packages
-        run: pip install -r $GITHUB_WORKSPACE/requirements.txt
-
-      - name: Translate profiles
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/translate_profiles.sh -p 0.5
-
-      - name: Build project with CMake
-        run: $GITHUB_WORKSPACE/build.sh -d $GITHUB_WORKSPACE
-
-      - name: Run CUnit tests
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_tests.sh
-
-      - name: Run Valgrind on CUnit tests
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_tests.sh valgrind
-
-      - name: Run cppcheck on source files
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_cppcheck.sh
-      
-      - name: Add nftables rules
-        run:  $GITHUB_WORKSPACE/.ci_scripts/firewall-test/add_nft_rules.sh
-
-      - name: Run NFQueue executables
-        run: $GITHUB_WORKSPACE/.ci_scripts/firewall-test/run_exec.sh
diff --git a/.gitignore b/.gitignore
index d1eba16..f571c4a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,3 @@
 # Build directories
 build
 bin
-
-# Python cache
-__pycache__
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 50f80e2..fe86854 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,6 +37,4 @@ set(PARSERS header dns dhcp http igmp ssdp coap)
 
 # Subdirectories containing code
 add_subdirectory(src)
-IF( NOT OPENWRT_CROSSCOMPILING )
-    add_subdirectory(test)
-ENDIF()
+add_subdirectory(test)
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index c6140dc..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# Libs
-PyYAML
-Jinja2
-# Custom
-pyyaml-loaders
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 5f70a07..b5fefe4 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 3.20)
 
 ## Test subdirectories
 # Sample profiles
-add_subdirectory(translator)
-# Unit tests for runtime code
+add_subdirectory(device)
+# Unit tests
 IF( NOT OPENWRT_CROSSCOMPILING )
-    add_subdirectory(runtime)
+    add_subdirectory(unit)
 ENDIF()
diff --git a/test/device/CMakeLists.txt b/test/device/CMakeLists.txt
new file mode 100644
index 0000000..f608138
--- /dev/null
+++ b/test/device/CMakeLists.txt
@@ -0,0 +1,15 @@
+# Minimum required CMake version
+cmake_minimum_required(VERSION 3.20)
+
+set(EXECUTABLE_OUTPUT_PATH ${BIN_DIR})
+
+# Nfqueue C file for device tplink-plug
+add_executable(tplink-plug nfqueues.c)
+target_link_libraries(tplink-plug pthread)
+IF( OPENWRT_CROSSCOMPILING )
+target_link_libraries(tplink-plug jansson mnl nfnetlink nftnl nftables netfilter_queue netfilter_log)
+ENDIF()
+target_link_libraries(tplink-plug nfqueue packet_utils rule_utils)
+target_link_libraries(tplink-plug header dns)
+target_include_directories(tplink-plug PRIVATE ${INCLUDE_DIR} ${INCLUDE_PARSERS_DIR})
+install(TARGETS tplink-plug DESTINATION ${EXECUTABLE_OUTPUT_PATH})
\ No newline at end of file
diff --git a/test/device/firewall.nft b/test/device/firewall.nft
new file mode 100644
index 0000000..b47709a
--- /dev/null
+++ b/test/device/firewall.nft
@@ -0,0 +1,39 @@
+#!/usr/sbin/nft -f
+
+table bridge tplink-plug {
+
+    # Chain PREROUTING, entry point for all traffic
+    chain prerouting {
+        
+        # Base chain, need configuration
+        # Default policy is ACCEPT
+        type filter hook prerouting priority 0; policy accept;
+
+        # NFQueue lan-tcp-to-phone
+        meta l4proto tcp tcp sport 9999 ip saddr 192.168.1.135 ip daddr 192.168.1.222 drop
+        
+        # NFQueue lan-tcp-to-phone-backward
+        meta l4proto tcp tcp dport 9999 ip daddr 192.168.1.135 ip saddr 192.168.1.222 drop
+        
+        # NFQueue lan-udp-to-phone
+        meta l4proto udp udp sport 9999 ip saddr 192.168.1.135 ip daddr 192.168.1.222 drop
+        
+        # NFQueue lan-udp-to-phone-backward
+        meta l4proto udp udp dport 9999 ip daddr 192.168.1.135 ip saddr 192.168.1.222 drop
+        
+        # NFQueue dns-query-tplinkapi
+        meta l4proto udp udp dport 53 ip saddr 192.168.1.135 ip daddr 192.168.1.1 queue num 0
+        
+        # NFQueue dns-query-tplinkapi-backward
+        meta l4proto udp udp sport 53 ip daddr 192.168.1.135 ip saddr 192.168.1.1 queue num 1
+        
+        # NFQueue wan-https-to-domain-tplinkapi
+        meta l4proto tcp tcp dport 443 ip saddr 192.168.1.135 queue num 10
+        
+        # NFQueue wan-https-to-domain-tplinkapi-backward
+        meta l4proto tcp tcp sport 443 ip daddr 192.168.1.135 queue num 11
+        
+        
+    }
+
+}
diff --git a/test/device/nfqueues.c b/test/device/nfqueues.c
new file mode 100644
index 0000000..fe67279
--- /dev/null
+++ b/test/device/nfqueues.c
@@ -0,0 +1,689 @@
+// THIS FILE HAS BEEN AUTOGENERATED. DO NOT EDIT.
+
+/**
+ * Nefilter queue for device tplink-plug
+ */
+
+// Standard libraries
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/time.h>
+// Custom libraries
+#include "nfqueue.h"
+#include "packet_utils.h"
+#include "rule_utils.h"
+// Parsers
+#include "header.h"
+#include "dns.h"
+
+
+/* CONSTANTS */
+
+float DROP_PROBA = 1.0;  // Drop probability for random drop verdict mode
+
+#define NUM_THREADS 4
+
+/**
+ * Thread-specific data.
+ */
+typedef struct {
+    uint8_t   id;      // Thread ID
+    uint32_t  seed;    // Thread-specific seed for random number generation
+    pthread_t thread;  // The thread itself
+} thread_data_t;
+
+thread_data_t thread_data[NUM_THREADS];
+
+dns_map_t *dns_map;  // Domain name to IP address mapping
+
+#ifdef DEBUG
+uint16_t dropped_packets = 0;
+#endif /* DEBUG */
+
+
+/**
+ * @brief dns-query-tplinkapi callback function, called when a packet enters the queue.
+ * 
+ * @param pkt_id packet ID for netfilter queue
+ * @param hash packet payload SHA256 hash (only present if LOG is defined)
+ * @param timestamp packet timestamp (only present if LOG is defined)
+ * @param pkt_len packet length, in bytes
+ * @param payload pointer to the packet payload
+ * @param arg pointer to the argument passed to the callback function
+ * @return the verdict for the packet
+ */
+#ifdef LOG
+uint32_t callback_dns_query_tplinkapi(int pkt_id, uint8_t *hash, struct timeval timestamp, int pkt_len, uint8_t *payload, void *arg)
+#else
+uint32_t callback_dns_query_tplinkapi(int pkt_id, int pkt_len, uint8_t *payload, void *arg)
+#endif /* LOG */
+{
+    #ifdef DEBUG
+    printf("Received packet from nfqueue 0\n");
+    #endif
+
+    // Skip layer 3 and 4 headers
+    size_t skipped = get_headers_length(payload);
+
+    // Parse payload as DNS message
+    dns_message_t dns_message = dns_parse_message(payload + skipped);
+    #ifdef DEBUG
+    dns_print_message(dns_message);
+    #endif
+    uint32_t verdict = NF_ACCEPT;  // Packet verdict: ACCEPT or DROP
+
+    /* Policy dns-query-tplinkapi */
+    if (
+        dns_message.header.qr == 0
+        &&
+        ( dns_message.header.qdcount > 0 && dns_message.questions->qtype == A )
+        &&
+        dns_contains_full_domain_name(dns_message.questions, dns_message.header.qdcount, "use1-api.tplinkra.com")
+    ) {
+
+
+
+        uint32_t old_verdict = verdict;
+
+        // Binary DROP
+        verdict = NF_DROP;
+
+        #if defined LOG || defined DEBUG
+        if (verdict == NF_DROP) {
+            #ifdef LOG
+            print_hash(hash);
+            printf(",%ld.%06ld,dns-query-tplinkapi,,DROP\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+            #endif /* LOG */
+            #ifdef DEBUG
+            printf("DROP - Policy: dns-query-tplinkapi\n");
+            if (old_verdict != NF_DROP) {
+                dropped_packets++;
+                printf("Dropped packets: %hu\n", dropped_packets);
+            }
+            #endif /* DEBUG */
+        }
+        #endif /* LOG || DEBUG */
+
+    }
+    /* Policy dns-query-tplinkcloud */
+    if (
+        dns_message.header.qr == 0
+        &&
+        ( dns_message.header.qdcount > 0 && dns_message.questions->qtype == A )
+        &&
+        dns_contains_full_domain_name(dns_message.questions, dns_message.header.qdcount, "n-devs.tplinkcloud.com")
+    ) {
+
+
+
+        uint32_t old_verdict = verdict;
+
+        // Binary DROP
+        verdict = NF_DROP;
+
+        #if defined LOG || defined DEBUG
+        if (verdict == NF_DROP) {
+            #ifdef LOG
+            print_hash(hash);
+            printf(",%ld.%06ld,dns-query-tplinkcloud,,DROP\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+            #endif /* LOG */
+            #ifdef DEBUG
+            printf("DROP - Policy: dns-query-tplinkcloud\n");
+            if (old_verdict != NF_DROP) {
+                dropped_packets++;
+                printf("Dropped packets: %hu\n", dropped_packets);
+            }
+            #endif /* DEBUG */
+        }
+        #endif /* LOG || DEBUG */
+
+    }
+
+    // Free memory allocated for parsed messages
+    dns_free_message(dns_message);
+
+    #ifdef LOG
+    if (verdict != NF_DROP) {
+        // Log packet as accepted
+        print_hash(hash);
+        printf(",%ld.%06ld,dns-query-tplinkapi,,ACCEPT\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+    }
+    free(hash);
+    #endif /* LOG */
+
+    return verdict;
+}
+
+
+
+
+/**
+ * @brief dns-query-tplinkapi-backward callback function, called when a packet enters the queue.
+ * 
+ * @param pkt_id packet ID for netfilter queue
+ * @param hash packet payload SHA256 hash (only present if LOG is defined)
+ * @param timestamp packet timestamp (only present if LOG is defined)
+ * @param pkt_len packet length, in bytes
+ * @param payload pointer to the packet payload
+ * @param arg pointer to the argument passed to the callback function
+ * @return the verdict for the packet
+ */
+#ifdef LOG
+uint32_t callback_dns_query_tplinkapi_backward(int pkt_id, uint8_t *hash, struct timeval timestamp, int pkt_len, uint8_t *payload, void *arg)
+#else
+uint32_t callback_dns_query_tplinkapi_backward(int pkt_id, int pkt_len, uint8_t *payload, void *arg)
+#endif /* LOG */
+{
+    #ifdef DEBUG
+    printf("Received packet from nfqueue 1\n");
+    #endif
+
+    // Skip layer 3 and 4 headers
+    size_t skipped = get_headers_length(payload);
+
+    // Parse payload as DNS message
+    dns_message_t dns_message = dns_parse_message(payload + skipped);
+    #ifdef DEBUG
+    dns_print_message(dns_message);
+    #endif
+    uint32_t verdict = NF_ACCEPT;  // Packet verdict: ACCEPT or DROP
+
+    /* Policy dns-query-tplinkapi-backward */
+    if (
+        dns_message.header.qr == 1
+        &&
+        ( dns_message.header.qdcount > 0 && dns_message.questions->qtype == A )
+        &&
+        dns_contains_full_domain_name(dns_message.questions, dns_message.header.qdcount, "use1-api.tplinkra.com")
+    ) {
+
+        // Retrieve IP addresses corresponding to the given domain name from the DNS response
+        char *domain_name = NULL;
+        ip_list_t ip_list = ip_list_init();
+        domain_name = "use1-api.tplinkra.com";
+        ip_list = dns_get_ip_from_name(dns_message.answers, dns_message.header.ancount, domain_name);
+
+        if (ip_list.ip_count > 0) {
+            // Add IP addresses to DNS map
+            dns_map_add(dns_map, domain_name, ip_list);
+        }
+
+        uint32_t old_verdict = verdict;
+
+        // Binary DROP
+        verdict = NF_DROP;
+
+        #if defined LOG || defined DEBUG
+        if (verdict == NF_DROP) {
+            #ifdef LOG
+            print_hash(hash);
+            printf(",%ld.%06ld,dns-query-tplinkapi-backward,,DROP\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+            #endif /* LOG */
+            #ifdef DEBUG
+            printf("DROP - Policy: dns-query-tplinkapi-backward\n");
+            if (old_verdict != NF_DROP) {
+                dropped_packets++;
+                printf("Dropped packets: %hu\n", dropped_packets);
+            }
+            #endif /* DEBUG */
+        }
+        #endif /* LOG || DEBUG */
+
+    }
+    /* Policy dns-query-tplinkcloud-backward */
+    if (
+        dns_message.header.qr == 1
+        &&
+        ( dns_message.header.qdcount > 0 && dns_message.questions->qtype == A )
+        &&
+        dns_contains_full_domain_name(dns_message.questions, dns_message.header.qdcount, "n-devs.tplinkcloud.com")
+    ) {
+
+        // Retrieve IP addresses corresponding to the given domain name from the DNS response
+        char *domain_name = NULL;
+        ip_list_t ip_list = ip_list_init();
+        domain_name = "n-devs.tplinkcloud.com";
+        ip_list = dns_get_ip_from_name(dns_message.answers, dns_message.header.ancount, domain_name);
+
+        if (ip_list.ip_count > 0) {
+            // Add IP addresses to DNS map
+            dns_map_add(dns_map, domain_name, ip_list);
+        }
+
+        uint32_t old_verdict = verdict;
+
+        // Binary DROP
+        verdict = NF_DROP;
+
+        #if defined LOG || defined DEBUG
+        if (verdict == NF_DROP) {
+            #ifdef LOG
+            print_hash(hash);
+            printf(",%ld.%06ld,dns-query-tplinkcloud-backward,,DROP\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+            #endif /* LOG */
+            #ifdef DEBUG
+            printf("DROP - Policy: dns-query-tplinkcloud-backward\n");
+            if (old_verdict != NF_DROP) {
+                dropped_packets++;
+                printf("Dropped packets: %hu\n", dropped_packets);
+            }
+            #endif /* DEBUG */
+        }
+        #endif /* LOG || DEBUG */
+
+    }
+
+    // Free memory allocated for parsed messages
+    dns_free_message(dns_message);
+
+    #ifdef LOG
+    if (verdict != NF_DROP) {
+        // Log packet as accepted
+        print_hash(hash);
+        printf(",%ld.%06ld,dns-query-tplinkapi-backward,,ACCEPT\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+    }
+    free(hash);
+    #endif /* LOG */
+
+    return verdict;
+}
+
+
+
+
+/**
+ * @brief wan-https-to-domain-tplinkapi callback function, called when a packet enters the queue.
+ * 
+ * @param pkt_id packet ID for netfilter queue
+ * @param hash packet payload SHA256 hash (only present if LOG is defined)
+ * @param timestamp packet timestamp (only present if LOG is defined)
+ * @param pkt_len packet length, in bytes
+ * @param payload pointer to the packet payload
+ * @param arg pointer to the argument passed to the callback function
+ * @return the verdict for the packet
+ */
+#ifdef LOG
+uint32_t callback_wan_https_to_domain_tplinkapi(int pkt_id, uint8_t *hash, struct timeval timestamp, int pkt_len, uint8_t *payload, void *arg)
+#else
+uint32_t callback_wan_https_to_domain_tplinkapi(int pkt_id, int pkt_len, uint8_t *payload, void *arg)
+#endif /* LOG */
+{
+    #ifdef DEBUG
+    printf("Received packet from nfqueue 10\n");
+    #endif
+
+    uint32_t dst_addr = get_ipv4_dst_addr(payload);  // IPv4 destination address, in network byte order
+    uint32_t verdict = NF_ACCEPT;  // Packet verdict: ACCEPT or DROP
+
+    /* Policy wan-https-to-domain-tplinkapi */
+    if (
+        ( dns_entry_contains(dns_map_get(dns_map, "use1-api.tplinkra.com"), (ip_addr_t) {.version = 4, .value.ipv4 = dst_addr}) )
+    ) {
+
+
+
+        uint32_t old_verdict = verdict;
+
+        // Binary DROP
+        verdict = NF_DROP;
+
+        #if defined LOG || defined DEBUG
+        if (verdict == NF_DROP) {
+            #ifdef LOG
+            print_hash(hash);
+            printf(",%ld.%06ld,wan-https-to-domain-tplinkapi,,DROP\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+            #endif /* LOG */
+            #ifdef DEBUG
+            printf("DROP - Policy: wan-https-to-domain-tplinkapi\n");
+            if (old_verdict != NF_DROP) {
+                dropped_packets++;
+                printf("Dropped packets: %hu\n", dropped_packets);
+            }
+            #endif /* DEBUG */
+        }
+        #endif /* LOG || DEBUG */
+
+    }
+    /* Policy wan-https-to-domain-tplinkcloud */
+    if (
+        ( dns_entry_contains(dns_map_get(dns_map, "n-devs.tplinkcloud.com"), (ip_addr_t) {.version = 4, .value.ipv4 = dst_addr}) )
+    ) {
+
+
+
+        uint32_t old_verdict = verdict;
+
+        // Binary DROP
+        verdict = NF_DROP;
+
+        #if defined LOG || defined DEBUG
+        if (verdict == NF_DROP) {
+            #ifdef LOG
+            print_hash(hash);
+            printf(",%ld.%06ld,wan-https-to-domain-tplinkcloud,,DROP\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+            #endif /* LOG */
+            #ifdef DEBUG
+            printf("DROP - Policy: wan-https-to-domain-tplinkcloud\n");
+            if (old_verdict != NF_DROP) {
+                dropped_packets++;
+                printf("Dropped packets: %hu\n", dropped_packets);
+            }
+            #endif /* DEBUG */
+        }
+        #endif /* LOG || DEBUG */
+
+    }
+
+
+    #ifdef LOG
+    if (verdict != NF_DROP) {
+        // Log packet as accepted
+        print_hash(hash);
+        printf(",%ld.%06ld,wan-https-to-domain-tplinkapi,,ACCEPT\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+    }
+    free(hash);
+    #endif /* LOG */
+
+    return verdict;
+}
+
+
+
+
+/**
+ * @brief wan-https-to-domain-tplinkapi-backward callback function, called when a packet enters the queue.
+ * 
+ * @param pkt_id packet ID for netfilter queue
+ * @param hash packet payload SHA256 hash (only present if LOG is defined)
+ * @param timestamp packet timestamp (only present if LOG is defined)
+ * @param pkt_len packet length, in bytes
+ * @param payload pointer to the packet payload
+ * @param arg pointer to the argument passed to the callback function
+ * @return the verdict for the packet
+ */
+#ifdef LOG
+uint32_t callback_wan_https_to_domain_tplinkapi_backward(int pkt_id, uint8_t *hash, struct timeval timestamp, int pkt_len, uint8_t *payload, void *arg)
+#else
+uint32_t callback_wan_https_to_domain_tplinkapi_backward(int pkt_id, int pkt_len, uint8_t *payload, void *arg)
+#endif /* LOG */
+{
+    #ifdef DEBUG
+    printf("Received packet from nfqueue 11\n");
+    #endif
+
+    uint32_t src_addr = get_ipv4_src_addr(payload);  // IPv4 source address, in network byte order
+    uint32_t verdict = NF_ACCEPT;  // Packet verdict: ACCEPT or DROP
+
+    /* Policy wan-https-to-domain-tplinkapi-backward */
+    if (
+        ( dns_entry_contains(dns_map_get(dns_map, "use1-api.tplinkra.com"), (ip_addr_t) {.version = 4, .value.ipv4 = src_addr}) )
+    ) {
+
+
+
+        uint32_t old_verdict = verdict;
+
+        // Binary DROP
+        verdict = NF_DROP;
+
+        #if defined LOG || defined DEBUG
+        if (verdict == NF_DROP) {
+            #ifdef LOG
+            print_hash(hash);
+            printf(",%ld.%06ld,wan-https-to-domain-tplinkapi-backward,,DROP\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+            #endif /* LOG */
+            #ifdef DEBUG
+            printf("DROP - Policy: wan-https-to-domain-tplinkapi-backward\n");
+            if (old_verdict != NF_DROP) {
+                dropped_packets++;
+                printf("Dropped packets: %hu\n", dropped_packets);
+            }
+            #endif /* DEBUG */
+        }
+        #endif /* LOG || DEBUG */
+
+    }
+    /* Policy wan-https-to-domain-tplinkcloud-backward */
+    if (
+        ( dns_entry_contains(dns_map_get(dns_map, "n-devs.tplinkcloud.com"), (ip_addr_t) {.version = 4, .value.ipv4 = src_addr}) )
+    ) {
+
+
+
+        uint32_t old_verdict = verdict;
+
+        // Binary DROP
+        verdict = NF_DROP;
+
+        #if defined LOG || defined DEBUG
+        if (verdict == NF_DROP) {
+            #ifdef LOG
+            print_hash(hash);
+            printf(",%ld.%06ld,wan-https-to-domain-tplinkcloud-backward,,DROP\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+            #endif /* LOG */
+            #ifdef DEBUG
+            printf("DROP - Policy: wan-https-to-domain-tplinkcloud-backward\n");
+            if (old_verdict != NF_DROP) {
+                dropped_packets++;
+                printf("Dropped packets: %hu\n", dropped_packets);
+            }
+            #endif /* DEBUG */
+        }
+        #endif /* LOG || DEBUG */
+
+    }
+
+
+    #ifdef LOG
+    if (verdict != NF_DROP) {
+        // Log packet as accepted
+        print_hash(hash);
+        printf(",%ld.%06ld,wan-https-to-domain-tplinkapi-backward,,ACCEPT\n", (long int)timestamp.tv_sec, (long int)timestamp.tv_usec);
+    }
+    free(hash);
+    #endif /* LOG */
+
+    return verdict;
+}
+
+
+
+/**
+ * @brief SIGINT handler, flush stdout and exit.
+ *
+ * @param arg unused
+ */
+void sigint_handler(int arg) {
+    fflush(stdout);
+    exit(0);
+}
+
+
+/**
+ * @brief Print program usage.
+ * 
+ * @param prog program name
+ */
+void usage(char* prog) {
+    fprintf(stderr, "Usage: %s [-s DNS_SERVER_IP] [-p DROP_PROBA]\n", prog);
+}
+
+
+/**
+ * @brief Program entry point
+ * 
+ * @param argc number of command line arguments
+ * @param argv list of command line arguments
+ * @return exit code, 0 if success
+ */
+int main(int argc, char *argv[]) {
+
+    // Initialize variables
+    int ret;
+    char *dns_server_ip = "8.8.8.8";  // Default DNS server: Google Quad8
+
+    // Setup SIGINT handler
+    signal(SIGINT, sigint_handler);
+
+
+    /* COMMAND LINE ARGUMENTS */
+    int opt;
+    while ((opt = getopt(argc, argv, "hp:s:")) != -1)
+    {
+        switch (opt)
+        {
+        case 'h':
+            /* Help */
+            usage(argv[0]);
+            exit(EXIT_SUCCESS);
+        case 'p':
+            /* Random verdict mode: drop probability (float between 0 and 1) */
+            DROP_PROBA = atof(optarg);
+            break;
+        case 's':
+            /* IP address of the network gateway */
+            dns_server_ip = optarg;
+            break;
+        default:
+            usage(argv[0]);
+            exit(EXIT_FAILURE);
+        }
+    }
+    #ifdef DEBUG
+    printf("Drop probability for random verdict mode: %f\n", DROP_PROBA);
+    #endif /* DEBUG */
+
+
+    #ifdef LOG
+    // CSV log file header
+    printf("hash,timestamp,policy,state,verdict\n");
+    #endif /* LOG */
+
+
+    /* GLOBAL STRUCTURES INITIALIZATION */
+
+    // Initialize variables for DNS
+    dns_map = dns_map_create();
+    dns_message_t dns_response;
+    ip_list_t ip_list;
+    dns_entry_t *dns_entry;
+
+    // Open socket for DNS
+    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    if (sockfd < 0) {
+        perror("Socket creation failed");
+        exit(EXIT_FAILURE);
+    }
+
+    // Server address: network gateway
+    struct sockaddr_in server_addr;
+    memset(&server_addr, 0, sizeof(server_addr));
+    server_addr.sin_family = AF_INET;
+    server_addr.sin_port = htons(53);
+    server_addr.sin_addr.s_addr = inet_addr(dns_server_ip);
+
+    // Add addresses for domain use1-api.tplinkra.com to DNS map
+    ret = dns_send_query("use1-api.tplinkra.com", sockfd, &server_addr);
+    if (ret == 0) {
+        ret = dns_receive_response(sockfd, &server_addr, &dns_response);
+        if (ret == 0) {
+            ip_list = dns_get_ip_from_name(dns_response.answers, dns_response.header.ancount, "use1-api.tplinkra.com");
+            dns_map_add(dns_map, "use1-api.tplinkra.com", ip_list);
+            #ifdef DEBUG
+            // Check DNS map has been correctly updated
+            dns_entry = dns_map_get(dns_map, "use1-api.tplinkra.com");
+            dns_entry_print(dns_entry);
+            #endif /* DEBUG */
+        }
+    }
+
+    // Add addresses for domain n-devs.tplinkcloud.com to DNS map
+    ret = dns_send_query("n-devs.tplinkcloud.com", sockfd, &server_addr);
+    if (ret == 0) {
+        ret = dns_receive_response(sockfd, &server_addr, &dns_response);
+        if (ret == 0) {
+            ip_list = dns_get_ip_from_name(dns_response.answers, dns_response.header.ancount, "n-devs.tplinkcloud.com");
+            dns_map_add(dns_map, "n-devs.tplinkcloud.com", ip_list);
+            #ifdef DEBUG
+            // Check DNS map has been correctly updated
+            dns_entry = dns_map_get(dns_map, "n-devs.tplinkcloud.com");
+            dns_entry_print(dns_entry);
+            #endif /* DEBUG */
+        }
+    }
+
+    
+
+    /* NFQUEUE THREADS LAUNCH */
+
+    // Create threads
+    uint8_t i = 0;
+
+    /* dns-query-tplinkapi */
+    // Setup thread-specific data
+    thread_data[i].id = i;
+    thread_data[i].seed = time(NULL) + i;
+    thread_arg_t thread_arg_dns_query_tplinkapi = {
+        .queue_id = 0,
+        .func = &callback_dns_query_tplinkapi,
+        .arg = &(thread_data[i].id)
+    };
+    ret = pthread_create(&(thread_data[i++].thread), NULL, nfqueue_thread, (void *) &thread_arg_dns_query_tplinkapi);
+    assert(ret == 0);
+    
+    /* dns-query-tplinkapi-backward */
+    // Setup thread-specific data
+    thread_data[i].id = i;
+    thread_data[i].seed = time(NULL) + i;
+    thread_arg_t thread_arg_dns_query_tplinkapi_backward = {
+        .queue_id = 1,
+        .func = &callback_dns_query_tplinkapi_backward,
+        .arg = &(thread_data[i].id)
+    };
+    ret = pthread_create(&(thread_data[i++].thread), NULL, nfqueue_thread, (void *) &thread_arg_dns_query_tplinkapi_backward);
+    assert(ret == 0);
+    
+    /* wan-https-to-domain-tplinkapi */
+    // Setup thread-specific data
+    thread_data[i].id = i;
+    thread_data[i].seed = time(NULL) + i;
+    thread_arg_t thread_arg_wan_https_to_domain_tplinkapi = {
+        .queue_id = 10,
+        .func = &callback_wan_https_to_domain_tplinkapi,
+        .arg = &(thread_data[i].id)
+    };
+    ret = pthread_create(&(thread_data[i++].thread), NULL, nfqueue_thread, (void *) &thread_arg_wan_https_to_domain_tplinkapi);
+    assert(ret == 0);
+    
+    /* wan-https-to-domain-tplinkapi-backward */
+    // Setup thread-specific data
+    thread_data[i].id = i;
+    thread_data[i].seed = time(NULL) + i;
+    thread_arg_t thread_arg_wan_https_to_domain_tplinkapi_backward = {
+        .queue_id = 11,
+        .func = &callback_wan_https_to_domain_tplinkapi_backward,
+        .arg = &(thread_data[i].id)
+    };
+    ret = pthread_create(&(thread_data[i++].thread), NULL, nfqueue_thread, (void *) &thread_arg_wan_https_to_domain_tplinkapi_backward);
+    assert(ret == 0);
+    
+    // Wait forever for threads
+    for (i = 0; i < NUM_THREADS; i++) {
+        pthread_join(thread_data[i++].thread, NULL);
+    }
+
+
+    /* FREE MEMORY */
+
+    // Free DNS map
+    dns_map_free(dns_map);
+
+    return 0;
+}
diff --git a/test/translator/CMakeLists.txt b/test/translator/CMakeLists.txt
deleted file mode 100644
index 1caaa53..0000000
--- a/test/translator/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-# Minimum required CMake version
-cmake_minimum_required(VERSION 3.20)
-
-add_subdirectory(devices)
diff --git a/test/translator/devices/.gitignore b/test/translator/devices/.gitignore
deleted file mode 100644
index e4b444f..0000000
--- a/test/translator/devices/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-# Translator's output files
-firewall.nft
-nfqueues.c
-*/CMakeLists.txt
diff --git a/test/translator/devices/CMakeLists.txt b/test/translator/devices/CMakeLists.txt
deleted file mode 100644
index 8a80a67..0000000
--- a/test/translator/devices/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# Minimum required CMake version
-cmake_minimum_required(VERSION 3.20)
-
-# Devices
-add_subdirectory(tplink-plug)
diff --git a/test/translator/devices/tplink-plug/profile.yaml b/test/translator/devices/tplink-plug/profile.yaml
deleted file mode 100644
index 69f51f1..0000000
--- a/test/translator/devices/tplink-plug/profile.yaml
+++ /dev/null
@@ -1,94 +0,0 @@
-# Sample profile for the TP-Link smart plug.
-
----
-device-info:
-  name: tplink-plug
-  mac: 50:c7:bf:ed:0a:54
-  ipv4: 192.168.1.135
-  network: wireless
-
-
-single-policies:
-
-  ### LAN ###
-
-  ## TCP with phone
-  
-  lan-tcp-to-phone:
-    protocols:
-      tcp:
-        src-port: 9999
-      ipv4:
-        src: self
-        dst: 192.168.1.222
-    bidirectional: true
-
-  
-  ## UDP with phone
-  
-  lan-udp-to-phone:
-    protocols:
-      udp:
-        src-port: 9999
-      ipv4:
-        src: self
-        dst: 192.168.1.222
-    bidirectional: true
-
-  
-  ## DNS for domain name use1-api.tplinkra.com
-
-  dns-query-tplinkapi:
-    protocols:
-      dns:
-        qtype: A
-        domain-name: use1-api.tplinkra.com
-      udp:
-        dst-port: 53
-      ipv4:
-        src: self
-        dst: gateway
-    bidirectional: true
-
-
-  ## DNS for domain name n-devs.tplinkcloud.com
-
-  dns-query-tplinkcloud:
-    protocols:
-      dns:
-        qtype: A
-        domain-name: n-devs.tplinkcloud.com
-      udp:
-        dst-port: 53
-      ipv4:
-        src: self
-        dst: gateway
-    bidirectional: true
-
-
-  ### WAN ###
-
-  ## HTTPS with domain use1-api.tplinkra.com
-  
-  wan-https-to-domain-tplinkapi:
-    protocols:
-      tcp:
-        dst-port: 443
-      ipv4:
-        src: self
-        dst: use1-api.tplinkra.com
-    bidirectional: true
-
-
-  ## HTTPS with domain n-devs.tplinkcloud.com
-  
-  wan-https-to-domain-tplinkcloud:
-    protocols:
-      tcp:
-        dst-port: 443
-      ipv4:
-        src: self
-        dst: n-devs.tplinkcloud.com
-    bidirectional: true
-
-...
\ No newline at end of file
diff --git a/test/translator/translate.sh b/test/translator/translate.sh
deleted file mode 100755
index d72d97a..0000000
--- a/test/translator/translate.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-## CONSTANTS
-SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )  # This script's path
-DEVICES_DIR="$SCRIPT_DIR/devices"
-TRANSLATOR_PATH="$SCRIPT_DIR/../../src/translator/translator.py"
-
-# Ensure globbing expands to an empty list if no matches are found
-shopt -s nullglob
-
-# Loop over devices
-NFQ_BASE_ID=0
-for DEVICE in "$DEVICES_DIR"/*/; do
-    python3 "$TRANSLATOR_PATH" "$DEVICE"profile.yaml $NFQ_BASE_ID
-    ((NFQ_BASE_ID=NFQ_BASE_ID+100))
-done
diff --git a/test/runtime/CMakeLists.txt b/test/unit/CMakeLists.txt
similarity index 100%
rename from test/runtime/CMakeLists.txt
rename to test/unit/CMakeLists.txt
diff --git a/test/runtime/Vagrantfile b/test/unit/Vagrantfile
similarity index 94%
rename from test/runtime/Vagrantfile
rename to test/unit/Vagrantfile
index 6faa57f..e019740 100644
--- a/test/runtime/Vagrantfile
+++ b/test/unit/Vagrantfile
@@ -11,7 +11,7 @@ VAGRANTFILE_API_VERSION = "2"
 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
   # https://vagrantcloud.com/ubuntu
   config.vm.box = "ubuntu/jammy64"  # Ubuntu 22.04
-  config.vm.hostname = "firewall-test"
+  config.vm.hostname = "native-build"
   config.vm.network "private_network", type: "dhcp", name: "vboxnet0"
 
   config.vm.provider "virtualbox" do |vb|
@@ -32,7 +32,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
   config.vm.provision "shell", inline: <<-SHELL
     sudo apt-get update
     sudo apt-get upgrade -y
-    sudo DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential cmake net-tools tshark nftables libnetfilter-queue-dev libnetfilter-log-dev python3-pip 
+    sudo DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential cmake net-tools tshark nftables libnetfilter-queue-dev libnetfilter-log-dev
     sudo pip3 install scapy
   SHELL
 
diff --git a/test/runtime/rule_utils.c b/test/unit/rule_utils.c
similarity index 100%
rename from test/runtime/rule_utils.c
rename to test/unit/rule_utils.c
-- 
GitLab