<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<article>
  <!--$Id$-->

  <articleinfo>
    <title>Extension Scripts (User Exits)</title>

    <authorgroup>
      <author>
        <firstname>Tom</firstname>

        <surname>Eastep</surname>
      </author>
    </authorgroup>

    <pubdate><?dbtimestamp format="Y/m/d"?></pubdate>

    <copyright>
      <year>2001-2010</year>

      <holder>Thomas M. Eastep</holder>
    </copyright>

    <legalnotice>
      <para>Permission is granted to copy, distribute and/or modify this
      document under the terms of the GNU Free Documentation License, Version
      1.2 or any later version published by the Free Software Foundation; with
      no Invariant Sections, with no Front-Cover, and with no Back-Cover
      Texts. A copy of the license is included in the section entitled
      <quote><ulink url="GnuCopyright.htm">GNU Free Documentation
      License</ulink></quote>.</para>
    </legalnotice>
  </articleinfo>

  <caution>
    <para><emphasis role="bold">This article applies to Shorewall 4.3 and
    later. If you are running a version of Shorewall earlier than Shorewall
    4.3.5 then please see the documentation for that
    release.</emphasis></para>
  </caution>

  <section id="Scripts">
    <title>Extension Scripts</title>

    <para>Extension scripts are user-provided scripts that are invoked at
    various points during firewall start, restart, stop and clear. For each
    script, the Shorewall compiler creates a Bourne Shell function with the
    extension script as its body and calls the function at runtime.</para>

    <caution>
      <orderedlist>
        <listitem>
          <para>Be sure that you actually need to use an extension script to
          do what you want. Shorewall has a wide range of features that cover
          most requirements.</para>
        </listitem>

        <listitem>
          <para>DO NOT SIMPLY COPY RULES THAT YOU FIND ON THE NET INTO AN
          EXTENSION SCRIPT AND EXPECT THEM TO WORK AND TO NOT BREAK SHOREWALL.
          TO USE SHOREWALL EXTENSION SCRIPTS YOU MUST KNOW WHAT YOU ARE DOING
          WITH RESPECT TO iptables/Netfilter AND SHOREWALL.</para>
        </listitem>
      </orderedlist>
    </caution>

    <para>The following scripts can be supplied:</para>

    <itemizedlist>
      <listitem>
        <para><filename>lib.private</filename> -- Intended to contain
        declarations of shell functions to be called by other run-time
        extension scripts. See<ulink url="MultiISP.html#lsm"> this
        article</ulink> for an example of its use.</para>
      </listitem>

      <listitem>
        <para><filename>compile</filename> -- Invoked by the rules compiler
        early in the compilation process. Must be written in Perl.</para>
      </listitem>

      <listitem>
        <para><filename>init</filename> -- invoked early in <quote>shorewall
        start</quote> and <quote>shorewall restart</quote></para>
      </listitem>

      <listitem>
        <para><filename>initdone</filename> -- invoked after Shorewall has
        flushed all existing rules but before any rules have been added to the
        builtin chains.</para>
      </listitem>

      <listitem>
        <para><filename>start</filename> -- invoked after the firewall has
        been started or restarted.</para>
      </listitem>

      <listitem>
        <para><filename>started</filename> -- invoked after the firewall has
        been marked as 'running'.</para>
      </listitem>

      <listitem>
        <para><filename>stop</filename> -- invoked as a first step when the
        firewall is being stopped.</para>
      </listitem>

      <listitem>
        <para><filename>stopped</filename> -- invoked after the firewall has
        been stopped.</para>
      </listitem>

      <listitem>
        <para><filename>clear</filename> -- invoked after the firewall has
        been cleared.</para>
      </listitem>

      <listitem>
        <para><filename>tcclear</filename> -- invoked to clear traffic shaping
        when CLEAR_TC=Yes in <ulink
        url="manpages/shorewall.conf.html">shorewall.conf</ulink>.</para>
      </listitem>

      <listitem>
        <para><filename>refresh</filename> -- called in place of
        <filename>init</filename> when the firewall is being refreshed rather
        than started or restarted.</para>
      </listitem>

      <listitem>
        <para><filename>refreshed</filename> -- invoked after the firewall has
        been refreshed.</para>
      </listitem>

      <listitem>
        <para><filename>maclog</filename> -- invoked while mac filtering rules
        are being created. It is invoked once for each interface having
        'maclist' specified and it is invoked just before the logging rule is
        added to the current chain (the name of that chain will be in
        $CHAIN).</para>
      </listitem>

      <listitem>
        <para><filename>isusable</filename> -- invoked when Shorewall is
        trying to determine the usability of the network interface associated
        with an optional entry in
        <filename>/etc/shorewall/providers</filename>. $1 is the name of the
        interface which will have been determined to be up and configured
        before the script is invoked. The return value from the script
        indicates whether or not the interface is usable (0 = usable, other =
        unusable).</para>

        <para>Example:<programlisting># Ping a gateway through the passed interface
case $1 in
    eth0)
        ping -c 4 -t 1 -I eth0 206.124.146.254 &gt; /dev/null 2&gt;&amp;1
        return
        ;;
    eth1)
        ping -c 4 -t 1 -I eth1 192.168.12.254 &gt; /dev/null 2&gt;&amp;1
        return
        ;;
    *)
        # No additional testing of other interfaces
        return 0
        ;;
esac</programlisting><caution>
            <para>We recommend that this script only be used with
            ADMINISABSENTMINDED=Yes.</para>

            <para>The firewall state when this script is invoked is
            indeterminate. So if you have ADMINISABSENTMINDED=No in <ulink
            url="manpages/shorewall.conf.html">shorewall.conf</ulink>(8) and
            output on an interface is not allowed by <ulink
            url="manpages/shorewall.conf.html">routestopped</ulink>(8) then
            the isuasable script must blow it's own holes in the firewall
            before probing.</para>
          </caution></para>
      </listitem>

      <listitem>
        <para><filename>save</filename> -- This script is invoked during
        execution of the <command>shorewall save</command> and
        <command>shorewall-lite save</command> commands.</para>
      </listitem>

      <listitem>
        <para><filename>restored</filename> -- This script is invoked at the
        completion of a successful <command>shorewall restore</command> and
        <command>shorewall-lite restore</command>.</para>
      </listitem>

      <listitem>
        <para>findgw -- This script is invoked when Shorewall is attempting to
        discover the gateway through a dynamic interface. The script is most
        often used when the interface is managed by dhclient which has no
        standardized location/name for its lease database. Scripts for use
        with dhclient on several distributions are available at <ulink
        url="http://www.shorewall.net/pub/shorewall/contrib/findgw/">http://www.shorewall.net/pub/shorewall/contrib/findgw/</ulink></para>
      </listitem>

      <listitem>
        <para><filename>scfilter</filename> -- Added in Shorewall 4.4.14.
        Unlike the other scripts, this script is executed by the command-line
        tools (<filename>/sbin/shorewall</filename>,
        <filename>/sbin/shorewall6</filename>, etc) and can be used to
        reformat the output of the <command>show connections</command>
        command. The connection information is piped through this script so
        that the script can drop information, add information or alter the
        format of the information. When using Shorewall Lite or Shorewall6
        Lite, the script is encapsulated in a function that is copied into the
        generated auxillary configuration file. That function is invoked by
        the 'show connections' command.</para>

        <para>The default script is as follows and simply pipes the output
        through unaltered.</para>

        <programlisting>#! /bin/sh
cat -</programlisting>
      </listitem>

      <listitem>
        <para><filename>postcompile</filename> -- Added in Shorewall 4.5.8.
        This shell script is invoked by<emphasis role="bold">
        /sbin/shorewall</emphasis> after a script has been compiled. $1 is the
        path name of the compiled script.</para>
      </listitem>
    </itemizedlist>

    <para><emphasis role="bold">If your version of Shorewall doesn't have the
    file that you want to use from the above list, you can simply create the
    file yourself.</emphasis> You can also supply a script with the same name
    as any of the filter chains in the firewall and the script will be invoked
    after the /etc/shorewall/rules file has been processed but before the
    /etc/shorewall/policy file has been processed.</para>

    <para>The following table indicate which commands invoke the various
    scripts.</para>

    <informaltable frame="none" rowheader="firstcol">
      <tgroup cols="2">
        <tbody>
          <row>
            <entry><emphasis role="bold">script</emphasis></entry>

            <entry><emphasis role="bold">Commands</emphasis></entry>
          </row>

          <row>
            <entry>clear</entry>

            <entry>clear</entry>
          </row>

          <row>
            <entry>compile</entry>

            <entry>check, compile, export, load, refresh, reload, restart,
            restore,start</entry>
          </row>

          <row>
            <entry>continue</entry>

            <entry></entry>
          </row>

          <row>
            <entry>init</entry>

            <entry>load, refresh, reload, restart restore, start</entry>
          </row>

          <row>
            <entry>initdone</entry>

            <entry>check, compile, export, refresh, restart, start</entry>
          </row>

          <row>
            <entry>isusable</entry>

            <entry>refresh, restart, restore, start</entry>
          </row>

          <row>
            <entry>maclog</entry>

            <entry>check, compile, export, refresh, restart, start</entry>
          </row>

          <row>
            <entry>postcompile</entry>

            <entry>compile, export, load, refresh, reload, restart, restore,
            start</entry>
          </row>

          <row>
            <entry>refresh</entry>

            <entry>refresh</entry>
          </row>

          <row>
            <entry>refreshed</entry>

            <entry>refresh</entry>
          </row>

          <row>
            <entry>restored</entry>

            <entry>restore</entry>
          </row>

          <row>
            <entry>save</entry>

            <entry>save</entry>
          </row>

          <row>
            <entry>scfilter</entry>

            <entry>show connections</entry>
          </row>

          <row>
            <entry>start</entry>

            <entry>load, reload, restart, start</entry>
          </row>

          <row>
            <entry>started</entry>

            <entry>load, reload, restart, start</entry>
          </row>

          <row>
            <entry>stop</entry>

            <entry>stop, clear</entry>
          </row>

          <row>
            <entry>stopped</entry>

            <entry>stop, clear</entry>
          </row>

          <row>
            <entry>tcclear</entry>

            <entry>load, reload, restart, restore, start</entry>
          </row>
        </tbody>
      </tgroup>
    </informaltable>

    <para>There are a couple of special considerations for commands in
    extension scripts:</para>

    <itemizedlist>
      <listitem>
        <para>When you want to run <command>iptables</command>, use the
        command <command>run_iptables</command> instead.
        <command>run_iptables</command> will run the iptables utility passing
        the arguments to <command>run_iptables</command> and if the command
        fails, the firewall will be stopped (or restored from the last
        <command>save</command> command, if any).
        <command>run_iptables</command> should not be called from the
        <filename>started</filename> or <filename>restored</filename>
        scripts.</para>
      </listitem>

      <listitem>
        <para>If you wish to generate a log message, use <emphasis
        role="bold">log_rule_limit</emphasis>. Parameters are:</para>

        <itemizedlist>
          <listitem>
            <para>Log Level</para>
          </listitem>

          <listitem>
            <para>Chain to insert the rule into</para>
          </listitem>

          <listitem>
            <para>Chain name to display in the message (this can be different
            from the preceding argument — see the <ulink
            url="PortKnocking.html">Port Knocking article</ulink> for an
            example of how to use this).</para>
          </listitem>

          <listitem>
            <para>Disposition to report in the message (ACCEPT, DROP,
            etc)</para>
          </listitem>

          <listitem>
            <para>Rate Limit (if passed as "" then $LOGLIMIT is assumed — see
            the LOGLIMIT option in <ulink
            url="Documentation.htm#Conf">/etc/shorewall/shorewall.conf</ulink>)</para>
          </listitem>

          <listitem>
            <para>Log Tag ("" if none)</para>
          </listitem>

          <listitem>
            <para>Command (-A or -I for append or insert).</para>
          </listitem>

          <listitem>
            <para>The remaining arguments are passed "as is" to
            iptables</para>
          </listitem>
        </itemizedlist>
      </listitem>

      <listitem>
        <para>Many of the extension scripts get executed for both the
        shorewall start and shorewall restart commands. You can determine
        which command is being executed using the contents of $COMMAND.</para>

        <programlisting>if [ $COMMAND = start ]; then
   ...</programlisting>
      </listitem>

      <listitem>
        <para>In addition to COMMAND, Shorewall defines three other variables
        that may be used for locating Shorewall files:</para>

        <itemizedlist>
          <listitem>
            <para>CONFDIR - The configuration directory. Will be <filename
            class="directory">/etc/shorewall</filename>, <filename
            class="directory">/etc/shorewall6/</filename>, <filename
            class="directory">/etc/shorewall-lite</filename>, or <filename
            class="directory">/etc/shorewall6-lite</filename> depending on
            which product is running.</para>
          </listitem>

          <listitem>
            <para>SHAREDIR - The product shared directory. Will be <filename
            class="directory">/usr/share/shorewall</filename>, <filename
            class="directory">/usr/share/shorewall6/</filename>, <filename
            class="directory">/usr/share/shorewall-lite</filename>, or
            <filename class="directory">/usr/share/shorewall6-lite</filename>
            depending on which product is running.</para>
          </listitem>

          <listitem>
            <para>VARDIR - The product state directory. Defaults <filename
            class="directory">/usr/share/shorewall</filename>, <filename
            class="directory">/usr/share/shorewall6/</filename>, <filename
            class="directory">/usr/share/shorewall-lite</filename>, or
            <filename class="directory">/usr/share/shorewall6-lite</filename>
            depending on which product is running, but may be overridden by an
            entry in ${CONFDIR}/vardir.</para>
          </listitem>
        </itemizedlist>
      </listitem>
    </itemizedlist>

    <para></para>

    <section id="Perl">
      <title>Compile-time vs Run-time Scripts</title>

      <para>Shorewall runs some extension scripts at compile-time rather than
      at run-time.</para>

      <para>The following table summarizes when the various extension scripts
      are run:<informaltable frame="all">
          <tgroup cols="2">
            <tbody>
              <row>
                <entry><emphasis role="bold">Compile-time</emphasis></entry>

                <entry><emphasis role="bold">Run-time</emphasis></entry>
              </row>

              <row>
                <entry>compile</entry>

                <entry>clear</entry>
              </row>

              <row>
                <entry>initdone</entry>

                <entry>init</entry>
              </row>

              <row>
                <entry>maclog</entry>

                <entry>isusable</entry>
              </row>

              <row>
                <entry>Per-chain (including those associated with
                actions)</entry>

                <entry>start</entry>
              </row>

              <row>
                <entry>postcompile</entry>

                <entry>started</entry>
              </row>

              <row>
                <entry></entry>

                <entry>stop</entry>
              </row>

              <row>
                <entry></entry>

                <entry>stopped</entry>
              </row>

              <row>
                <entry></entry>

                <entry>tcclear</entry>
              </row>

              <row>
                <entry></entry>

                <entry>refresh</entry>
              </row>

              <row>
                <entry></entry>

                <entry>refreshed</entry>
              </row>

              <row>
                <entry></entry>

                <entry>restored</entry>
              </row>

              <row>
                <entry></entry>

                <entry>scfilter</entry>
              </row>
            </tbody>
          </tgroup>
        </informaltable></para>

      <para>With the exception of postcompile, compile-time extension scripts
      are executed using the Perl 'eval `cat
      &lt;<emphasis>file</emphasis>&gt;`' mechanism. Be sure that each script
      returns a 'true' value; otherwise, the compiler will assume that the
      script failed and will abort the compilation.</para>

      <para>Each compile-time script is implicitly prefaced with:</para>

      <programlisting>package Shorewall::User;</programlisting>

      <para>Most scripts will need to begin with the following
      line:<programlisting>use Shorewall::Chains;</programlisting>For more
      complex scripts, you may need to 'use' other Shorewall Perl modules --
      browse <filename
      class="directory">/usr/share/shorewall/Shorewall/</filename> to see
      what's available.</para>

      <para>When a script is invoked, the <emphasis
      role="bold">$chainref</emphasis> scalar variable will hold a reference
      to a chain table entry.<simplelist>
          <member><emphasis role="bold">$chainref-&gt;{name}</emphasis>
          contains the name of the chain</member>

          <member><emphasis role="bold">$chainref-&gt;{table}</emphasis> holds
          the table name</member>
        </simplelist></para>

      <para>To add a rule to the chain:<programlisting>add_rule( $chainref, &lt;<emphasis>the rule</emphasis>&gt; [ , &lt;<emphasis>break lists</emphasis>&gt; ] );</programlisting>Where<simplelist>
          <member>&lt;<emphasis>the rule</emphasis>&gt; is a scalar argument
          holding the rule text. Do not include "-A &lt;<emphasis>chain
          name</emphasis>&gt;"</member>
        </simplelist>Example:<programlisting>add_rule( $chainref, '-j ACCEPT' );</programlisting></para>

      <para>The add_rule() function accepts an optional third argument; If
      that argument evaluates to true and the passed rule contains a <emphasis
      role="bold">--dports</emphasis> or <emphasis
      role="bold">--sports</emphasis> list with more than 15 ports (a port
      range counts as two ports), the rule will be split into multiple rules
      where each resulting rule has 15 or fewer ports in its <emphasis
      role="bold">--dports</emphasis> and <emphasis
      role="bold">--sports</emphasis> lists.</para>

      <para>To insert a rule into the chain:<programlisting> insert_rule( $chainref, &lt;<emphasis>rulenum</emphasis>&gt;, &lt;<emphasis>the rule</emphasis>&gt; );</programlisting>The
      <emphasis role="bold">log_rule_limit()</emphasis> function works like it
      did in the shell compiler with three exceptions:<itemizedlist>
          <listitem>
            <para>You pass the chain reference rather than the name of the
            chain.</para>
          </listitem>

          <listitem>
            <para>The commands are 'add' and 'insert' rather than '-A' and
            '-I'.</para>
          </listitem>

          <listitem>
            <para>There is only a single "pass as-is to iptables" argument (so
            you must quote that part).</para>
          </listitem>
        </itemizedlist>Example:<programlisting>log_rule_limit(
               'info' ,             #Log Level
               $chainref ,          #Chain to add the rule to
               $chainref-&gt;{name},   #Name of the chain as it will appear in the log prefix
               'DROP' ,             #Disposition of the packet
               '',                  #Limit
               '' ,                 #Log tag
               'add',               #Command
               '-p tcp'             #Added to the rule as-is
               );</programlisting>Note that in the 'initdone' script, there is
      no default chain (<emphasis role="bold">$chainref</emphasis>). You can
      obtain a reference to a standard chain by:<programlisting>my $chainref = $chain_table{&lt;<emphasis>table</emphasis>&gt;}{&lt;<emphasis>chain name</emphasis>&gt;};</programlisting>Example:<programlisting>my $chainref = $chain_table{filter}{INPUT};</programlisting></para>

      <para>You can also use the hash references <emphasis
      role="bold">$filter_table</emphasis>, <emphasis
      role="bold">$mangle_table</emphasis> and <emphasis
      role="bold">$nat_table</emphasis> to access chain references in the
      three main tables.</para>

      <para>Example:</para>

      <programlisting>my $chainref = $filter_table-&gt;{INPUT}; #Same as above with a few less keystrokes; runs faster too</programlisting>

      <para>For imformation about the 'compile' extension script, see the
      <ulink url="ManualChains.html">Manual Chains article</ulink>.</para>
    </section>
  </section>
</article>