[PATCH nft] tests: validate generated netlink instructions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Don't apply -- this patch is intentionally incomplete;
I don't want to spam this list with 350k patch.

If you think this is worthwile to have in nft I'll push the full
changeset (this patch + 64 test files with recorded netlink debug output).

The test script is extended to compare netlink instructions generated
by each of the nft test files with recorded version.

Example: udp dport 80 accept in ip family should look like

ip test-ip4 input
  [ payload load 1b @ network header + 9 => reg 1 ]
  [ cmp eq reg 1 0x00000011 ]
  [ payload load 2b @ transport header + 2 => reg 1 ]
  [ cmp eq reg 1 0x00005000 ]
  [ immediate reg 0 accept ]

This is stored in udp.t.payload.ip

Other suffixes:
.payload.ip6
.payload.inet
.payload ('any')

The test script first looks for 'testname.t.payload.$family', if that
doesn't exist 'testname.t.payload' is used.

This allows for family independent test (e.g. meta).

Signed-off-by: Florian Westphal <fw@xxxxxxxxx>
---
 tests/regression/nft-test.py | 100 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 95 insertions(+), 5 deletions(-)

Full diffstat with test files:
 65 files changed, 11259 insertions(+), 5 deletions(-)
 An excerpt of the ct.t.payload is included here
 so you can have a look at the format.

diff --git a/tests/regression/nft-test.py b/tests/regression/nft-test.py
index 153f5e8..26fc2ec 100755
--- a/tests/regression/nft-test.py
+++ b/tests/regression/nft-test.py
@@ -423,9 +423,32 @@ def output_clean(pre_output, chain):
         return ""
     return rule
 
+def payload_check(payload_buffer, file, cmd):
+
+    file.seek(0, 0)
+
+    ret = False
+    i = 0
+
+    for lineno, want_line in enumerate(payload_buffer):
+        line = file.readline()
+
+        if want_line == line:
+            i += 1
+            continue
+
+        if want_line.find('[') < 0 and line.find('[') < 0:
+            continue
+        if want_line.find(']') < 0 and line.find(']') < 0:
+            continue
+
+        print_differences_warning(file.name, lineno, want_line.strip(), line.strip(), cmd);
+        return 0
+
+    return i > 0
 
 def rule_add(rule, table_list, chain_list, filename, lineno,
-             force_all_family_option):
+             force_all_family_option, filename_path):
     '''
     Adds a rule
     '''
@@ -437,7 +460,23 @@ def rule_add(rule, table_list, chain_list, filename, lineno,
         print_error(reason, filename, lineno)
         return [-1, warning, error, unit_tests]
 
+    payload_expected = []
+
     for table in table_list:
+        try:
+            payload_log = open("%s.payload.%s" % (filename_path, table[0]))
+        except (IOError):
+            payload_log = open("%s.payload" % filename_path)
+
+        if rule[1].strip() == "ok":
+	    try:
+                payload_expected.index(rule[0])
+            except (ValueError):
+                payload_expected = payload_find_expected(payload_log, rule[0])
+
+                if payload_expected == []:
+                    print_error("did not find payload information for rule '%s'" % rule[0], payload_log.name, 1)
+
         for chain in chain_list:
             if len(rule) == 1:
                 reason = "Skipping malformed test. (" + \
@@ -450,7 +489,10 @@ def rule_add(rule, table_list, chain_list, filename, lineno,
             table_info = " " + table[0] + " " + table[1] + " "
             cmd = "nft add rule" + table_info + chain + " " + rule[0]
 
-            ret = execute_cmd(cmd, filename, lineno)
+            payload_log = os.tmpfile();
+
+            cmd = "nft add rule --debug=netlink" + table_info + chain + " " + rule[0]
+            ret = execute_cmd(cmd, filename, lineno, payload_log)
 
             state = rule[1].rstrip()
             if (ret == 0 and state == "fail") or (ret != 0 and state == "ok"):
@@ -470,6 +512,20 @@ def rule_add(rule, table_list, chain_list, filename, lineno,
                 continue
 
             if ret == 0:
+            # Check for matching payload
+                if state == "ok" and not payload_check(payload_expected, payload_log, cmd):
+                    error += 1
+                    gotf = open("%s.payload.got" % filename_path, 'a')
+                    payload_log.seek(0, 0)
+                    gotf.write("# %s\n" % rule[0])
+                    while True:
+                        line = payload_log.readline()
+                        if line == "":
+                            break
+                        gotf.write(line)
+	            gotf.close()
+                    print_warning("Wrote payload for rule %s" % rule[0], gotf.name, 1)
+
             # Check output of nft
                 process = subprocess.Popen(['nft', '-nnn', 'list', 'table'] + table,
                                            shell=False, stdout=subprocess.PIPE,
@@ -536,7 +592,7 @@ def signal_handler(signal, frame):
     signal_received = 1
 
 
-def execute_cmd(cmd, filename, lineno):
+def execute_cmd(cmd, filename, lineno, stdout_log = False):
     '''
     Executes a command, checks for segfaults and returns the command exit
     code.
@@ -549,8 +605,12 @@ def execute_cmd(cmd, filename, lineno):
     print >> log_file, "command: %s" % cmd
     if debug_option:
         print cmd
+
+    if not stdout_log:
+        stdout_log = log_file
+
     ret = subprocess.call(cmd, shell=True, universal_newlines=True,
-                          stderr=subprocess.STDOUT, stdout=log_file,
+                          stderr=log_file, stdout=stdout_log,
                           preexec_fn=preexec)
     log_file.flush()
 
@@ -619,6 +679,36 @@ def set_element_process(element_line, filename, lineno):
     return set_add_elements(set_element, set_name, all_set, rule_state,
                             table_list, filename, lineno)
 
+def payload_find_expected(payload_log, rule):
+    '''
+    Find the netlink payload that should be generated by given rule in payload_log
+
+    :param payload_log: open file handle of the payload data
+    :param rule: nft rule we are going to add
+    '''
+    found = 0
+    pos = 0
+    payload_buffer = []
+
+    while True:
+        line = payload_log.readline()
+	if not line:
+	    break
+
+        if line[0] == "#":  # rule start
+            rule_line = line.strip()[2:]
+
+            if rule_line == rule.strip():
+                found = 1
+                continue
+
+        if found == 1:
+            payload_buffer.append(line)
+            if line.isspace():
+                return payload_buffer
+
+    payload_log.seek(0, 0)
+    return payload_buffer
 
 def run_test_file(filename, force_all_family_option, specific_file):
     '''
@@ -699,7 +789,7 @@ def run_test_file(filename, force_all_family_option, specific_file):
             continue
 
         result = rule_add(rule, table_list, chain_list, filename, lineno,
-                          force_all_family_option)
+                          force_all_family_option, filename_path)
         tests += 1
         ret = result[0]
         warning = result[1]
diff --git a/tests/regression/any/ct.t.payload b/tests/regression/any/ct.t.payload
new file mode 100644
index 0000000..f77c284
--- /dev/null
+++ b/tests/regression/any/ct.t.payload
@@ -0,0 +1,239 @@
+# ct state new,established, related, untracked
+ip test-ip4 output
+  [ ct load state => reg 1 ]
+  [ bitwise reg 1 = (reg=1 & 0x0000004e ) ^ 0x00000000 ]
+  [ cmp neq reg 1 0x00000000 ]
+
+# ct state != related
+ip test-ip4 output
+  [ ct load state => reg 1 ]
+  [ cmp neq reg 1 0x00000004 ]
+
+# ct state {new,established, related, untracked}
+set%d test-ip4 3
+set%d test-ip4 0
+	element 00000008  : 0 [end]	element 00000002  : 0 [end]	element 00000004  : 0 [end]	element 00000040  : 0 [end]
+ip test-ip4 output
+  [ ct load state => reg 1 ]
+  [ lookup reg 1 set set%d ]
+
+# ct state invalid drop
+ip test-ip4 output
+  [ ct load state => reg 1 ]
+  [ bitwise reg 1 = (reg=1 & 0x00000001 ) ^ 0x00000000 ]
+  [ cmp neq reg 1 0x00000000 ]
+  [ immediate reg 0 drop ]
+
+# ct state established accept
+ip test-ip4 output
+  [ ct load state => reg 1 ]
+  [ bitwise reg 1 = (reg=1 & 0x00000002 ) ^ 0x00000000 ]
+  [ cmp neq reg 1 0x00000000 ]
+  [ immediate reg 0 accept ]
+
+# ct state 8
+ip test-ip4 output
+  [ ct load state => reg 1 ]
+  [ bitwise reg 1 = (reg=1 & 0x00000008 ) ^ 0x00000000 ]
+  [ cmp neq reg 1 0x00000000 ]

[ Rest omitted, they all look pretty much the same ...]

-- 
2.0.5

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux