@@ -670,6 +670,16 @@ class xtables_target(ct.Union):
670670 ("v10" , _xtables_target_v10 )]
671671
672672
673+ class xtables_afinfo (ct .Structure ):
674+ _fields_ = [("kmod" , ct .c_char_p ),
675+ ("proc_exists" , ct .c_char_p ),
676+ ("libprefix" , ct .c_char_p ),
677+ ("family" , ct .c_uint8 ),
678+ ("ipproto" , ct .c_uint8 ),
679+ ("so_rev_match" , ct .c_int ),
680+ ("so_rev_target" , ct .c_int )]
681+
682+
673683class XTablesError (Exception ):
674684 """Raised when an xtables call fails for some reason."""
675685
@@ -698,6 +708,10 @@ class XTablesError(Exception):
698708_wrap_uintfn .restype = ct .c_int
699709_wrap_uintfn .argtypes = [ct .c_void_p , ct .c_uint ]
700710
711+ _wrap_voidfn = _lib_xtwrapper .wrap_voidfn
712+ _wrap_voidfn .restype = ct .c_int
713+ _wrap_voidfn .argtypes = [ct .c_void_p ]
714+
701715_wrap_x6fn = _lib_xtwrapper .wrap_x6fn
702716_wrap_x6fn .restype = ct .c_int
703717_wrap_x6fn .argtypes = [ct .c_void_p , ct .c_void_p ]
@@ -713,6 +727,20 @@ def _xt_exit(status, *args):
713727_xt_exit = _EXIT_FN (_xt_exit )
714728
715729
730+ def preserve_globals (fn ):
731+ def new (* args ):
732+ obj = args [0 ]
733+ obj ._restore_globals ()
734+ try :
735+ ret = fn (* args )
736+ except Exception :
737+ obj ._save_globals ()
738+ raise
739+ obj ._save_globals ()
740+ return ret
741+ return new
742+
743+
716744class xtables (object ):
717745 _xtables_init_all = _lib_xtables .xtables_init_all
718746 _xtables_init_all .restype = ct .c_int
@@ -726,16 +754,26 @@ class xtables(object):
726754 _xtables_find_target .restype = ct .POINTER (xtables_target )
727755 _xtables_find_target .argtypes = [ct .c_char_p , ct .c_int ]
728756
757+ _xtables_afinfo = ct .c_void_p .in_dll (_lib_xtables , "afinfo" )
758+ _xtables_xt_params = ct .c_void_p .in_dll (_lib_xtables , "xt_params" )
759+ _xtables_matches = (ct .c_void_p .in_dll (_lib_xtables , "xtables_matches" ))
760+ _xtables_pending_matches = (ct .c_void_p .in_dll (_lib_xtables ,
761+ "xtables_pending_matches" ))
762+ _xtables_targets = (ct .c_void_p .in_dll (_lib_xtables , "xtables_targets" ))
763+ _xtables_pending_targets = (ct .c_void_p .in_dll (_lib_xtables ,
764+ "xtables_pending_targets" ))
765+
729766 _cache = weakref .WeakValueDictionary ()
730767
731768 def __new__ (cls , proto ):
732769 obj = xtables ._cache .get (proto , None )
733770 if not obj :
734771 obj = object .__new__ (cls )
735772 xtables ._cache [proto ] = obj
773+ obj ._xtinit (proto )
736774 return obj
737775
738- def __init__ (self , proto ):
776+ def _xtinit (self , proto ):
739777 self .proto = proto
740778 self ._xt_globals = xtables_globals ()
741779 self ._xt_globals .option_offset = 0
@@ -744,13 +782,52 @@ def __init__(self, proto):
744782 self ._xt_globals .orig_opts = None
745783 self ._xt_globals .opts = None
746784 self ._xt_globals .exit_err = _xt_exit
785+
786+ self ._loaded_exts = []
787+
788+ # make sure we're initializing with clean state
789+ self ._afinfo = ct .c_void_p (None ).value
790+ self ._xt_params = ct .c_void_p (None ).value
791+ self ._matches = ct .c_void_p (None ).value
792+ self ._pending_matches = ct .c_void_p (None ).value
793+ self ._targets = ct .c_void_p (None ).value
794+ self ._pending_targets = ct .c_void_p (None ).value
795+
747796 rv = xtables ._xtables_init_all (ct .pointer (self ._xt_globals ), proto )
748797 if rv :
749798 raise XTablesError ("xtables_init_all() failed: %d" % (rv ))
799+ self ._save_globals ()
750800
751801 def __repr__ (self ):
752802 return "XTables for protocol %d" % (self .proto )
753803
804+ def _save_globals (self ):
805+ # Save our per-protocol libxtables global variables, and set them to
806+ # NULL so that we don't interfere with other protocols.
807+ null = ct .c_void_p (None )
808+ self ._afinfo = xtables ._xtables_afinfo .value
809+ xtables ._xtables_afinfo .value = null .value
810+ self ._xt_params = xtables ._xtables_xt_params .value
811+ xtables ._xtables_xt_params .value = null .value
812+ self ._matches = xtables ._xtables_matches .value
813+ xtables ._xtables_matches .value = null .value
814+ self ._pending_matches = xtables ._xtables_pending_matches .value
815+ xtables ._xtables_pending_matches .value = null .value
816+ self ._targets = xtables ._xtables_targets .value
817+ xtables ._xtables_targets .value = null .value
818+ self ._pending_targets = xtables ._xtables_pending_targets .value
819+ xtables ._xtables_pending_targets .value = null .value
820+
821+ def _restore_globals (self ):
822+ # Restore per-protocol libxtables global variables saved in
823+ # _save_globals().
824+ xtables ._xtables_afinfo .value = self ._afinfo
825+ xtables ._xtables_xt_params .value = self ._xt_params
826+ xtables ._xtables_matches .value = self ._matches
827+ xtables ._xtables_pending_matches .value = self ._pending_matches
828+ xtables ._xtables_targets .value = self ._targets
829+ xtables ._xtables_pending_targets .value = self ._pending_targets
830+
754831 @classmethod
755832 def _get_xtables_version (cls , version ):
756833 version_match = re .match ("libxtables.so.(\d+)" , version )
@@ -760,10 +837,62 @@ def _get_xtables_version(cls, version):
760837 else :
761838 raise RuntimeError ("Xtables returned unknown version format" )
762839
840+ def _check_extname (self , name ):
841+ if name in ["" , "ACCEPT" , "DROP" , "QUEUE" , "RETURN" ]:
842+ name = "standard"
843+ return name
844+
845+ def _loaded (self , name ):
846+ self ._loaded_exts .append (name )
847+
848+ def _is_loaded (self , name ):
849+ if name in self ._loaded_exts :
850+ return True
851+ else :
852+ return False
853+
854+ def _get_initfn_from_lib (self , name , lib ):
855+ try :
856+ initfn = getattr (lib , "libxt_%s_init" % (name ))
857+ except AttributeError :
858+ afinfo = ct .cast (self ._afinfo , ct .POINTER (xtables_afinfo ))
859+ prefix = afinfo [0 ].libprefix
860+ initfn = getattr (lib , "%s%s_init" % (prefix , name ), None )
861+ return initfn
862+
863+ def _try_extinit (self , name , lib ):
864+ try :
865+ if type (lib ) != ct .CDLL :
866+ lib = ct .CDLL (lib )
867+ fn = self ._get_initfn_from_lib (name , lib )
868+ if fn :
869+ _wrap_voidfn (fn )
870+ return True
871+ except :
872+ pass
873+ return False
874+
875+ def _try_register (self , name ):
876+ if self ._try_extinit (name , _lib_xtables ):
877+ return
878+ afinfo = ct .cast (self ._afinfo , ct .POINTER (xtables_afinfo ))
879+ prefix = afinfo [0 ].libprefix
880+ libs = ["/lib/xtables/libxt_" + name + ".so" ,
881+ "/lib/xtables/" + prefix + name + ".so" ]
882+ for lib in libs :
883+ if self ._try_extinit (name , lib ):
884+ return
885+
886+ @preserve_globals
763887 def find_match (self , name ):
888+ name = self ._check_extname (name )
764889 match = xtables ._xtables_find_match (name , XTF_TRY_LOAD , None )
765890 if not match :
766- return match
891+ self ._try_register (name )
892+ match = xtables ._xtables_find_match (name , XTF_TRY_LOAD , None )
893+ if not match :
894+ return match
895+ self ._loaded (name )
767896 version = xtables ._get_xtables_version (match .contents .v1 .version )
768897
769898 if 1 == version :
@@ -785,10 +914,16 @@ def find_match(self, name):
785914 else :
786915 raise Exception ("Match object casting failed" )
787916
917+ @preserve_globals
788918 def find_target (self , name ):
919+ name = self ._check_extname (name )
789920 target = xtables ._xtables_find_target (name , XTF_TRY_LOAD )
790921 if not target :
791- return target
922+ self ._try_register (name )
923+ target = xtables ._xtables_find_target (name , XTF_TRY_LOAD )
924+ if not target :
925+ return target
926+ self ._loaded (name )
792927 version = xtables ._get_xtables_version (target .contents .v1 .version )
793928
794929 if 1 == version :
@@ -810,6 +945,7 @@ def find_target(self, name):
810945 else :
811946 raise Exception ("Target object casting failed" )
812947
948+ @preserve_globals
813949 def save (self , module , ip , ptr ):
814950 _wrap_save (module .save , ct .cast (ct .pointer (ip ), ct .c_void_p ), ptr )
815951
@@ -835,6 +971,7 @@ def _parse(self, module, argv, inv, flags, entry, ptr):
835971
836972 # Dispatch arguments to the appropriate parse function, based upon the
837973 # extension's choice of API.
974+ @preserve_globals
838975 def parse_target (self , argv , invert , t , fw , ptr ):
839976 _optarg .value = argv [1 ]
840977 _optind .value = 2
@@ -879,6 +1016,7 @@ def parse_target(self, argv, invert, t, fw, ptr):
8791016
8801017 # Dispatch arguments to the appropriate parse function, based upon the
8811018 # extension's choice of API.
1019+ @preserve_globals
8821020 def parse_match (self , argv , invert , m , fw , ptr ):
8831021 _optarg .value = argv [1 ]
8841022 _optind .value = 2
@@ -936,6 +1074,7 @@ def _options_fcheck(self, name, xflags, table):
9361074
9371075 # Dispatch arguments to the appropriate final_check function, based upon
9381076 # the extension's choice of API.
1077+ @preserve_globals
9391078 def final_check_target (self , target ):
9401079 x6_fcheck = None
9411080 try :
@@ -973,6 +1112,7 @@ def final_check_target(self, target):
9731112
9741113 # Dispatch arguments to the appropriate final_check function, based upon
9751114 # the extension's choice of API.
1115+ @preserve_globals
9761116 def final_check_match (self , match ):
9771117 x6_fcheck = None
9781118 try :
0 commit comments