Blob Blame History Raw
commit 6b9867cd5c5e2c83adeec42666521a420e59ef11
Author: Thomas Woerner <twoerner@redhat.com>
Date:   Fri Sep 26 17:11:11 2014 +0200

    New runtimeToPermanent and tracked passsthrough support
    
    - New D-Bus method DBUS_INTERFACE.runtimeToPermanent
      Copyies all needed files from the runtime into the permanent environmant
    - New RT_TO_PERM_FAILED error used in case of runtime to permanent failure
    - New D-Bus method DBUS_INTERFACE.runtimeToPermanent
    - New firewall-cmd option --runtime-to-permanent
    - Enabled support for tracked runtime passthrough versus untracked simple
      passthrough (runtime --passthrough option):
      This is needed to have tracked passthrough settings after a runtime to
      permanent call.
      - New D-Bus methods for tracked passthrough in DBUS_INTERFACE_DIRECT:
        addPassthrough, removePassthrough, queryPassthrough, getAllPassthroughs,
        setAllPassthroughs, removeAllPassthroughs,
      - New D-Bus signals for tracked passthrough in DBUS_INTERFACE_DIRECT:
        PassthroughAdded, PassthroughRemoved
      - Support for new D-Bus methods and signals in client.py
      - Enabled runtime options in firewall-cmd for
        --get-[all-]passthroughs
        --{add|remove|query}-passthrough
      - Enabled tracked runtime passthrough settings in firewall-config
    - Using list types for rule args in fw_direct to enable runtime-to-permanent
      data usage
    - Fixed or added string conversions for direct rules and passthrough, also for
      lockdown_whitelist commands, contexts and users
    - Fixed bash_completion script
    - firewall-cmd man page: Added unracked comment for runtime --passthrough
    - firewalld.dbus man page: Added new D-Bus methods and signals

commit 735fa38bd9e49945656dd8b44ee60bb962196b6d
Author: Thomas Woerner <twoerner@redhat.com>
Date:   Mon Sep 29 15:57:59 2014 +0200

    Fixed runtimeToPermanent to use DBUS_INTERFACE.get{Service,IcmpType,Zone}Set
tings()
    
    The use of DBUS_INTERFACE.getZoneSettings() is needed to be able to pass the
    identical check in the runtimeToPermanent call.

diff -up firewalld-0.3.9/doc/xml/firewall-cmd.xml.RHBZ#993650 firewalld-0.3.9/doc/xml/firewall-cmd.xml
--- firewalld-0.3.9/doc/xml/firewall-cmd.xml.RHBZ#993650	2014-09-29 22:40:23.976109890 +0200
+++ firewalld-0.3.9/doc/xml/firewall-cmd.xml	2014-09-29 22:41:16.188349186 +0200
@@ -915,21 +915,21 @@
 	  </listitem>
 	</varlistentry>
 
-	<!-- direct passthrough -->
+	<!-- direct untracked passthrough -->
 
 	<varlistentry>
 	  <term><option>--direct</option> <option>--passthrough</option> { <literal>ipv4</literal> | <literal>ipv6</literal> | <literal>eb</literal> } <replaceable>args</replaceable></term>
 	  <listitem>
 	    <para>
-	      Pass a command through to the firewall. <replaceable>args</replaceable> can be all <command>iptables</command>, <command>ip6tables</command> and <command>ebtables</command> command line arguments.
+	      Pass a command through to the firewall. <replaceable>args</replaceable> can be all <command>iptables</command>, <command>ip6tables</command> and <command>ebtables</command> command line arguments. This command is untracked, which means that firewalld is not able to provide information about this command later on, also not a listing of the untracked passthoughs.
 	    </para>
 	  </listitem>
 	</varlistentry>
 
-	<!-- direct permanent passthrough -->
+	<!-- direct tracked passthrough -->
 
 	<varlistentry>
-	  <term><option>--permanent</option> <option>--direct</option> <option>--get-all-passthroughs</option></term>
+	  <term><optional><option>--permanent</option></optional> <option>--direct</option> <option>--get-all-passthroughs</option></term>
 	  <listitem>
 	    <para>
 	      Get all permanent passthrough as a newline separated list of the ipv value and arguments.
@@ -938,7 +938,7 @@
 	</varlistentry>
 
 	<varlistentry>
-	  <term><option>--permanent</option> <option>--direct</option> <option>--get-passthroughs</option> { <literal>ipv4</literal> | <literal>ipv6</literal> | <literal>eb</literal> } </term>
+	  <term><optional><option>--permanent</option></optional> <option>--direct</option> <option>--get-passthroughs</option> { <literal>ipv4</literal> | <literal>ipv6</literal> | <literal>eb</literal> } </term>
 	  <listitem>
 	    <para>
 	      Get all permanent passthrough rules for the ipv value as a newline separated list of the priority and arguments.
@@ -947,7 +947,7 @@
 	</varlistentry>
 
 	<varlistentry>
-	  <term><option>--permanent</option> <option>--direct</option> <option>--add-passthrough</option> { <literal>ipv4</literal> | <literal>ipv6</literal> | <literal>eb</literal> } <replaceable>args</replaceable></term>
+	  <term><optional><option>--permanent</option></optional> <option>--direct</option> <option>--add-passthrough</option> { <literal>ipv4</literal> | <literal>ipv6</literal> | <literal>eb</literal> } <replaceable>args</replaceable></term>
 	  <listitem>
 	    <para>
 	      Add a permanent passthrough rule with the arguments <replaceable>args</replaceable> for the ipv value.
@@ -956,7 +956,7 @@
 	</varlistentry>
 
 	<varlistentry>
-	  <term><option>--permanent</option> <option>--direct</option> <option>--remove-passthrough</option> { <literal>ipv4</literal> | <literal>ipv6</literal> | <literal>eb</literal> } <replaceable>args</replaceable></term>
+	  <term><optional><option>--permanent</option></optional> <option>--direct</option> <option>--remove-passthrough</option> { <literal>ipv4</literal> | <literal>ipv6</literal> | <literal>eb</literal> } <replaceable>args</replaceable></term>
 	  <listitem>
 	    <para>
 	      Remove a permanent passthrough rule with the arguments <replaceable>args</replaceable> for the ipv value.
@@ -965,7 +965,7 @@
 	</varlistentry>
 
 	<varlistentry>
-	  <term><option>--permanent</option> <option>--direct</option> <option>--query-passthrough</option> { <literal>ipv4</literal> | <literal>ipv6</literal> | <literal>eb</literal> } <replaceable>args</replaceable></term>
+	  <term><optional><option>--permanent</option></optional> <option>--direct</option> <option>--query-passthrough</option> { <literal>ipv4</literal> | <literal>ipv6</literal> | <literal>eb</literal> } <replaceable>args</replaceable></term>
 	  <listitem>
 	    <para>
 	      Return whether a permanent passthrough rule with the arguments <replaceable>args</replaceable> exists for the ipv value. Returns 0 if true, 1 otherwise.
diff -up firewalld-0.3.9/doc/xml/firewalld.dbus.xml.RHBZ#993650 firewalld-0.3.9/doc/xml/firewalld.dbus.xml
--- firewalld-0.3.9/doc/xml/firewalld.dbus.xml.RHBZ#993650	2014-09-29 22:40:23.972109870 +0200
+++ firewalld-0.3.9/doc/xml/firewalld.dbus.xml	2014-09-29 22:46:02.838503889 +0200
@@ -292,6 +292,17 @@
               </para>
             </listitem>
           </varlistentry>
+          <varlistentry id="FirewallD1.Methods.runtimeToPermanent">
+            <term><methodname>runtimeToPermanent</methodname>() &rarr; Nothing</term>
+            <listitem>
+              <para>
+		Make runtime settings permanent. Replaces permanent settings with runtime settings for zones, services, icmptypes, direct and policies (lockdown whitelist).
+              </para>
+	      <para>
+		Possible errors: RT_TO_PERM_FAILED
+	      </para>
+            </listitem>
+          </varlistentry>
         </variablelist>
       </refsect3>
     </refsect2>
@@ -313,6 +324,17 @@
               </para>
             </listitem>
           </varlistentry>
+          <varlistentry id="FirewallD1.direct.Methods.addPasstrough">
+            <term><methodname>addPassthrough</methodname>(s: ipv, as: args) &rarr; Nothing</term>
+            <listitem>
+              <para>
+		Add a tracked passthrough rule with the arguments <replaceable>args</replaceable> for <replaceable>ipv</replaceable> being either <literal>ipv4</literal> (iptables) or <literal>ipv6</literal> (ip6tables) or <literal>eb</literal> (ebtables).
+              </para>
+	      <para>
+		Possible errors: INVALID_IPV, ALREADY_ENABLED, COMMAND_FAILED
+	      </para>
+            </listitem>
+          </varlistentry>
           <varlistentry>
             <term>addRule(s: ipv, s: table, s: chain, i: priority, as: args) &rarr; Nothing</term>
             <listitem>
@@ -329,6 +351,22 @@
               </para>
             </listitem>
           </varlistentry>
+          <varlistentry id="FirewallD1.direct.Methods.getAllPassthroughs">
+            <term><methodname>getAllPassthroughs</methodname>() &rarr; a(sas)</term>
+            <listitem>
+              <para>
+		Get all tracked passthrough rules added in all ipv types in format: ipv, rule.
+		This concerns only rules previously added with <methodname>addPassthrough</methodname>.
+		Return value is a array of (<parameter>ipv</parameter>, array of <parameter>arguments</parameter>).
+              </para>
+              <para>
+                <variablelist>
+                  <varlistentry><term><parameter>ipv (s)</parameter>: either <literal>ipv4</literal> (iptables) or <literal>ipv6</literal> (ip6tables) or <literal>eb</literal> (ebtables).</term></varlistentry>
+		  <varlistentry><term><parameter>arguments (as)</parameter>: array of commands, parameters and other iptables/ip6tables/ebtables command line options.</term></varlistentry>
+                </variablelist>
+              </para>
+            </listitem>
+          </varlistentry>
           <varlistentry>
             <term>getAllRules() &rarr; a(sssias)</term>
             <listitem>
@@ -358,6 +396,7 @@
             <listitem>
               <para>
 		Pass a command through to the firewall. <replaceable>ipv</replaceable> can be either <literal>ipv4</literal> or <literal>ipv6</literal> or <literal>eb</literal>. <replaceable>args</replaceable> can be all <command>iptables</command>, <command>ip6tables</command> and <command>ebtables</command> command line arguments.
+		This command is untracked, which means that firewalld is not able to provide information about this command later on.
               </para>
             </listitem>
           </varlistentry>
@@ -369,6 +408,18 @@
               </para>
             </listitem>
           </varlistentry>
+          <varlistentry id="FirewallD1.direct.Methods.queryPassthrough">
+            <term><methodname>queryPassthrough</methodname>(s: ipv, as: args) &rarr; b</term>
+            <listitem>
+              <para>
+		Return whether a tracked passthrough rule with the arguments <replaceable>args</replaceable> exists for <replaceable>ipv</replaceable> being either <literal>ipv4</literal> (iptables) or <literal>ipv6</literal> (ip6tables) or <literal>eb</literal> (ebtables).
+		This concerns only rules previously added with <methodname>addPassthrough</methodname>.
+              </para>
+	      <para>
+		Possible errors: INVALID_IPV
+	      </para>
+            </listitem>
+          </varlistentry>
           <varlistentry>
             <term>queryRule(s: ipv, s: table, s: chain, i: priority, as: args) &rarr; b</term>
             <listitem>
@@ -385,6 +436,18 @@
               </para>
             </listitem>
           </varlistentry>
+          <varlistentry id="FirewallD1.direct.Methods.removePassthrough">
+            <term><methodname>removePassthrough</methodname>(s: ipv, as: args) &rarr; Nothing</term>
+            <listitem>
+              <para>
+		Remove a tracked passthrough rule with arguments <replaceable>args</replaceable> for <replaceable>ipv</replaceable> being either <literal>ipv4</literal> (iptables) or <literal>ipv6</literal> (ip6tables) or <literal>eb</literal> (ebtables).
+		Only rules previously added with <methodname>addPassthrough</methodname> can be removed this way.
+              </para>
+	      <para>
+		Possible errors: INVALID_IPV, NOT_ENABLED, COMMAND_FAILED
+	      </para>
+            </listitem>
+          </varlistentry>
           <varlistentry>
             <term>removeRule(s: ipv, s: table, s: chain, i: priority, as: args) &rarr; Nothing</term>
             <listitem>
@@ -423,6 +486,22 @@
               </para>
             </listitem>
           </varlistentry>
+          <varlistentry id="FirewallD1.direct.Signals.PassthroughAdded">
+            <term>PassthroughAdded(s: ipv, as: args)</term>
+            <listitem>
+              <para>
+		Emitted when a tracked passthruogh rule with <replaceable>args</replaceable> has been added for <replaceable>ipv</replaceable> being either <literal>ipv4</literal> (iptables) or <literal>ipv6</literal> (ip6tables) or <literal>eb</literal> (ebtables).
+              </para>
+            </listitem>
+          </varlistentry>
+          <varlistentry id="FirewallD1.direct.Signals.PassthroughRemoved">
+            <term>PassthroughRemoved(s: ipv, as: args)</term>
+            <listitem>
+              <para>
+		Emitted when a tracked passthrough rule with <replaceable>args</replaceable> has been removed for <replaceable>ipv</replaceable> being either <literal>ipv4</literal> (iptables) or <literal>ipv6</literal> (ip6tables) or <literal>eb</literal> (ebtables).
+              </para>
+            </listitem>
+          </varlistentry>
           <varlistentry>
             <term>RuleAdded(s: ipv, s: table, s: chain, i: priority, as: args)</term>
             <listitem>
diff -up firewalld-0.3.9/shell-completion/bash/firewall-cmd.RHBZ#993650 firewalld-0.3.9/shell-completion/bash/firewall-cmd
--- firewalld-0.3.9/shell-completion/bash/firewall-cmd.RHBZ#993650	2013-12-03 14:59:48.000000000 +0100
+++ firewalld-0.3.9/shell-completion/bash/firewall-cmd	2014-09-29 22:41:16.191349178 +0200
@@ -60,10 +60,9 @@ OPTIONS_PERMANENT="${OPTIONS_CONFIG} --z
 
 OPTIONS_DIRECT="--passthrough \
         --add-chain --remove-chain --query-chain --get-chains --get-all-chains \
-         --add-rule --remove-rule  --query-rule  --get-rules  --get-all-rules"
-
-OPTIONS_DIRECT_PERMANENT="--add-passthrough --remove-passthrough \
-                  --query-passthrough --get-passthroughs --get-all-passthroughs"
+         --add-rule --remove-rule  --query-rule  --get-rules  --get-all-rules \
+         --add-passthrough --remove-passthrough \
+         --query-passthrough --get-passthroughs --get-all-passthroughs"
 
 # these all can be used as a "first" option
 OPTIONS_GENERAL="--help --version \
@@ -108,11 +107,11 @@ _firewall_cmd()
         _available_interfaces
         ;;
     --permanent)
-        [[ ${words[@]} == *--direct* ]] && opts="${OPTIONS_DIRECT} ${OPTIONS_DIRECT_PERMANENT}" || opts="${OPTIONS_PERMANENT} --direct"
+        [[ ${words[@]} == *--direct* ]] && opts="${OPTIONS_DIRECT}" || opts="${OPTIONS_PERMANENT} --direct"
         COMPREPLY=( $( compgen -W "${opts}" -- "$cur" ) )
         ;;
     --direct)
-        [[ ${words[@]} == *--permanent* ]] && opts="${OPTIONS_DIRECT} ${OPTIONS_DIRECT_PERMANENT}" || opts="${OPTIONS_DIRECT} --permanent"
+        [[ ${words[@]} == *--permanent* ]] && opts="${OPTIONS_DIRECT}" || opts="${OPTIONS_DIRECT} --permanent"
         COMPREPLY=( $( compgen -W "${opts}" -- "$cur" ) )
         ;;
     --passthrough|--*-chain|--get-chains|--*-rule|--get-rules)
diff -up firewalld-0.3.9/src/firewall/client.py.RHBZ#993650 firewalld-0.3.9/src/firewall/client.py
--- firewalld-0.3.9/src/firewall/client.py.RHBZ#993650	2014-09-29 22:40:23.973109875 +0200
+++ firewalld-0.3.9/src/firewall/client.py	2014-09-29 22:41:16.196349207 +0200
@@ -764,13 +764,16 @@ class FirewallClientDirect(object):
     def getAllPassthroughs(self):
         return self.settings[2]
     @handle_exceptions
+    def setAllPassthroughs(self, passthroughs):
+        self.settings[2] = passthroughs
+    @handle_exceptions
+    def removeAllPassthroughs(self):
+        self.settings[2] = passthroughs
+    @handle_exceptions
     def getPassthroughs(self, ipv):
         return [ entry[1] for entry in self.settings[2] \
                  if entry[0] == ipv ]
     @handle_exceptions
-    def setAllPassthroughs(self, passthroughs):
-        self.settings[2] = passthroughs
-    @handle_exceptions
     def addPassthrough(self, ipv, args):
         idx = (ipv, args)
         if idx not in self.settings[2]:
@@ -1005,6 +1008,8 @@ class FirewallClient(object):
             "direct:chain-removed": "ChainRemoved",
             "direct:rule-added": "RuleAdded",
             "direct:rule-removed": "RuleRemoved",
+            "direct:passthrough-added": "PassthroughAdded",
+            "direct:passthrough-removed": "PassthroughRemoved",
             "config:direct:updated": "config:direct:Updated",
             # policy callbacks
             "lockdown-enabled": "LockdownEnabled",
@@ -1195,6 +1200,11 @@ class FirewallClient(object):
 
     @slip.dbus.polkit.enable_proxy
     @handle_exceptions
+    def runtimeToPermanent(self):
+        self.fw.runtimeToPermanent()
+
+    @slip.dbus.polkit.enable_proxy
+    @handle_exceptions
     def get_property(self, prop):
         return dbus_to_python(self.fw_properties.Get(DBUS_INTERFACE, prop))
 
@@ -1566,6 +1576,38 @@ class FirewallClient(object):
     def passthrough(self, ipv, args):
         return dbus_to_python(self.fw_direct.passthrough(ipv, args))
 
+    # tracked passthrough
+
+    @slip.dbus.polkit.enable_proxy
+    @handle_exceptions
+    def getAllPassthroughs(self):
+        return dbus_to_python(self.fw_direct.getAllPassthroughs())
+
+    @slip.dbus.polkit.enable_proxy
+    @handle_exceptions
+    def removeAllPassthroughs(self):
+        self.fw_direct.removeAllPassthroughs()
+
+    @slip.dbus.polkit.enable_proxy
+    @handle_exceptions
+    def getPassthroughs(self, ipv):
+        return dbus_to_python(self.fw_direct.getPassthroughs(ipv))
+
+    @slip.dbus.polkit.enable_proxy
+    @handle_exceptions
+    def addPassthrough(self, ipv, args):
+        self.fw_direct.addPassthrough(ipv, args)
+
+    @slip.dbus.polkit.enable_proxy
+    @handle_exceptions
+    def removePassthrough(self, ipv, args):
+        self.fw_direct.removePassthrough(ipv, args)
+
+    @slip.dbus.polkit.enable_proxy
+    @handle_exceptions
+    def queryPassthrough(self, ipv, args):
+        return dbus_to_python(self.fw_direct.queryPassthrough(ipv, args))
+
     # lockdown
 
     @slip.dbus.polkit.enable_proxy
diff -up firewalld-0.3.9/src/firewall-cmd.RHBZ#993650 firewalld-0.3.9/src/firewall-cmd
--- firewalld-0.3.9/src/firewall-cmd.RHBZ#993650	2014-09-29 22:40:24.039110186 +0200
+++ firewalld-0.3.9/src/firewall-cmd	2014-09-29 22:49:38.406206387 +0200
@@ -68,6 +68,8 @@ Status Options
   --state              Return and print firewalld state
   --reload             Reload firewall and keep state information
   --complete-reload    Reload firewall and loose state information
+  --runtime-to-permanent
+                       Create permanent from runtime configuration
 
 Permanent Options
   --permanent          Set an option permanently
@@ -204,18 +206,18 @@ Direct Options
                        Return whether a rule with priority has been added to
                        chain in table [P]
   --passthrough {ipv4|ipv6|eb} <arg>...
-                       Pass a command through
+                       Pass a command through (untracked by firewalld)
   --get-all-passthroughs
-                       Get all passthrough rules [P only]
+                       Get all tracked passthrough rules [P]
   --get-passthroughs {ipv4|ipv6|eb} <arg>...
-                       Get passthrough rules [P only]
+                       Get tracked passthrough rules [P]
   --add-passthrough {ipv4|ipv6|eb} <arg>...
-                       Add a new passthrough rule [P only]
+                       Add a new tracked passthrough rule [P]
   --remove-passthrough {ipv4|ipv6|eb} <arg>...
-                       Remove a passthrough rule [P only]
+                       Remove a tracked passthrough rule [P]
   --query-passthrough {ipv4|ipv6|eb} <arg>...
-                       Return whether the passthrough rule has been added
-                       [P only]
+                       Return whether the tracked passthrough rule has been
+                       added [P]
 
 Lockdown Options
   --lockdown-on        Enable lockdown.
@@ -373,6 +375,8 @@ parser_group_standalone.add_argument("-V
 parser_group_standalone.add_argument("--state", action="store_true")
 parser_group_standalone.add_argument("--reload", action="store_true")
 parser_group_standalone.add_argument("--complete-reload", action="store_true")
+parser_group_standalone.add_argument("--runtime-to-permanent",
+                                     action="store_true")
 parser_group_standalone.add_argument("--panic-on", action="store_true")
 parser_group_standalone.add_argument("--panic-off", action="store_true")
 parser_group_standalone.add_argument("--query-panic", action="store_true")
@@ -524,7 +528,7 @@ a = parser.parse_args(args)
 
 
 options_standalone = a.help or a.version or \
-    a.state or a.reload or a.complete_reload or \
+    a.state or a.reload or a.complete_reload or a.runtime_to_permanent or \
     a.panic_on or a.panic_off or a.query_panic or \
     a.lockdown_on or a.lockdown_off or a.query_lockdown or \
     a.get_default_zone or a.set_default_zone or \
@@ -582,9 +586,7 @@ options_direct = a.passthrough or \
            a.add_chain or a.remove_chain or a.query_chain or \
            a.get_chains or a.get_all_chains or \
            a.add_rule or a.remove_rule or a.remove_rules or a.query_rule or \
-           a.get_rules or a.get_all_rules
-
-options_direct_permanent = \
+           a.get_rules or a.get_all_rules or \
            a.add_passthrough or a.remove_passthrough or a.query_passthrough or \
            a.get_passthroughs or a.get_all_passthroughs
 
@@ -601,30 +603,24 @@ options_list_get = a.help or a.version o
 # Check various impossible combinations of options
 
 if not (options_standalone or options_zone or \
-        options_permanent or options_direct or options_direct_permanent or \
+        options_permanent or options_direct or \
         options_permanent_only):
     __fail(parser.format_usage() + "No option specified.")
 
 if options_standalone and (options_zone or options_permanent or \
-                           options_direct or options_direct_permanent or \
-                           options_permanent_only):
+                           options_direct or options_permanent_only):
     __fail(parser.format_usage() +
            "Can't use stand-alone options with other options.")
 
-if (options_direct or options_direct_permanent or options_permanent_only) and \
-   (options_zone):
+if (options_direct or options_permanent_only) and (options_zone):
     __fail(parser.format_usage() +
            "Can't use 'direct' options with other options.")
 
-if (a.direct and not (options_direct or options_direct_permanent)) or \
-        ((options_direct or options_direct_permanent) and not a.direct):
+if (a.direct and not options_direct) or \
+        (options_direct and not a.direct):
     __fail(parser.format_usage() +
            "Wrong usage of 'direct' options.")
 
-if options_direct_permanent and not a.permanent:
-    __fail(parser.format_usage() +
-           "Option can be used only with --permanent.")
-
 if options_permanent_only and not a.permanent:
     __fail(parser.format_usage() +
            "Option can be used only with --permanent.")
@@ -774,7 +770,7 @@ if a.permanent:
         # apply whitelist changes
         fw.config().policies().setLockdownWhitelist(whitelist)
 
-    elif options_direct or options_direct_permanent:
+    elif options_direct:
         settings = fw.config().direct().getSettings()
 
         if a.passthrough:
@@ -1053,6 +1049,8 @@ elif a.reload:
     fw.reload()
 elif a.complete_reload:
     fw.complete_reload()
+elif a.runtime_to_permanent:
+    fw.runtimeToPermanent()
 elif a.direct:
     if a.passthrough:
         if len (a.passthrough) < 2:
@@ -1060,6 +1058,32 @@ elif a.direct:
         msg = fw.passthrough(_check_ipv(a.passthrough[0]), splitArgs(a.passthrough[1]))
         if msg:
             print(msg)
+
+    elif a.add_passthrough:
+        if len (a.add_passthrough) < 2:
+            __fail("usage: --direct --add-passthrough { ipv4 | ipv6 | eb } <args>")
+        fw.addPassthrough(_check_ipv(a.add_passthrough[0]),
+                          splitArgs(a.add_passthrough[1]))
+    elif a.remove_passthrough:
+        if len (a.remove_passthrough) < 2:
+            __fail("usage: --direct --remove-passthrough { ipv4 | ipv6 | eb } <args>")
+        fw.removePassthrough(_check_ipv(a.remove_passthrough[0]),
+                             splitArgs(a.remove_passthrough[1]))
+    elif a.query_passthrough:
+        if len (a.query_passthrough) < 2:
+            __fail("usage: --direct --query-passthrough { ipv4 | ipv6 | eb } <args>")
+        __print_query_result(
+            fw.queryPassthrough(_check_ipv(a.query_passthrough[0]),
+                                splitArgs(a.query_passthrough[1])))
+    elif a.get_passthroughs:
+        rules = fw.getPassthroughs(_check_ipv(a.get_passthroughs[0]))
+        for rule in rules:
+            __print(joinArgs(rule))
+        sys.exit(0)
+    elif a.get_all_passthroughs:
+        for (ipv,rule) in fw.getAllPassthroughs():
+            __print("%s %s" % (ipv, joinArgs(rule)))
+        sys.exit(0)
     elif a.add_chain:
         fw.addChain(_check_ipv(a.add_chain[0]), a.add_chain[1], a.add_chain[2])
     elif a.remove_chain:
diff -up firewalld-0.3.9/src/firewall-config.glade.RHBZ#993650 firewalld-0.3.9/src/firewall-config.glade
--- firewalld-0.3.9/src/firewall-config.glade.RHBZ#993650	2014-09-29 22:40:23.986109937 +0200
+++ firewalld-0.3.9/src/firewall-config.glade	2014-09-29 22:41:16.195349202 +0200
@@ -2115,6 +2115,22 @@
                         <property name="use_underline">True</property>
                       </object>
                     </child>
+                    <child>
+                      <object class="GtkSeparatorMenuItem" id="separatormenuitem2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkMenuItem" id="menuitem5">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="tooltip_text" translatable="yes">Make runtime configuration permanent</property>
+                        <property name="label" translatable="yes">Runtime To permant</property>
+                        <property name="use_underline">True</property>
+                        <signal name="activate" handler="onRuntimeToPermanent" swapped="no"/>
+                      </object>
+                    </child>
                   </object>
                 </child>
               </object>
@@ -3928,7 +3944,7 @@
                                                 <property name="can_focus">False</property>
                                                 <property name="yalign">0.49000000953674316</property>
                                                 <property name="stock">gtk-network</property>
-                                                <property name="icon_size">1</property>
+                                                <property name="icon-size">1</property>
                                               </object>
                                               <packing>
                                                 <property name="expand">False</property>
@@ -3988,7 +4004,7 @@
                                                 <property name="visible">True</property>
                                                 <property name="can_focus">False</property>
                                                 <property name="stock">gtk-network</property>
-                                                <property name="icon_size">1</property>
+                                                <property name="icon-size">1</property>
                                               </object>
                                               <packing>
                                                 <property name="expand">False</property>
@@ -4718,21 +4734,6 @@
                                   </packing>
                                 </child>
                                 <child>
-                                  <object class="GtkLabel" id="label97">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="xalign">0</property>
-                                    <property name="yalign">0</property>
-                                    <property name="label" translatable="yes">Passthrough rules can only be modified for the permanent configuration.</property>
-                                    <property name="wrap">True</property>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">False</property>
-                                    <property name="position">2</property>
-                                  </packing>
-                                </child>
-                                <child>
                                   <object class="GtkLabel" id="label88">
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
@@ -4744,7 +4745,7 @@
                                   <packing>
                                     <property name="expand">False</property>
                                     <property name="fill">False</property>
-                                    <property name="position">3</property>
+                                    <property name="position">2</property>
                                   </packing>
                                 </child>
                                 <child>
@@ -4780,7 +4781,7 @@
                                   <packing>
                                     <property name="expand">True</property>
                                     <property name="fill">True</property>
-                                    <property name="position">4</property>
+                                    <property name="position">3</property>
                                   </packing>
                                 </child>
                                 <child>
@@ -4849,7 +4850,7 @@
                                   <packing>
                                     <property name="expand">False</property>
                                     <property name="fill">True</property>
-                                    <property name="position">5</property>
+                                    <property name="position">4</property>
                                   </packing>
                                 </child>
                               </object>
@@ -6690,7 +6691,7 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="stock">gtk-network</property>
-                                    <property name="icon_size">1</property>
+                                    <property name="icon-size">1</property>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
@@ -7322,7 +7323,7 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="stock">gtk-properties</property>
-                                    <property name="icon_size">1</property>
+                                    <property name="icon-size">1</property>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
diff -up firewalld-0.3.9/src/firewall-config.RHBZ#993650 firewalld-0.3.9/src/firewall-config
--- firewalld-0.3.9/src/firewall-config.RHBZ#993650	2014-09-29 22:40:24.043110205 +0200
+++ firewalld-0.3.9/src/firewall-config	2014-09-29 22:41:16.193349185 +0200
@@ -912,6 +912,8 @@ class FirewallConfig(object):
         self.fw.connect("direct:chain-removed", self.direct_chain_removed_cb)
         self.fw.connect("direct:rule-added", self.direct_rule_added_cb)
         self.fw.connect("direct:rule-removed", self.direct_rule_removed_cb)
+        self.fw.connect("direct:passthrough-added", self.direct_passthrough_added_cb)
+        self.fw.connect("direct:passthrough-removed", self.direct_passthrough_removed_cb)
         self.fw.connect("config:direct:updated", self.direct_updated_cb)
 
         self.fw.connect("config:zone-added", self.conf_zone_added_cb)
@@ -1737,7 +1739,6 @@ class FirewallConfig(object):
             self.serviceConfPortBox.show()
             self.serviceConfModuleBox.show()
             self.icmpDialogIcmpEditBox.show()
-        self.directPassthroughBox.set_sensitive(not self.runtime_view)
 
         self.load_zones()
         self.load_services()
@@ -1891,6 +1892,9 @@ class FirewallConfig(object):
                 self.default_zone = new_default_zone
                 self.changes_applied()
 
+    def onRuntimeToPermanent(self, *args):
+        self.fw.runtimeToPermanent()
+
     def on_defaultZoneViewSelection_changed(self, selection):
         (model, iter) = selection.get_selected()
         if not iter:
@@ -4266,7 +4270,7 @@ class FirewallConfig(object):
         if self.runtime_view:
             chains = self.fw.getAllChains()
             rules = self.fw.getAllRules()
-            passthroughs = [ ]
+            passthroughs = self.fw.getAllPassthroughs()
         else:
             direct = self.fw.config().direct()
             settings = direct.getSettings()
@@ -5085,6 +5089,34 @@ class FirewallConfig(object):
                 self.fw.config().direct().update(settings)
                 self.changes_applied()
 
+    def direct_passthrough_added_cb(self, ipv, args):
+        print "direct_passthrough_added_cb", repr(ipv), repr(args)
+        if not self.show_direct:
+            return
+        joined_args = joinArgs(args)
+        print "joined_args=%s" % repr(joined_args)
+        iter = self.directPassthroughStore.get_iter_first()
+        while iter:
+            if self.directPassthroughStore.get_value(iter, 0) == ipv and \
+               self.directPassthroughStore.get_value(iter, 1) == joined_args:
+                return
+            iter = self.directPassthroughStore.iter_next(iter)
+        self.directPassthroughStore.append([ipv, joined_args])
+
+    def direct_passthrough_removed_cb(self, ipv, args):
+        print "direct_passthrough_removed_cb", repr(ipv), repr(args)
+        if not self.show_direct:
+            return
+        joined_args = joinArgs(args)
+        print "joined_args=%s" % repr(joined_args)
+        iter = self.directPassthroughStore.get_iter_first()
+        while iter:
+            if self.directPassthroughStore.get_value(iter, 0) == ipv and \
+               self.directPassthroughStore.get_value(iter, 1) == joined_args:
+                self.directPassthroughStore.remove(iter)
+                break
+            iter = self.directPassthroughStore.iter_next(iter)
+
     def add_edit_direct_passthrough(self, add):
         if add:
             old_ipv = ""
diff -up firewalld-0.3.9/src/firewall/core/fw_direct.py.RHBZ#993650 firewalld-0.3.9/src/firewall/core/fw_direct.py
--- firewalld-0.3.9/src/firewall/core/fw_direct.py.RHBZ#993650	2013-12-03 14:59:48.000000000 +0100
+++ firewalld-0.3.9/src/firewall/core/fw_direct.py	2014-09-29 22:52:11.977661537 +0200
@@ -55,15 +55,16 @@ class FirewallDirect:
         self._chains = LastUpdatedOrderedDict()
         self._rules = LastUpdatedOrderedDict()
         self._rule_priority_positions = { }
+        self._passthroughs = LastUpdatedOrderedDict()
 
     def cleanup(self):
         self.__init_vars()
 
     def get_config(self):
-        return (self._chains, self._rules)
+        return (self._chains, self._rules, self._passthroughs)
 
     def set_config(self, config):
-        (_chains, _rules) = config
+        (_chains, _rules, _passthroughs) = config
         for table_id in _chains:
             (ipv, table) = table_id
             for chain in _chains[table_id]:
@@ -82,6 +83,14 @@ class FirewallDirect:
                     except FirewallError as error:
                         log.warning(str(error))
 
+        for ipv in _passthroughs:
+            for args in _passthroughs[ipv]:
+                if not self.query_passthrough(ipv, args):
+                    try:
+                        self.add_passthrough(ipv, args)
+                    except FirewallError as error:
+                        log.warning(str(error))
+
     # DIRECT CHAIN
 
     def __chain(self, add, ipv, table, chain):
@@ -268,10 +277,10 @@ class FirewallDirect:
         for key in self._rules:
             (ipv, table, chain) = key
             for (priority, args) in self._rules[key]:
-                r.append((ipv, table, chain, priority, args))
+                r.append((ipv, table, chain, priority, list(args)))
         return r
 
-    # DIRECT PASSTROUGH
+    # DIRECT PASSTROUGH (untracked)
 
     def passthrough(self, ipv, args):
         try:
@@ -279,3 +288,64 @@ class FirewallDirect:
         except Exception as msg:
             log.debug2(msg)
             raise FirewallError(COMMAND_FAILED, msg)
+
+    # DIRECT PASSTROUGH (tracked)
+
+    def _check_ipv(self, ipv):
+        ipvs = [ 'ipv4', 'ipv6', 'eb' ]
+        if ipv not in ipvs:
+            raise FirewallError(INVALID_IPV,
+                                "'%s' not in '%s'" % (ipv, ipvs))
+
+    def __passthrough(self, enable, ipv, args):
+        self._check_ipv(ipv)
+
+        passthrough_id = (ipv, args)
+        if enable:
+            if ipv in self._passthroughs and args in self._passthroughs[ipv]:
+                raise FirewallError(ALREADY_ENABLED,
+                                    "passthrough '%s', '%s'" % (ipv, args))
+        else:
+            if not ipv in self._passthroughs or \
+               args not in self._passthroughs[ipv]:
+                raise FirewallError(NOT_ENABLED,
+                                    "passthrough '%s', '%s'" % (ipv, args))
+
+        try:
+            self._fw.rule(ipv, args)
+        except Exception as msg:
+            log.debug2(msg)
+            raise FirewallError(COMMAND_FAILED, msg)
+
+        if enable:
+            if not ipv in self._passthroughs:
+                self._passthroughs[ipv] = [ ]
+            self._passthroughs[ipv].append(args)
+        else:
+            self._passthroughs[ipv].remove(args)
+            if len(self._passthroughs[ipv]) == 0:
+                del self._passthroughs[ipv]
+
+    def add_passthrough(self, ipv, args):
+        self.__passthrough(True, ipv, list(args))
+
+    def remove_passthrough(self, ipv, args):
+        self.__passthrough(False, ipv, list(args))
+
+    def query_passthrough(self, ipv, args):
+        return (ipv in self._passthroughs and \
+                list(args) in self._passthroughs[ipv])
+
+    def get_all_passthroughs(self):
+        r = [ ]
+        for ipv in self._passthroughs:
+            for args in self._passthroughs[ipv]:
+                r.append((ipv, list(args)))
+        return r
+
+    def get_passthroughs(self, ipv):
+        r = [ ]
+        if ipv in self._passthroughs:
+            for args in self._passthroughs[ipv]:
+                r.append(list(args))
+        return r
diff -up firewalld-0.3.9/src/firewall/core/fw.py.RHBZ#993650 firewalld-0.3.9/src/firewall/core/fw.py
--- firewalld-0.3.9/src/firewall/core/fw.py.RHBZ#993650	2014-09-29 22:40:24.027110130 +0200
+++ firewalld-0.3.9/src/firewall/core/fw.py	2014-09-29 22:51:23.763516877 +0200
@@ -211,14 +211,9 @@ class Firewall:
             log.debug1("Failed to load direct rules file '%s': %s",
                       FIREWALLD_DIRECT, msg)
         else:
-            self.direct.set_config((obj.get_all_chains(), obj.get_all_rules()))
-            for ipv, args in obj.get_all_passthroughs().items():
-                for arg in args:
-                    try:
-                        self.direct.passthrough(ipv, arg)
-                    except FirewallError as error:
-                        log.warning(str(error))
-            # TODO: copy obj into config interface
+            self.direct.set_config((obj.get_all_chains(),
+                                    obj.get_all_rules(),
+                                    obj.get_all_passthroughs()))
         self.config.set_direct(copy.deepcopy(obj))
 
         # check if default_zone is a valid zone
diff -up firewalld-0.3.9/src/firewall/core/io/direct.py.RHBZ#993650 firewalld-0.3.9/src/firewall/core/io/direct.py
--- firewalld-0.3.9/src/firewall/core/io/direct.py.RHBZ#993650	2014-09-29 22:40:24.035110167 +0200
+++ firewalld-0.3.9/src/firewall/core/io/direct.py	2014-09-29 22:41:16.197349211 +0200
@@ -25,7 +25,7 @@ import io
 import shutil
 
 from firewall.fw_types import *
-from firewall.functions import splitArgs, joinArgs
+from firewall.functions import splitArgs, joinArgs, u2b_if_py2
 from firewall.errors import *
 from firewall.core.io.io_object import *
 from firewall.core.logger import log
@@ -50,7 +50,8 @@ class direct_ContentHandler(IO_Object_Co
             ipv = attrs["ipv"]
             table = attrs["table"]
             chain = attrs["chain"]
-            self.item.add_chain(ipv, table, chain)
+            self.item.add_chain(u2b_if_py2(ipv), u2b_if_py2(table),
+                                u2b_if_py2(chain))
 
         elif name == "rule":
             if not self.direct:
@@ -67,14 +68,15 @@ class direct_ContentHandler(IO_Object_Co
                 log.error("Parse Error: %s is not a valid priority" % 
                           attrs["priority"])
                 return
-            self._rule = [ipv, table, chain, priority]
+            self._rule = [ u2b_if_py2(ipv), u2b_if_py2(table),
+                           u2b_if_py2(chain), priority ]
 
         elif name == "passthrough":
             if not self.direct:
                 log.error("Parse Error: command outside of direct")
                 return
             ipv = attrs["ipv"]
-            self._passthrough = [ipv]
+            self._passthrough = [ u2b_if_py2(ipv) ]
 
         else:
             log.error('Unknown XML element %s' % name)
@@ -86,7 +88,7 @@ class direct_ContentHandler(IO_Object_Co
         if name == "rule":
             if self._element:
                 # add arguments
-                self._rule.append(splitArgs(self._element))
+                self._rule.append([ u2b_if_py2(x) for x in splitArgs(self._element) ])
                 self.item.add_rule(*self._rule)
             else:
                 log.error("Error: rule does not have any arguments, ignoring.")
@@ -94,7 +96,7 @@ class direct_ContentHandler(IO_Object_Co
         elif name == "passthrough":
             if self._element:
                 # add arguments
-                self._passthrough.append(splitArgs(self._element))
+                self._passthrough.append([ u2b_if_py2(x) for x in splitArgs(self._element) ])
                 self.item.add_passthrough(*self._passthrough)
             else:
                 log.error("Error: passthrough does not have any arguments, " +
diff -up firewalld-0.3.9/src/firewall/core/io/lockdown_whitelist.py.RHBZ#993650 firewalld-0.3.9/src/firewall/core/io/lockdown_whitelist.py
--- firewalld-0.3.9/src/firewall/core/io/lockdown_whitelist.py.RHBZ#993650	2014-09-29 22:40:23.962109823 +0200
+++ firewalld-0.3.9/src/firewall/core/io/lockdown_whitelist.py	2014-09-29 22:41:16.197349211 +0200
@@ -142,9 +142,9 @@ class LockdownWhitelist(IO_Object):
         """ HACK. I haven't been able to make sax parser return
             strings encoded (because of python 2) instead of in unicode.
             Get rid of it once we throw out python 2 support."""
-        self.commands = u2b_if_py2(self.commands)
-        self.contexts = u2b_if_py2(self.contexts)
-        self.users = u2b_if_py2(self.users)
+        self.commands = [ u2b_if_py2(x) for x in self.commands ]
+        self.contexts = [ u2b_if_py2(x) for x in self.contexts ]
+        self.users = [ u2b_if_py2(x) for x in self.users ]
 
     # commands
 
diff -up firewalld-0.3.9/src/firewall/errors.py.RHBZ#993650 firewalld-0.3.9/src/firewall/errors.py
--- firewalld-0.3.9/src/firewall/errors.py.RHBZ#993650	2013-12-03 14:59:48.000000000 +0100
+++ firewalld-0.3.9/src/firewall/errors.py	2014-09-29 22:41:16.197349211 +0200
@@ -39,6 +39,7 @@ NAME_MISMATCH       =   27
 PARSE_ERROR         =   28
 ACCESS_DENIED       =   29
 UNKNOWN_SOURCE      =   30
+RT_TO_PERM_FAILED   =   31
 
 INVALID_ACTION      =  100
 INVALID_SERVICE     =  101
diff -up firewalld-0.3.9/src/firewall/server/firewalld.py.RHBZ#993650 firewalld-0.3.9/src/firewall/server/firewalld.py
--- firewalld-0.3.9/src/firewall/server/firewalld.py.RHBZ#993650	2014-09-29 22:40:23.973109875 +0200
+++ firewalld-0.3.9/src/firewall/server/firewalld.py	2014-09-29 22:54:13.271031417 +0200
@@ -259,6 +259,120 @@ class FirewallD(slip.dbus.service.Object
     def Reloaded(self):
         log.debug1("Reloaded()")
 
+    # runtime to permanent
+
+    @slip.dbus.polkit.require_auth(PK_ACTION_CONFIG)
+    @dbus_service_method(DBUS_INTERFACE, in_signature='', out_signature='')
+    @dbus_handle_exceptions
+    def runtimeToPermanent(self, sender=None):
+        """Make runtime configuration permanent
+        """
+        log.debug1("copyRuntimeToPermanent()")
+
+        # Services or icmptypes can not be modified in runtime, but they can
+        # be removed or modified in permanent environment. Therefore copying
+        # of services and icmptypes to permanent is also needed.
+
+        # services
+
+        for name in self.fw.service.get_services():
+            config = self.getServiceSettings(name)
+            try:
+                try:
+                    conf_obj = self.config.getServiceByName(name)
+                except FirewallError as e:
+                    if "INVALID_SERVICE" in e:
+                        log.debug1("Creating service '%s'" % name)
+                        self.config.addService(name, config)
+                    else:
+                        raise
+                else:
+                    if conf_obj.getSettings() != config:
+                        log.debug1("Copying service '%s' settings" % name)
+                        conf_obj.update(config)
+                    else:
+                        log.debug1("Service '%s' is identical" % name)
+            except Exception as e:
+                raise FirewallError(RT_TO_PERM_FAILED,
+                                    "service '%s' : %s" % (name, e))
+
+        # icmptypes
+
+        for name in self.fw.icmptype.get_icmptypes():
+            config = self.getIcmpTypeSettings(name)
+            try:
+                try:
+                    conf_obj = self.config.getIcmpTypeByName(name)
+                except FirewallError as e:
+                    if "INVALID_ICMPTYPE" in e:
+                        log.debug1("Creating icmptype '%s'" % name)
+                        self.config.addIcmpType(name, config)
+                    else:
+                        raise
+                else:
+                    if conf_obj.getSettings() != config:
+                        log.debug1("Copying icmptype '%s' settings" % name)
+                        conf_obj.update(config)
+                    else:
+                        log.debug1("IcmpType '%s' is identical" % name)
+            except Exception as e:
+                raise FirewallError(RT_TO_PERM_FAILED,
+                                "icmptype '%s' : %s" % (name, e))
+
+        # zones
+
+        for name in self.fw.zone.get_zones():
+            # zone runtime settings can be modified, but not service and
+            # icmptye settings
+            config = self.getZoneSettings(name)
+            try:
+                try:
+                    conf_obj = self.config.getZoneByName(name)
+                except FirewallError as e:
+                    if "INVALID_ZONE" in e:
+                        log.debug1("Creating zone '%s'" % name)
+                        self.config.addZone(name, config)
+                    else:
+                        raise
+                else:
+                    if conf_obj.getSettings() != config:
+                        log.debug1("Copying zone '%s' settings" % name)
+                        conf_obj.update(config)
+                    else:
+                        log.debug1("Zone '%s' is identical" % name)
+            except Exception as e:
+                raise FirewallError(RT_TO_PERM_FAILED,
+                                    "zone '%s' : %s" % (name, e))
+
+        # direct
+
+        # rt_config = self.fw.direct.get_config()
+        config = ( self.fw.direct.get_all_chains(),
+                   self.fw.direct.get_all_rules(),
+                   self.fw.direct.get_all_passthroughs() )
+        try:
+            if self.config.getSettings() != config:
+                log.debug1("Copying direct configuration")
+                self.config.update(config)
+            else:
+                log.debug1("Direct configuration is identical")
+        except Exception as e:
+            raise FirewallError(RT_TO_PERM_FAILED,
+                                "direct configuration: %s" % e)
+
+        # policies
+
+        config = self.fw.policies.lockdown_whitelist.export_config()
+        try:
+            if self.config.getSettings() != config:
+                log.debug1("Copying policies configuration")
+                self.config.setLockdownWhitelist(config)
+            else:
+                log.debug1("Policies configuration is identical")
+        except Exception as e:
+            raise FirewallError(RT_TO_PERM_FAILED,
+                                "policies configuration: %s" % e)
+
     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
     # POLICIES
     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@@ -1665,7 +1779,7 @@ class FirewallD(slip.dbus.service.Object
 
     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
-    # DIRECT PASSTHROUGH
+    # DIRECT PASSTHROUGH (untracked)
 
     @slip.dbus.polkit.require_auth(PK_ACTION_DIRECT)
     @dbus_service_method(DBUS_INTERFACE_DIRECT, in_signature='sas',
@@ -1678,3 +1792,84 @@ class FirewallD(slip.dbus.service.Object
         log.debug1("direct.passthrough('%s', '%s')" % (ipv, "','".join(args)))
         self.accessCheck(sender)
         return self.fw.direct.passthrough(ipv, args)
+
+    # DIRECT PASSTHROUGH (tracked)
+
+    @dbus_service_method(DBUS_INTERFACE_DIRECT, in_signature='sas',
+                         out_signature='')
+    @dbus_handle_exceptions
+    def addPassthrough(self, ipv, args, sender=None):
+        # inserts direct passthrough
+        ipv = dbus_to_python(ipv)
+        args = tuple( dbus_to_python(i) for i in args )
+        log.debug1("direct.addPassthrough('%s', '%s')" % \
+                   (ipv, "','".join(args)))
+        self.accessCheck(sender)
+        self.fw.direct.add_passthrough(ipv, args)
+        self.PassthroughAdded(ipv, args)
+
+    @dbus_service_method(DBUS_INTERFACE_DIRECT, in_signature='sas',
+                         out_signature='')
+    @dbus_handle_exceptions
+    def removePassthrough(self, ipv, args, sender=None):
+        # removes direct passthrough
+        ipv = dbus_to_python(ipv)
+        args = tuple( dbus_to_python(i) for i in args )
+        log.debug1("direct.removePassthrough('%s', '%s')" % \
+                       (ipv, "','".join(args)))
+        self.accessCheck(sender)
+        self.fw.direct.remove_passthrough(ipv, args)
+        self.PassthroughRemoved(ipv, args)
+
+    @slip.dbus.polkit.require_auth(PK_ACTION_DIRECT)
+    @dbus_service_method(DBUS_INTERFACE_DIRECT, in_signature='sas',
+                         out_signature='b')
+    @dbus_handle_exceptions
+    def queryPassthrough(self, ipv, args, sender=None):
+        # returns true if a passthrough is enabled
+        ipv = dbus_to_python(ipv)
+        args = tuple( dbus_to_python(i) for i in args )
+        log.debug1("direct.queryPassthrough('%s', '%s')" % \
+                       (ipv, "','".join(args)))
+        return self.fw.direct.query_passthrough(ipv, args)
+
+    @slip.dbus.polkit.require_auth(PK_ACTION_DIRECT)
+    @dbus_service_method(DBUS_INTERFACE_DIRECT, in_signature='',
+                         out_signature='a(sas)')
+    @dbus_handle_exceptions
+    def getAllPassthroughs(self, sender=None):
+        # returns list of all added passthroughs
+        log.debug1("direct.getAllPassthroughs()")
+        return self.fw.direct.get_all_passthroughs()
+
+    @slip.dbus.polkit.require_auth(PK_ACTION_DIRECT)
+    @dbus_service_method(DBUS_INTERFACE_DIRECT, in_signature='',
+                         out_signature='')
+    @dbus_handle_exceptions
+    def removeAllPassthroughs(self, sender=None):
+        # remove all passhroughs
+        log.debug1("direct.removeAllPassthroughs()")
+        for passthrough in self.getAllPassthroughs():
+            self.removePassthrough(*passthrough)
+
+    @slip.dbus.polkit.require_auth(PK_ACTION_DIRECT)
+    @dbus_service_method(DBUS_INTERFACE_DIRECT, in_signature='s',
+                         out_signature='aas')
+    @dbus_handle_exceptions
+    def getPassthroughs(self, ipv, sender=None):
+        # returns list of all added passthroughs with ipv
+        ipv = dbus_to_python(ipv)
+        log.debug1("direct.getPassthroughs('%s')", ipv)
+        return self.fw.direct.get_passthroughs(ipv)
+
+    @dbus.service.signal(DBUS_INTERFACE_DIRECT, signature='sas')
+    @dbus_handle_exceptions
+    def PassthroughAdded(self, ipv, args):
+        log.debug1("direct.PassthroughAdded('%s', '%s')" % \
+                       (ipv, "','".join(args)))
+
+    @dbus.service.signal(DBUS_INTERFACE_DIRECT, signature='sas')
+    @dbus_handle_exceptions
+    def PassthroughRemoved(self, ipv, args):
+        log.debug1("direct.PassthroughRemoved('%s', '%s')" % \
+                       (ipv, "','".join(args)))