[nft PATCH 6/8] tests/py: Use libnftables instead of calling nft binary

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

 



This adds a simple nftables Python class in py/nftables.py which gives
access to libnftables API via ctypes module.

nft-test.py is extended to make use of the above class instead of
calling nft binary. Since command line formatting had to be touched
anyway, this patch also streamlines things a bit by introducing
__str__ methods to classes Table and Chain and making extensive use of
format strings instead of onerously adding all string parts together.

Since the called commands don't see a shell anymore, all shell meta
character escaping done in testcases is removed.

The visible effects of this change are:

* Four new warnings in ip/flowtable.t due to changing objref IDs (will
  be addressed later in a patch to libnftnl).

* Reported command line in warning and error messages changed slightly
  for obvious reasons.

* Reduction of a full test run's runtime by a factor of four. Status
  diff after running with 'time':

  < 83 test files, 77 files passed, 1724 unit tests, 0 error, 33 warning
  < 87.23user 696.13system 15:11.82elapsed 85%CPU (0avgtext+0avgdata 9604maxresident)k
  < 8inputs+36800outputs (0major+35171235minor)pagefaults 0swaps

  > 83 test files, 77 files passed, 1724 unit tests, 4 error, 33 warning
  > 6.80user 30.18system 3:45.86elapsed 16%CPU (0avgtext+0avgdata 14064maxresident)k
  > 0inputs+35808outputs (0major+2874minor)pagefaults 0swaps

Signed-off-by: Phil Sutter <phil@xxxxxx>
---
 py/.gitignore                     |   1 +
 py/nftables.py                    | 224 ++++++++++++++++++++++++++++++++++++++
 tests/py/any/ct.t                 |  14 +--
 tests/py/any/ct.t.payload         |  12 +-
 tests/py/any/log.t                |   2 +-
 tests/py/any/log.t.payload        |   2 +-
 tests/py/any/meta.t               |   4 +-
 tests/py/arp/arp.t                |   2 +-
 tests/py/arp/arp.t.payload        |   2 +-
 tests/py/arp/arp.t.payload.netdev |   2 +-
 tests/py/inet/tcp.t               |   2 +-
 tests/py/inet/tcp.t.payload       |   2 +-
 tests/py/ip/ip.t                  |   6 +-
 tests/py/ip/ip.t.payload          |   6 +-
 tests/py/ip/ip.t.payload.bridge   |   6 +-
 tests/py/ip/ip.t.payload.inet     |   6 +-
 tests/py/ip/ip.t.payload.netdev   |   6 +-
 tests/py/ip/objects.t             |   4 +-
 tests/py/nft-test.py              | 156 +++++++++++++-------------
 19 files changed, 339 insertions(+), 120 deletions(-)
 create mode 100644 py/.gitignore
 create mode 100644 py/nftables.py

diff --git a/py/.gitignore b/py/.gitignore
new file mode 100644
index 0000000000000..0d20b6487c61e
--- /dev/null
+++ b/py/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/py/nftables.py b/py/nftables.py
new file mode 100644
index 0000000000000..c175975076982
--- /dev/null
+++ b/py/nftables.py
@@ -0,0 +1,224 @@
+import json
+from ctypes import *
+import sys
+
+class Nftables:
+    """A class representing libnftables interface"""
+
+    debug_flags = {
+        "scanner":   0x1,
+        "parser":    0x2,
+        "eval":      0x4,
+        "netlink":   0x8,
+        "mnl":       0x10,
+        "proto-ctx": 0x20,
+        "segtree":   0x40,
+    }
+
+    numeric_levels = {
+        "none": 0,
+        "addr": 1,
+        "port": 2,
+        "all":  3,
+    }
+
+    def __init__(self, sofile="libnftables.so"):
+        """Instantiate a new Nftables class object.
+
+        Accepts a shared object file to open, by default standard search path
+        is searched for a file named 'libnftables.so'.
+
+        After loading the library using ctypes module, a new nftables context
+        is requested from the library and buffering of output and error streams
+        is turned on.
+        """
+        lib = cdll.LoadLibrary(sofile)
+
+        ### API function definitions
+
+        self.nft_ctx_new = lib.nft_ctx_new
+        self.nft_ctx_new.restype = c_void_p
+        self.nft_ctx_new.argtypes = [c_int]
+
+        self.nft_ctx_output_get_handle = lib.nft_ctx_output_get_handle
+        self.nft_ctx_output_get_handle.restype = c_bool
+        self.nft_ctx_output_get_handle.argtypes = [c_void_p]
+
+        self.nft_ctx_output_set_handle = lib.nft_ctx_output_set_handle
+        self.nft_ctx_output_set_handle.argtypes = [c_void_p, c_bool]
+
+        self.nft_ctx_output_get_numeric = lib.nft_ctx_output_get_numeric
+        self.nft_ctx_output_get_numeric.restype = c_int
+        self.nft_ctx_output_get_numeric.argtypes = [c_void_p]
+
+        self.nft_ctx_output_set_numeric = lib.nft_ctx_output_set_numeric
+        self.nft_ctx_output_set_numeric.argtypes = [c_void_p, c_int]
+
+        self.nft_ctx_output_get_stateless = lib.nft_ctx_output_get_stateless
+        self.nft_ctx_output_get_stateless.restype = c_bool
+        self.nft_ctx_output_get_stateless.argtypes = [c_void_p]
+
+        self.nft_ctx_output_set_stateless = lib.nft_ctx_output_set_stateless
+        self.nft_ctx_output_set_stateless.argtypes = [c_void_p, c_bool]
+
+        self.nft_ctx_output_get_debug = lib.nft_ctx_output_get_debug
+        self.nft_ctx_output_get_debug.restype = c_int
+        self.nft_ctx_output_get_debug.argtypes = [c_void_p]
+
+        self.nft_ctx_output_set_debug = lib.nft_ctx_output_set_debug
+        self.nft_ctx_output_set_debug.argtypes = [c_void_p, c_int]
+
+        self.nft_ctx_buffer_output = lib.nft_ctx_buffer_output
+        self.nft_ctx_buffer_output.restype = c_int
+        self.nft_ctx_buffer_output.argtypes = [c_void_p]
+
+        self.nft_ctx_get_output_buffer = lib.nft_ctx_get_output_buffer
+        self.nft_ctx_get_output_buffer.restype = c_char_p
+        self.nft_ctx_get_output_buffer.argtypes = [c_void_p]
+
+        self.nft_ctx_buffer_error = lib.nft_ctx_buffer_error
+        self.nft_ctx_buffer_error.restype = c_int
+        self.nft_ctx_buffer_error.argtypes = [c_void_p]
+
+        self.nft_ctx_get_error_buffer = lib.nft_ctx_get_error_buffer
+        self.nft_ctx_get_error_buffer.restype = c_char_p
+        self.nft_ctx_get_error_buffer.argtypes = [c_void_p]
+
+        self.nft_run_cmd_from_buffer = lib.nft_run_cmd_from_buffer
+        self.nft_run_cmd_from_buffer.restype = c_int
+        self.nft_run_cmd_from_buffer.argtypes = [c_void_p, c_char_p, c_int]
+
+        self.nft_ctx_free = lib.nft_ctx_free
+        lib.nft_ctx_free.argtypes = [c_void_p]
+
+        # initialize libnftables context
+        self.__ctx = self.nft_ctx_new(0)
+        self.nft_ctx_buffer_output(self.__ctx)
+        self.nft_ctx_buffer_error(self.__ctx)
+
+    def get_handle_output(self):
+        """Get the current state of handle output.
+
+        Returns a boolean indicating whether handle output is active or not.
+        """
+        return self.nft_ctx_output_get_handle(self.__ctx)
+
+    def set_handle_output(self, val):
+        """Enable or disable handle output.
+
+        Accepts a boolean turning handle output on or off.
+
+        Returns the previous value.
+        """
+        old = self.get_handle_output()
+        self.nft_ctx_output_set_handle(self.__ctx, val)
+        return old
+
+    def get_numeric_output(self):
+        """Get the current state of numeric output.
+
+        Returns a boolean indicating whether boolean output is active or not.
+        """
+        return self.nft_ctx_output_get_numeric(self.__ctx)
+
+    def set_numeric_output(self, val):
+        """Enable or disable numeric output.
+
+        Accepts a boolean turning numeric output on or off.
+
+        Returns the previous value.
+        """
+        old = self.get_numeric_output()
+
+        if type(val) is str:
+            val = self.numeric_levels[val]
+        self.nft_ctx_output_set_numeric(self.__ctx, val)
+
+        return old
+
+    def get_stateless_output(self):
+        """Get the current state of stateless output.
+
+        Returns a boolean indicating whether stateless output is active or not.
+        """
+        return self.nft_ctx_output_get_stateless(self.__ctx)
+
+    def set_stateless_output(self, val):
+        """Enable or Disable stateless output.
+
+        Accepts a boolean turning stateless output either on or off.
+
+        Returns the previous value.
+        """
+        old = self.get_stateless_output()
+        self.nft_ctx_output_set_stateless(self.__ctx, val)
+        return old
+
+    def get_debug(self):
+        """Get currently active debug flags.
+
+        Returns a set of flag names. See set_debug() for details.
+        """
+        val = self.nft_ctx_output_get_debug(self.__ctx)
+
+        names = []
+        for n,v in self.debug_flags.items():
+            if val & v:
+                names.append(n)
+                val &= ~v
+        if val:
+            names.append(val)
+
+        return names
+
+    def set_debug(self, values):
+        """Set debug output flags.
+
+        Accepts either a single flag or a set of flags. Each flag might be
+        given either as string or integer value as shown in the following
+        table:
+
+        Name      | Value (hex)
+        -----------------------
+        scanner   | 0x1
+        parser    | 0x2
+        eval      | 0x4
+        netlink   | 0x8
+        mnl       | 0x10
+        proto-ctx | 0x20
+        segtree   | 0x40
+
+        Returns a set of previously active debug flags, as returned by
+        get_debug() method.
+        """
+        old = self.get_debug()
+
+        if type(values) in [str, int]:
+            values = [values]
+
+        val = 0
+        for v in values:
+            if type(v) is str:
+                v = self.debug_flags[v]
+            val |= v
+
+        self.nft_ctx_output_set_debug(self.__ctx, val)
+
+        return old
+
+    def cmd(self, cmdline):
+        """Run a simple nftables command via libnftables.
+
+        Accepts a string containing an nftables command just like what one
+        would enter into an interactive nftables (nft -i) session.
+
+        Returns a tuple (rc, output, error):
+        rc     -- return code as returned by nft_run_cmd_from_buffer() fuction
+        output -- a string containing output written to stdout
+        error  -- a string containing output written to stderr
+        """
+        rc = self.nft_run_cmd_from_buffer(self.__ctx, cmdline, len(cmdline))
+        output = self.nft_ctx_get_output_buffer(self.__ctx)
+        error = self.nft_ctx_get_error_buffer(self.__ctx)
+
+        return (rc, output, error)
diff --git a/tests/py/any/ct.t b/tests/py/any/ct.t
index 6334dd760b563..ce6d51a401151 100644
--- a/tests/py/any/ct.t
+++ b/tests/py/any/ct.t
@@ -75,19 +75,19 @@ ct expiration != {33-55};ok;ct expiration != { 33s-55s}
 
 ct helper "ftp";ok
 ct helper "12345678901234567";fail
-ct helper '""';fail
+ct helper "";fail
 
 ct state . ct mark { new . 0x12345678};ok
 ct state . ct mark { new . 0x12345678, new . 0x34127856, established . 0x12785634};ok
 ct direction . ct mark { original . 0x12345678};ok
 ct state . ct mark vmap { new . 0x12345678 : drop};ok
 
-ct original bytes \> 100000;ok;ct original bytes > 100000
-ct reply packets \< 100;ok;ct reply packets < 100
-ct bytes \> 100000;ok;ct bytes > 100000
+ct original bytes > 100000;ok
+ct reply packets < 100;ok
+ct bytes > 100000;ok
 
-ct avgpkt \> 200;ok;ct avgpkt > 200
-ct original avgpkt \< 500;ok;ct original avgpkt < 500
+ct avgpkt > 200;ok
+ct original avgpkt < 500;ok
 
 # bogus direction
 ct both bytes gt 1;fail
@@ -107,7 +107,7 @@ ct mark original;fail
 
 ct event set new;ok
 ct event set new or related or destroy or foobar;fail
-ct event set 'new | related | destroy | label';ok;ct event set new,related,destroy,label
+ct event set new | related | destroy | label;ok;ct event set new,related,destroy,label
 ct event set new,related,destroy,label;ok
 ct event set new,destroy;ok
 ct event set 1;ok;ct event set new
diff --git a/tests/py/any/ct.t.payload b/tests/py/any/ct.t.payload
index 7ebf3f8d327e0..9f288e79140df 100644
--- a/tests/py/any/ct.t.payload
+++ b/tests/py/any/ct.t.payload
@@ -343,31 +343,31 @@ ip test-ip4 output
   [ lookup reg 1 set __map%d dreg 1 ]
   [ ct set mark with reg 1 ]
 
-# ct original bytes \> 100000
+# ct original bytes > 100000
 ip test-ip4 output
   [ ct load bytes => reg 1 , dir original ]
   [ byteorder reg 1 = hton(reg 1, 8, 8) ]
   [ cmp gt reg 1 0x00000000 0xa0860100 ]
 
-# ct reply packets \< 100
+# ct reply packets < 100
 ip test-ip4 output
   [ ct load packets => reg 1 , dir reply ]
   [ byteorder reg 1 = hton(reg 1, 8, 8) ]
   [ cmp lt reg 1 0x00000000 0x64000000 ]
 
-# ct bytes \> 100000
+# ct bytes > 100000
 ip test-ip4 output
   [ ct load bytes => reg 1 ]
   [ byteorder reg 1 = hton(reg 1, 8, 8) ]
   [ cmp gt reg 1 0x00000000 0xa0860100 ]
 
-# ct avgpkt \> 200
+# ct avgpkt > 200
 ip test-ip4 output
   [ ct load avgpkt => reg 1 ]
   [ byteorder reg 1 = hton(reg 1, 8, 8) ]
   [ cmp gt reg 1 0x00000000 0xc8000000 ]
 
-# ct original avgpkt \< 500
+# ct original avgpkt < 500
 ip test-ip4 output
   [ ct load avgpkt => reg 1 , dir original ]
   [ byteorder reg 1 = hton(reg 1, 8, 8) ]
@@ -396,7 +396,7 @@ ip test-ip4 output
   [ immediate reg 1 0x00000001 ]
   [ ct set event with reg 1 ]
 
-# ct event set 'new | related | destroy | label'
+# ct event set new | related | destroy | label
 ip test-ip4 output
   [ immediate reg 1 0x00000407 ]
   [ ct set event with reg 1 ]
diff --git a/tests/py/any/log.t b/tests/py/any/log.t
index 37982022a6232..d1b4ab623c4de 100644
--- a/tests/py/any/log.t
+++ b/tests/py/any/log.t
@@ -24,7 +24,7 @@ log prefix aaaaa-aaaaaa group 2 snaplen 33;ok;log prefix "aaaaa-aaaaaa" group 2
 # The correct rule is log group 2 queue-threshold 2
 log group 2 queue-threshold 2;ok
 log group 2 snaplen 33;ok
-log group 2 prefix \"nft-test: \";ok;log prefix "nft-test: " group 2
+log group 2 prefix "nft-test: ";ok;log prefix "nft-test: " group 2
 
 log flags all;ok
 log level debug flags ip options flags skuid;ok
diff --git a/tests/py/any/log.t.payload b/tests/py/any/log.t.payload
index 385b8bba2460d..ffb914d20de5f 100644
--- a/tests/py/any/log.t.payload
+++ b/tests/py/any/log.t.payload
@@ -46,7 +46,7 @@ ip test-ip4 output
 ip test-ip4 output
   [ log group 2 snaplen 33 qthreshold 0 ]
 
-# log group 2 prefix \"nft-test: \"
+# log group 2 prefix "nft-test: "
 ip test-ip4 output
   [ log prefix nft-test:  group 2 snaplen 0 qthreshold 0 ]
 
diff --git a/tests/py/any/meta.t b/tests/py/any/meta.t
index 9df038e510f42..b3bb0504f0ff7 100644
--- a/tests/py/any/meta.t
+++ b/tests/py/any/meta.t
@@ -70,7 +70,7 @@ meta iifname {"dummy0", "lo"};ok;iifname {"dummy0", "lo"}
 meta iifname != {"dummy0", "lo"};ok;iifname != {"dummy0", "lo"}
 meta iifname "dummy*";ok;iifname "dummy*"
 meta iifname "dummy\*";ok;iifname "dummy\*"
-meta iifname '""';fail
+meta iifname "";fail
 
 meta iiftype {ether, ppp, ipip, ipip6, loopback, sit, ipgre};ok;iiftype {ether, ppp, ipip, ipip6, loopback, sit, ipgre}
 meta iiftype != {ether, ppp, ipip, ipip6, loopback, sit, ipgre};ok;iiftype != {ether, ppp, ipip, ipip6, loopback, sit, ipgre}
@@ -89,7 +89,7 @@ meta oifname != "dummy0";ok;oifname != "dummy0"
 meta oifname { "dummy0", "lo"};ok;oifname { "dummy0", "lo"}
 meta oifname "dummy*";ok;oifname "dummy*"
 meta oifname "dummy\*";ok;oifname "dummy\*"
-meta oifname '""';fail
+meta oifname "";fail
 
 meta oiftype {ether, ppp, ipip, ipip6, loopback, sit, ipgre};ok;oiftype {ether, ppp, ipip, ipip6, loopback, sit, ipgre}
 meta oiftype != {ether, ppp, ipip, ipip6, loopback, sit, ipgre};ok;oiftype != {ether, ppp, ipip, ipip6, loopback, sit, ipgre}
diff --git a/tests/py/arp/arp.t b/tests/py/arp/arp.t
index 36c7f1964841c..d62cc546f24d5 100644
--- a/tests/py/arp/arp.t
+++ b/tests/py/arp/arp.t
@@ -55,4 +55,4 @@ arp operation != inreply;ok
 arp operation != nak;ok
 arp operation != reply;ok
 
-meta iifname \"invalid\" arp ptype 0x0800 arp htype 1 arp hlen 6 arp plen 4 @nh,192,32 0xc0a88f10 @nh,144,48 set 0x112233445566;ok;iifname "invalid" arp htype 1 arp ptype ip arp hlen 6 arp plen 4 @nh,192,32 3232272144 @nh,144,48 set 18838586676582
+meta iifname "invalid" arp ptype 0x0800 arp htype 1 arp hlen 6 arp plen 4 @nh,192,32 0xc0a88f10 @nh,144,48 set 0x112233445566;ok;iifname "invalid" arp htype 1 arp ptype ip arp hlen 6 arp plen 4 @nh,192,32 3232272144 @nh,144,48 set 18838586676582
diff --git a/tests/py/arp/arp.t.payload b/tests/py/arp/arp.t.payload
index 34ae241448067..bb95e1c14be04 100644
--- a/tests/py/arp/arp.t.payload
+++ b/tests/py/arp/arp.t.payload
@@ -268,7 +268,7 @@ arp test-arp input
   [ payload load 2b @ network header + 6 => reg 1 ]
   [ cmp neq reg 1 0x00000200 ]
 
-# meta iifname \"invalid\" arp ptype 0x0800 arp htype 1 arp hlen 6 arp plen 4 @nh,192,32 0xc0a88f10 @nh,144,48 set 0x112233445566
+# meta iifname "invalid" arp ptype 0x0800 arp htype 1 arp hlen 6 arp plen 4 @nh,192,32 0xc0a88f10 @nh,144,48 set 0x112233445566
 arp test-arp input
   [ meta load iifname => reg 1 ]
   [ cmp eq reg 1 0x61766e69 0x0064696c 0x00000000 0x00000000 ]
diff --git a/tests/py/arp/arp.t.payload.netdev b/tests/py/arp/arp.t.payload.netdev
index 21818ba2f2db8..00c26ccc1932f 100644
--- a/tests/py/arp/arp.t.payload.netdev
+++ b/tests/py/arp/arp.t.payload.netdev
@@ -358,7 +358,7 @@ netdev test-netdev ingress
   [ payload load 2b @ network header + 6 => reg 1 ]
   [ cmp neq reg 1 0x00000200 ]
 
-# meta iifname \"invalid\" arp ptype 0x0800 arp htype 1 arp hlen 6 arp plen 4 @nh,192,32 0xc0a88f10 @nh,144,48 set 0x112233445566
+# meta iifname "invalid" arp ptype 0x0800 arp htype 1 arp hlen 6 arp plen 4 @nh,192,32 0xc0a88f10 @nh,144,48 set 0x112233445566
 netdev test-netdev ingress
   [ meta load iifname => reg 1 ]
   [ cmp eq reg 1 0x61766e69 0x0064696c 0x00000000 0x00000000 ]
diff --git a/tests/py/inet/tcp.t b/tests/py/inet/tcp.t
index f25be59969d7f..5276516625cce 100644
--- a/tests/py/inet/tcp.t
+++ b/tests/py/inet/tcp.t
@@ -76,7 +76,7 @@ tcp flags { fin, syn, rst, psh, ack, urg, ecn, cwr} drop;ok
 tcp flags != { fin, urg, ecn, cwr} drop;ok
 tcp flags cwr;ok
 tcp flags != cwr;ok
-tcp 'flags & (syn|fin) == (syn|fin)';ok;tcp flags & (fin | syn) == fin | syn
+tcp flags & (syn|fin) == (syn|fin);ok;tcp flags & (fin | syn) == fin | syn
 
 tcp window 22222;ok
 tcp window 22;ok
diff --git a/tests/py/inet/tcp.t.payload b/tests/py/inet/tcp.t.payload
index bf2ffaaff3bc9..512b42e9df084 100644
--- a/tests/py/inet/tcp.t.payload
+++ b/tests/py/inet/tcp.t.payload
@@ -421,7 +421,7 @@ inet test-inet input
   [ payload load 1b @ transport header + 13 => reg 1 ]
   [ cmp neq reg 1 0x00000080 ]
 
-# tcp 'flags & (syn|fin) == (syn|fin)'
+# tcp flags & (syn|fin) == (syn|fin)
 inet test-inet input
   [ meta load l4proto => reg 1 ]
   [ cmp eq reg 1 0x00000006 ]
diff --git a/tests/py/ip/ip.t b/tests/py/ip/ip.t
index d773042afe021..0421d01bf6e49 100644
--- a/tests/py/ip/ip.t
+++ b/tests/py/ip/ip.t
@@ -113,10 +113,10 @@ ip daddr 192.168.0.1;ok
 ip daddr 192.168.0.1 drop;ok
 ip daddr 192.168.0.2;ok
 
-ip saddr \& 0xff == 1;ok;ip saddr & 0.0.0.255 == 0.0.0.1
-ip saddr \& 0.0.0.255 \< 0.0.0.127;ok;ip saddr & 0.0.0.255 < 0.0.0.127
+ip saddr & 0xff == 1;ok;ip saddr & 0.0.0.255 == 0.0.0.1
+ip saddr & 0.0.0.255 < 0.0.0.127;ok
 
-ip saddr \& 0xffff0000 == 0xffff0000;ok;ip saddr 255.255.0.0/16
+ip saddr & 0xffff0000 == 0xffff0000;ok;ip saddr 255.255.0.0/16
 
 ip version 4 ip hdrlength 5;ok
 ip hdrlength 0;ok
diff --git a/tests/py/ip/ip.t.payload b/tests/py/ip/ip.t.payload
index e9de690d8f70e..eba79dec4f2e9 100644
--- a/tests/py/ip/ip.t.payload
+++ b/tests/py/ip/ip.t.payload
@@ -484,19 +484,19 @@ ip test-ip4 input
   [ payload load 4b @ network header + 16 => reg 1 ]
   [ cmp eq reg 1 0x0200a8c0 ]
 
-# ip saddr \& 0xff == 1
+# ip saddr & 0xff == 1
 ip test-ip4 input
   [ payload load 4b @ network header + 12 => reg 1 ]
   [ bitwise reg 1 = (reg=1 & 0xff000000 ) ^ 0x00000000 ]
   [ cmp eq reg 1 0x01000000 ]
 
-# ip saddr \& 0.0.0.255 \< 0.0.0.127
+# ip saddr & 0.0.0.255 < 0.0.0.127
 ip test-ip4 input
   [ payload load 4b @ network header + 12 => reg 1 ]
   [ bitwise reg 1 = (reg=1 & 0xff000000 ) ^ 0x00000000 ]
   [ cmp lt reg 1 0x7f000000 ]
 
-# ip saddr \& 0xffff0000 == 0xffff0000
+# ip saddr & 0xffff0000 == 0xffff0000
 ip test-ip4 input
   [ payload load 4b @ network header + 12 => reg 1 ]
   [ bitwise reg 1 = (reg=1 & 0x0000ffff ) ^ 0x00000000 ]
diff --git a/tests/py/ip/ip.t.payload.bridge b/tests/py/ip/ip.t.payload.bridge
index d1c57a01db739..f16759bfbbaea 100644
--- a/tests/py/ip/ip.t.payload.bridge
+++ b/tests/py/ip/ip.t.payload.bridge
@@ -632,7 +632,7 @@ bridge test-bridge input
   [ payload load 4b @ network header + 16 => reg 1 ]
   [ cmp eq reg 1 0x0200a8c0 ]
 
-# ip saddr \& 0xff == 1
+# ip saddr & 0xff == 1
 bridge test-bridge input 
   [ payload load 2b @ link header + 12 => reg 1 ]
   [ cmp eq reg 1 0x00000008 ]
@@ -640,7 +640,7 @@ bridge test-bridge input
   [ bitwise reg 1 = (reg=1 & 0xff000000 ) ^ 0x00000000 ]
   [ cmp eq reg 1 0x01000000 ]
 
-# ip saddr \& 0.0.0.255 \< 0.0.0.127
+# ip saddr & 0.0.0.255 < 0.0.0.127
 bridge test-bridge input 
   [ payload load 2b @ link header + 12 => reg 1 ]
   [ cmp eq reg 1 0x00000008 ]
@@ -648,7 +648,7 @@ bridge test-bridge input
   [ bitwise reg 1 = (reg=1 & 0xff000000 ) ^ 0x00000000 ]
   [ cmp lt reg 1 0x7f000000 ]
 
-# ip saddr \& 0xffff0000 == 0xffff0000
+# ip saddr & 0xffff0000 == 0xffff0000
 bridge test-bridge input 
   [ payload load 2b @ link header + 12 => reg 1 ]
   [ cmp eq reg 1 0x00000008 ]
diff --git a/tests/py/ip/ip.t.payload.inet b/tests/py/ip/ip.t.payload.inet
index e6cb700f0db3b..12b03e2e16f24 100644
--- a/tests/py/ip/ip.t.payload.inet
+++ b/tests/py/ip/ip.t.payload.inet
@@ -632,7 +632,7 @@ inet test-inet input
   [ payload load 4b @ network header + 16 => reg 1 ]
   [ cmp eq reg 1 0x0200a8c0 ]
 
-# ip saddr \& 0xff == 1
+# ip saddr & 0xff == 1
 inet test-inet input
   [ meta load nfproto => reg 1 ]
   [ cmp eq reg 1 0x00000002 ]
@@ -640,7 +640,7 @@ inet test-inet input
   [ bitwise reg 1 = (reg=1 & 0xff000000 ) ^ 0x00000000 ]
   [ cmp eq reg 1 0x01000000 ]
 
-# ip saddr \& 0.0.0.255 \< 0.0.0.127
+# ip saddr & 0.0.0.255 < 0.0.0.127
 inet test-inet input
   [ meta load nfproto => reg 1 ]
   [ cmp eq reg 1 0x00000002 ]
@@ -648,7 +648,7 @@ inet test-inet input
   [ bitwise reg 1 = (reg=1 & 0xff000000 ) ^ 0x00000000 ]
   [ cmp lt reg 1 0x7f000000 ]
 
-# ip saddr \& 0xffff0000 == 0xffff0000
+# ip saddr & 0xffff0000 == 0xffff0000
 inet test-inet input
   [ meta load nfproto => reg 1 ]
   [ cmp eq reg 1 0x00000002 ]
diff --git a/tests/py/ip/ip.t.payload.netdev b/tests/py/ip/ip.t.payload.netdev
index 0f15247fa0f3e..187a39f3ab890 100644
--- a/tests/py/ip/ip.t.payload.netdev
+++ b/tests/py/ip/ip.t.payload.netdev
@@ -531,7 +531,7 @@ netdev test-netdev ingress
   [ cmp eq reg 1 0x0100a8c0 ]
   [ immediate reg 0 drop ]
 
-# ip saddr \& 0xff == 1
+# ip saddr & 0xff == 1
 netdev test-netdev ingress 
   [ meta load protocol => reg 1 ]
   [ cmp eq reg 1 0x00000008 ]
@@ -539,7 +539,7 @@ netdev test-netdev ingress
   [ bitwise reg 1 = (reg=1 & 0xff000000 ) ^ 0x00000000 ]
   [ cmp eq reg 1 0x01000000 ]
 
-# ip saddr \& 0.0.0.255 \< 0.0.0.127
+# ip saddr & 0.0.0.255 < 0.0.0.127
 netdev test-netdev ingress 
   [ meta load protocol => reg 1 ]
   [ cmp eq reg 1 0x00000008 ]
@@ -547,7 +547,7 @@ netdev test-netdev ingress
   [ bitwise reg 1 = (reg=1 & 0xff000000 ) ^ 0x00000000 ]
   [ cmp lt reg 1 0x7f000000 ]
 
-# ip saddr \& 0xffff0000 == 0xffff0000
+# ip saddr & 0xffff0000 == 0xffff0000
 netdev test-netdev ingress 
   [ meta load protocol => reg 1 ]
   [ cmp eq reg 1 0x00000008 ]
diff --git a/tests/py/ip/objects.t b/tests/py/ip/objects.t
index 76b802ac969b0..5e8c763136ff7 100644
--- a/tests/py/ip/objects.t
+++ b/tests/py/ip/objects.t
@@ -19,8 +19,8 @@ ip saddr 192.168.1.3 quota name "qt3";fail
 quota name tcp dport map {443 : "qt1", 80 : "qt2", 22 : "qt1"};ok
 
 # ct helper
-%cthelp1 type ct helper { type \"ftp\" protocol tcp\; };ok
-%cthelp2 type ct helper { type \"ftp\" protocol tcp\; l3proto ip6\; };fail
+%cthelp1 type ct helper { type "ftp" protocol tcp; };ok
+%cthelp2 type ct helper { type "ftp" protocol tcp; l3proto ip6; };fail
 
 ct helper set "cthelp1";ok
 ct helper set tcp dport map {21 : "cthelp1", 2121 : "cthelp1" };ok
diff --git a/tests/py/nft-test.py b/tests/py/nft-test.py
index 7998914aa4180..d2d13218cac98 100755
--- a/tests/py/nft-test.py
+++ b/tests/py/nft-test.py
@@ -15,11 +15,9 @@
 
 import sys
 import os
-import subprocess
 import argparse
 import signal
 
-NFT_BIN = os.getenv('NFT', "src/nft")
 TESTS_PATH = os.path.dirname(os.path.abspath(__file__))
 TESTS_DIRECTORY = ["any", "arp", "bridge", "inet", "ip", "ip6"]
 LOGFILE = "/tmp/nftables-test.log"
@@ -57,6 +55,9 @@ class Chain:
     def __eq__(self, other):
         return self.__dict__ == other.__dict__
 
+    def __str__(self):
+        return "%s" % self.name
+
 
 class Table:
     """Class that represents a table"""
@@ -69,6 +70,9 @@ class Table:
     def __eq__(self, other):
         return self.__dict__ == other.__dict__
 
+    def __str__(self):
+        return "%s %s" % (self.family, self.name)
+
 
 class Set:
     """Class that represents a set"""
@@ -133,8 +137,8 @@ def table_exist(table, filename, lineno):
     '''
     Exists a table.
     '''
-    cmd = NFT_BIN + " list -nnn table " + table.family + " " + table.name
-    ret = execute_cmd(cmd, filename, lineno)
+    cmd = "list table %s" % table
+    ret = execute_cmd(cmd, filename, lineno, numeric="all")
 
     return True if (ret == 0) else False
 
@@ -143,7 +147,7 @@ def table_flush(table, filename, lineno):
     '''
     Flush a table.
     '''
-    cmd = NFT_BIN + " flush table " + table.family + " " + table.name
+    cmd = "flush table %s" % table
     execute_cmd(cmd, filename, lineno)
 
     return cmd
@@ -162,7 +166,7 @@ def table_create(table, filename, lineno):
     table_list.append(table)
 
     # We add a new table
-    cmd = NFT_BIN + " add table " + table.family + " " + table.name
+    cmd = "add table %s" % table
     ret = execute_cmd(cmd, filename, lineno)
 
     if ret != 0:
@@ -195,15 +199,13 @@ def table_delete(table, filename=None, lineno=None):
     '''
     Deletes a table.
     '''
-    table_info = " " + table.family + " " + table.name + " "
-
     if not table_exist(table, filename, lineno):
         reason = "Table " + table.name + \
                  " does not exist but I added it before."
         print_error(reason, filename, lineno)
         return -1
 
-    cmd = NFT_BIN + " delete table" + table_info
+    cmd = "delete table %s" % table
     ret = execute_cmd(cmd, filename, lineno)
     if ret != 0:
         reason = cmd + ": " + "I cannot delete table '" + table.name + \
@@ -224,9 +226,8 @@ def chain_exist(chain, table, filename):
     '''
     Checks a chain
     '''
-    table_info = " " + table.family + " " + table.name + " "
-    cmd = NFT_BIN + " list -nnn chain" + table_info + chain.name
-    ret = execute_cmd(cmd, filename, chain.lineno)
+    cmd = "list chain %s %s" % (table, chain)
+    ret = execute_cmd(cmd, filename, chain.lineno, numeric="all")
 
     return True if (ret == 0) else False
 
@@ -235,16 +236,13 @@ def chain_create(chain, table, filename):
     '''
     Adds a chain
     '''
-    table_info = " " + table.family + " " + table.name + " "
-
     if chain_exist(chain, table, filename):
         reason = "This chain '" + chain.name + "' exists in " + table.name + \
                  ". I cannot create two chains with same name."
         print_error(reason, filename, chain.lineno)
         return -1
 
-    cmd = NFT_BIN + " add chain" + table_info + chain.name + \
-          "\{ " + chain.config + "\; \}"
+    cmd = "add chain %s %s { %s; }" % (table, chain, chain.config)
 
     ret = execute_cmd(cmd, filename, chain.lineno)
     if ret != 0:
@@ -265,22 +263,20 @@ def chain_delete(chain, table, filename=None, lineno=None):
     '''
     Flushes and deletes a chain.
     '''
-    table_info = " " + table.family + " " + table.name + " "
-
     if not chain_exist(chain, table, filename):
         reason = "The chain " + chain.name + " does not exists in " + \
                  table.name + ". I cannot delete it."
         print_error(reason, filename, lineno)
         return -1
 
-    cmd = NFT_BIN + " flush chain" + table_info + chain.name
+    cmd = "flush chain %s %s" % (table, chain)
     ret = execute_cmd(cmd, filename, lineno)
     if ret != 0:
         reason = "I cannot flush this chain " + chain.name
         print_error(reason, filename, lineno)
         return -1
 
-    cmd = NFT_BIN + " delete chain" + table_info + chain.name
+    cmd = "delete chain %s %s" % (table, chain)
     ret = execute_cmd(cmd, filename, lineno)
     if ret != 0:
         reason = cmd + "I cannot delete this chain. DD"
@@ -323,13 +319,11 @@ def set_add(s, test_result, filename, lineno):
             print_error(reason, filename, lineno)
             return -1
 
-        table_handle = " " + table.family + " " + table.name + " "
-        if s.flags == "":
-            set_cmd = " " + s.name + " { type " + s.type + "\;}"
-        else:
-            set_cmd = " " + s.name + " { type " + s.type + "\; flags " + s.flags + "\; }"
+        flags = s.flags
+        if flags != "":
+            flags = "flags %s; " % flags
 
-        cmd = NFT_BIN + " add set" + table_handle + set_cmd
+        cmd = "add set %s %s { type %s; %s}" % (table, s.name, s.type, flags)
         ret = execute_cmd(cmd, filename, lineno)
 
         if (ret == 0 and test_result == "fail") or \
@@ -365,17 +359,8 @@ def set_add_elements(set_element, set_name, state, filename, lineno):
             print_error(reason, filename, lineno)
             return -1
 
-        table_info = " " + table.family + " " + table.name + " "
-
-        element = ""
-        for e in set_element:
-            if not element:
-                element = e
-            else:
-                element = element + ", " + e
-
-        set_text = set_name + " { " + element + " }"
-        cmd = NFT_BIN + " add element" + table_info + set_text
+        element = ", ".join(set_element)
+        cmd = "add element %s %s { %s }" % (table, set_name, element)
         ret = execute_cmd(cmd, filename, lineno)
 
         if (state == "fail" and ret == 0) or (state == "ok" and ret != 0):
@@ -397,11 +382,8 @@ def set_delete_elements(set_element, set_name, table, filename=None,
     '''
     Deletes elements in a set.
     '''
-    table_info = " " + table.family + " " + table.name + " "
-
     for element in set_element:
-        set_text = set_name + " {" + element + "}"
-        cmd = NFT_BIN + " delete element" + table_info + set_text
+        cmd = "delete element %s %s { %s }" % (table, set_name, element)
         ret = execute_cmd(cmd, filename, lineno)
         if ret != 0:
             reason = "I cannot delete an element" + element + \
@@ -429,8 +411,7 @@ def set_delete(table, filename=None, lineno=None):
                             lineno)
 
         # We delete the set.
-        table_info = " " + table.family + " " + table.name + " "
-        cmd = NFT_BIN + " delete set " + table_info + " " + set_name
+        cmd = "delete set %s %s" % (table, set_name)
         ret = execute_cmd(cmd, filename, lineno)
 
         # Check if the set still exists after I deleted it.
@@ -446,9 +427,8 @@ def set_exist(set_name, table, filename, lineno):
     '''
     Check if the set exists.
     '''
-    table_info = " " + table.family + " " + table.name + " "
-    cmd = NFT_BIN + " list -nnn set" + table_info + set_name
-    ret = execute_cmd(cmd, filename, lineno)
+    cmd = "list set %s %s" % (table, set_name)
+    ret = execute_cmd(cmd, filename, lineno, numeric="all")
 
     return True if (ret == 0) else False
 
@@ -457,9 +437,8 @@ def _set_exist(s, filename, lineno):
     '''
     Check if the set exists.
     '''
-    table_handle = " " + s.family + " " + s.table + " "
-    cmd = NFT_BIN + " list -nnn set" + table_handle + s.name
-    ret = execute_cmd(cmd, filename, lineno)
+    cmd = "list set %s %s %s" % (s.family, s.table, s.name)
+    ret = execute_cmd(cmd, filename, lineno, numeric="all")
 
     return True if (ret == 0) else False
 
@@ -510,9 +489,7 @@ def obj_add(o, test_result, filename, lineno):
             print_error(reason, filename, lineno)
             return -1
 
-        table_handle = " " + table.family + " " + table.name + " "
-
-        cmd = NFT_BIN + " add " + o.type + table_handle + o.name + " " + o.spcf
+        cmd = "add %s %s %s %s" % (o.type, table, o.name, o.spcf)
         ret = execute_cmd(cmd, filename, lineno)
 
         if (ret == 0 and test_result == "fail") or \
@@ -552,8 +529,7 @@ def obj_delete(table, filename=None, lineno=None):
             return -1
 
         # We delete the object.
-        table_info = " " + table.family + " " + table.name + " "
-        cmd = NFT_BIN + " delete " + o.type + table_info + " " + o.name
+        cmd = "delete %s %s %s" % (o.type, table, o.name)
         ret = execute_cmd(cmd, filename, lineno)
 
         # Check if the object still exists after I deleted it.
@@ -569,9 +545,8 @@ def obj_exist(o, table, filename, lineno):
     '''
     Check if the object exists.
     '''
-    table_handle = " " + table.family + " " + table.name + " "
-    cmd = NFT_BIN + " list -nnn " + o.type + table_handle + o.name
-    ret = execute_cmd(cmd, filename, lineno)
+    cmd = "list %s %s %s" % (o.type, table, o.name)
+    ret = execute_cmd(cmd, filename, lineno, numeric="all")
 
     return True if (ret == 0) else False
 
@@ -580,9 +555,8 @@ def _obj_exist(o, filename, lineno):
     '''
     Check if the object exists.
     '''
-    table_handle = " " + o.family + " " + o.table + " "
-    cmd = NFT_BIN + " list -nnn " + o.type + table_handle + o.name
-    ret = execute_cmd(cmd, filename, lineno)
+    cmd = "list %s %s %s %s" % (o.type, o.family, o.table, o.name)
+    ret = execute_cmd(cmd, filename, lineno, numeric="all")
 
     return True if (ret == 0) else False
 
@@ -697,13 +671,11 @@ def rule_add(rule, filename, lineno, force_all_family_option, filename_path):
             chain = chain_get_by_name(table_chain)
             unit_tests += 1
             table_flush(table, filename, lineno)
-            table_info = " " + table.family + " " + table.name + " "
 
             payload_log = os.tmpfile()
 
-            cmd = NFT_BIN + " add rule --debug=netlink" + table_info + \
-                  chain.name + " " + rule[0]
-            ret = execute_cmd(cmd, filename, lineno, payload_log)
+            cmd = "add rule %s %s %s" % (table, chain, rule[0])
+            ret = execute_cmd(cmd, filename, lineno, payload_log, debug="netlink")
 
             state = rule[1].rstrip()
             if (ret in [0,134] and state == "fail") or (ret != 0 and state == "ok"):
@@ -740,13 +712,14 @@ def rule_add(rule, filename, lineno, force_all_family_option, filename_path):
                                   gotf.name, 1)
 
                 # Check output of nft
-                process = subprocess.Popen([NFT_BIN, '-nnns', 'list', 'table',
-                                            table.family, table.name],
-                                           shell=False,
-                                           stdout=subprocess.PIPE,
-                                           preexec_fn=preexec)
-                pre_output = process.communicate()
-                output = pre_output[0].split(";")
+                numeric_old = nftables.set_numeric_output("all")
+                stateless_old = nftables.set_stateless_output(True)
+                list_cmd = 'list table %s' % table
+                rc, pre_output, err = nftables.cmd(list_cmd)
+                nftables.set_numeric_output(numeric_old)
+                nftables.set_stateless_output(stateless_old)
+
+                output = pre_output.split(";")
                 if len(output) < 2:
                     reason = cmd + ": Listing is broken."
                     print_error(reason, filename, lineno)
@@ -755,7 +728,7 @@ def rule_add(rule, filename, lineno, force_all_family_option, filename_path):
                     if not force_all_family_option:
                         return [ret, warning, error, unit_tests]
                 else:
-                    rule_output = output_clean(pre_output[0], chain)
+                    rule_output = output_clean(pre_output, chain)
                     if len(rule) == 3:
                         teoric_exit = rule[2]
                     else:
@@ -809,7 +782,8 @@ def signal_handler(signal, frame):
     signal_received = 1
 
 
-def execute_cmd(cmd, filename, lineno, stdout_log=False):
+def execute_cmd(cmd, filename, lineno,
+                stdout_log=False, numeric=False, debug=False):
     '''
     Executes a command, checks for segfaults and returns the command exit
     code.
@@ -817,23 +791,36 @@ def execute_cmd(cmd, filename, lineno, stdout_log=False):
     :param cmd: string with the command to be executed
     :param filename: name of the file tested (used for print_error purposes)
     :param lineno: line number being tested (used for print_error purposes)
+    :param stdout_log: redirect stdout to this file instead of global log_file
+    :param numeric: turn numeric output temporarily on
+    :param debug: temporarily set these debug flags
     '''
     global log_file
     print >> log_file, "command: %s" % cmd
     if debug_option:
         print cmd
 
+    if numeric:
+        numeric_old = nftables.get_numeric_output()
+        nftables.set_numeric_output(numeric)
+    if debug:
+        debug_old = nftables.get_debug()
+        nftables.set_debug(debug)
+
+    ret, out, err = nftables.cmd(cmd)
+
     if not stdout_log:
         stdout_log = log_file
 
-    ret = subprocess.call(cmd, shell=True, universal_newlines=True,
-                          stderr=log_file, stdout=stdout_log,
-                          preexec_fn=preexec)
+    stdout_log.write(out)
+    stdout_log.flush()
+    log_file.write(err)
     log_file.flush()
 
-    if ret == -11:
-        reason = "command segfaults: " + cmd
-        print_error(reason, filename, lineno)
+    if numeric:
+        nftables.set_numeric_output(numeric_old)
+    if debug:
+        nftables.set_debug(debug_old)
 
     return ret
 
@@ -1123,10 +1110,17 @@ def main():
     # Change working directory to repository root
     os.chdir(TESTS_PATH + "/../..")
 
-    if not os.path.isfile(NFT_BIN):
-        print "The nft binary does not exist. You need to build the project."
+    sys.path.append('py/')
+    from nftables import Nftables
+
+    if not os.path.exists('src/.libs/libnftables.so'):
+        print "The nftables library does not exist. " \
+              "You need to build the project."
         return
 
+    global nftables
+    nftables = Nftables('src/.libs/libnftables.so')
+
     test_files = files_ok = run_total = 0
     tests = passed = warnings = errors = 0
     global log_file
-- 
2.16.1

--
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