python-iptables icon indicating copy to clipboard operation
python-iptables copied to clipboard

Bug - Module not working for iptables version 1.8.11-2

Open jkklemm opened this issue 1 year ago • 3 comments

If iptables version 1.8.11-2 is installed and python 3.12 or python 3.13 is used in Ubuntu or Arch Linux and and the python-iptables module of pip version 1.0.1. is installed, the following code creates the following issue:

import iptc
rule = iptc.Rule()
rule.protocol = "udp"
match = iptc.Match(rule, "udp")
match.dport = "22"

XTablesError                              Traceback (most recent call last)
Cell In[1], line 5
      3 rule.protocol = "udp"
      4 match = iptc.Match(rule, "udp")
----> 5 match.sport = "22"

File /usr/local/lib/python3.12/dist-packages/iptc/ip4tc.py:460, in IPTCModule.__setattr__(self, name, value)
    458 def __setattr__(self, name, value):
    459     if not name.startswith('_') and name not in dir(self):
--> 460         self.parse(name.replace("_", "-"), value)
    461     else:
    462         object.__setattr__(self, name, value)

File /usr/local/lib/python3.12/dist-packages/iptc/ip4tc.py:332, in IPTCModule.parse(self, parameter, value)
    328     argv[i + 1] = args[i]
    330 entry = self._rule.entry and ct.pointer(self._rule.entry) or None
--> 332 self._parse(argv, inv, entry)

File /usr/local/lib/python3.12/dist-packages/iptc/ip4tc.py:601, in Match._parse(self, argv, inv, entry)
    600 def _parse(self, argv, inv, entry):
--> 601     self._xt.parse_match(argv, inv, self._module, entry,
    602                          ct.cast(self._ptrptr, ct.POINTER(ct.c_void_p)),
    603                          self._orig_parse, self._orig_options)

File /usr/local/lib/python3.12/dist-packages/iptc/xtables.py:869, in set_nfproto.<locals>.new(*args)
    867 xtobj = args[0]
    868 xtables._xtables_set_nfproto(xtobj.proto)
--> 869 return fn(*args)

File /usr/local/lib/python3.12/dist-packages/iptc/xtables.py:1144, in xtables.parse_match(self, argv, invert, m, fw, ptr, x6_parse, x6_options)
   1142 entry = self._option_lookup(x6_options, argv[0])
   1143 if not entry:
-> 1144     raise XTablesError("%s: no such parameter %s" % (m.name,
   1145                                                      argv[0]))
   1147 cb = xt_option_call()
   1148 cb.entry = ct.pointer(entry)

XTablesError: b'udp': no such parameter b'sport'

There are other things that do not work, such as sport for match udp. I don't know why iptc doesn't work with iptables in version 1.8.11-2 anymore.

jkklemm avatar Apr 05 '25 06:04 jkklemm

I was able to reproduce this with python-iptables 1.2.0 as well.

de11n avatar Apr 29 '25 19:04 de11n

This is due to ("base", ct.c_uint) field missing from xt_option_entry structure

AbsurdlySuspicious avatar Jul 25 '25 23:07 AbsurdlySuspicious

I've tried to make a patch but I have no idea how to conditionally include this change. Major xtables version haven't been bumped (still v12), actual iptables version is not exported anywhere in iptables libs (xtables, ip4tc) as far as I can tell. Versioning this change is necessary since it obviously segfaults with new field added on iptables<=1.8.10.

Diff for struct itself if anyone needs it:

diff --git a/iptc/xtables.py b/iptc/xtables.py
index 024779c..abdd6f2 100644
--- a/iptc/xtables.py
+++ b/iptc/xtables.py
@@ -108,7 +108,8 @@ class xt_option_entry(ct.Structure):
                 ("ptroff", ct.c_uint),
                 ("size", ct.c_size_t),
                 ("min", ct.c_uint),
-                ("max", ct.c_uint)]
+                ("max", ct.c_uint),
+                ("base", ct.c_uint)]


 class _U1(ct.Union):

My solution for the moment is to vendor this lib as two submodules (current master and patched) and conditionally build one or the other depending on current iptables version in bash wrapper script:

verlte() {
    local sorted
    sorted=$(echo -e "$1\n$2" | sort -V)
    [[ $1 = "$(head -n1 <<<"$sorted")" ]]
}

iptables_version=$(iptables --version | perl -pe 's/iptables v(\S+).*/$1/')
if verlte "$iptables_version" 1.8.10; then
    iptc_path=python-iptables-master
else
    iptc_path=python-iptables-bf
fi
iptc_dst=libs/python-iptables
rm "$iptc_dst" >/dev/null || true
ln -sf "$iptc_path" "$iptc_dst"

# then include `libs/python-iptables` in your favorite pkg manager as editable package

AbsurdlySuspicious avatar Jul 26 '25 12:07 AbsurdlySuspicious