<?xml version="1.0"?> 

<!-- Simon Chudley (src299) XML Network Manager -->
<!-- XST document to describe the translation from a generic firewall implementation into IPFilter rules. -->

<xsl:stylesheet xmlns="http://www.ecs.soton.ac.uk/~src299/xmlnetman" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:fw="http://www.ecs.soton.ac.uk/~src299/xmlnetman/firewall"
                version="1.0">
 
  <!-- Is just text output -->
  <xsl:output method="text"/>


  <!-- Top level component will be a single Firewall element -->
  <xsl:template match="/*/fw:Firewall">

    <xsl:text>&#xa;  # Automatically Generated IPFilter Firewall Configurations &#xa;&#xa;&#xa;</xsl:text>
 
    <!-- Process each FirewallConstruct -->
    <xsl:for-each select="fw:FirewallConstruct">

      <xsl:text>  ###############&#xa;</xsl:text>
      <xsl:text>  # </xsl:text><xsl:value-of select="@name"/><xsl:text>&#xa;</xsl:text>
      <xsl:text>  # </xsl:text><xsl:value-of select="@description"/><xsl:text>&#xa;&#xa;</xsl:text>

      <!-- Loop through each firewall rule -->
      <xsl:for-each select="fw:Rule">

        <!-- Check for direction of travel of the rule -->
        <xsl:choose>

          <!-- If this is true, then the rule has no direction, so we need to duplicate -->
          <xsl:when test="not(./fw:interface/@direction)">

            <!-- Outgoing packet -->
            <xsl:apply-templates select=".">
              <xsl:with-param name="direction">in</xsl:with-param>
              <xsl:with-param name="doDesc">yes</xsl:with-param>
            </xsl:apply-templates>

            <!-- Increment the rule ID by one if they are using it -->
            <xsl:variable name="sum">
              <xsl:choose>
                <xsl:when test="./@RuleID"> <xsl:value-of select="./@RuleID + 1"/> </xsl:when>
              </xsl:choose>
            </xsl:variable>

            <!-- Incomming packet -->
            <xsl:apply-templates select=".">
              <xsl:with-param name="direction">out</xsl:with-param>
              <xsl:with-param name="rule_id"> <xsl:value-of select="$sum"/> </xsl:with-param>
            </xsl:apply-templates>

          </xsl:when>
          <xsl:otherwise>

            <!-- Process this rule using the standard definition -->
            <xsl:apply-templates select="."/>

          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>

      <xsl:text>&#xa;</xsl:text>
      <xsl:text>&#xa;</xsl:text>

    </xsl:for-each>
  </xsl:template>


  <!-- Match an individual firewall rule -->
  <xsl:template match="fw:Rule">
    <xsl:param name="direction"/>
    <xsl:param name="doDesc"/>
    <xsl:param name="rule_id"/>

    <xsl:if test="@Desc != '' and $doDesc">
      <xsl:text>  # </xsl:text><xsl:value-of select="@Desc"/><xsl:text>&#xa;</xsl:text>
    </xsl:if>

    <!-- We may be overriding the rule ID -->
    <xsl:text>  </xsl:text>
    <xsl:if test="@RuleID != '' and not($rule_id)">@<xsl:value-of select="@RuleID"/><xsl:text> </xsl:text></xsl:if>
    <xsl:if test="$rule_id"><xsl:text>@</xsl:text> <xsl:value-of select="$rule_id"/> <xsl:text> </xsl:text></xsl:if>

    <!-- Process all the main components -->    
    <xsl:apply-templates select="fw:action"/> 

    <!-- Whether we want to override the direction definition -->
    <xsl:choose>
 
      <!-- Override it, use $direction value instead -->
      <xsl:when test="$direction">
        <xsl:value-of select="$direction"/> <xsl:text> </xsl:text>
      </xsl:when>
  
      <!-- Either explicitly going in or out -->           
      <xsl:otherwise>
        <xsl:value-of select="./fw:interface/@direction"/> <xsl:text> </xsl:text>
      </xsl:otherwise>

    </xsl:choose>

    <!-- Packet logging - we need other stuff for IPFilter -->
    <xsl:if test="./fw:log/@value = 'true'"> 
      <xsl:text>log </xsl:text>
    </xsl:if>

    <!-- How does this compare? WARNING:- USING QUICK ON ALL RULES -->
    <xsl:text>quick </xsl:text>

    <!-- The interface name : what about fastroute and dup?? -->
    <xsl:if test="./fw:interface/@via != ''"> 
      <xsl:text>on </xsl:text> 
      <xsl:value-of select="./fw:interface/@via"/> 
      <xsl:text> </xsl:text>
    </xsl:if>

    <!-- No TOS or TTL -->

    <!-- The protocol definition -->
    <xsl:if test="self::node()[fw:protocol] and ./fw:protocol/@type != 'all'"> 
      <xsl:apply-templates select="fw:protocol"/> 
    </xsl:if>

    <!-- Source and destination handeling -->
    <xsl:choose>

      <xsl:when test="self::node()[fw:src] and self::node()[fw:dst]">
        <xsl:choose> 
          <!-- DO WE REALLY WANT TO DO THIS? WHAT ABOUT PORT NUMBERS ON ANY TO ANY COMMS -->
          <xsl:when test="./fw:src/@type ='any' and ./fw:dst/@type = 'any'"> <xsl:text>all </xsl:text> </xsl:when>
          <xsl:otherwise>
            <xsl:text>from </xsl:text>
            <xsl:apply-templates select="fw:src"/>
            <xsl:text>to </xsl:text>
            <xsl:apply-templates select="fw:dst"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>

      <xsl:otherwise>
        <xsl:text>all </xsl:text>
      </xsl:otherwise>

    </xsl:choose>

    <xsl:if test="self::node()[fw:options]">
      <xsl:apply-templates select="fw:options"/>
    </xsl:if>

    <xsl:text>&#xa;</xsl:text>
    <xsl:if test="@Desc != '' and not($doDesc)"><xsl:text>&#xa;</xsl:text></xsl:if>

  </xsl:template>
 

  <!-- The action to perform -->
  <xsl:template match="fw:action">

    <xsl:if test="@perform='pass'"> <xsl:text>pass</xsl:text> </xsl:if>
    <xsl:if test="@perform='deny'"> <xsl:text>block</xsl:text> </xsl:if>
    <xsl:if test="@perform='reset'"> <xsl:text>block return-rst</xsl:text> </xsl:if>
    <xsl:if test="@perform='count'"> <xsl:text>count</xsl:text> </xsl:if>
<!--    <xsl:if test="@perform='check-state'"> <xsl:text>check-state</xsl:text> </xsl:if> ??? -->

    <xsl:if test="@perform='unreach'"> 
      <xsl:text>return-icmp </xsl:text><xsl:value-of select="@code"/>
    </xsl:if>

<!--    <xsl:if test="@perform='divert'"> 
      <xsl:text>divert </xsl:text><xsl:value-of select="@port"/>
    </xsl:if> ??? -->

<!--    <xsl:if test="@perform='tee'"> 
      <xsl:text>dup-to </xsl:text><xsl:value-of select="@port"/>
    </xsl:if> ??? dup-to needs interface[:IP] -->

<!--    <xsl:if test="@perform='fwd'"> 
      <xsl:text>fwd </xsl:text><xsl:value-of select="@ipaddr"/>
      <xsl:if test="self::node()[@port]"> <xsl:text> </xsl:text> <xsl:value-of select="@port"/> </xsl:if>
    </xsl:if> ??? Possibly 'to' but that takes an interface name, doesn't redirect to another machine -->

<!--    <xsl:if test="@perform='pipe'"> 
      <xsl:text>pipe </xsl:text><xsl:value-of select="@pipe_num"/>
    </xsl:if> ??? -->

<!--    <xsl:if test="@perform='queue'"> 
      <xsl:text>queue </xsl:text><xsl:value-of select="@queue_num"/>
    </xsl:if> ??? -->

    <xsl:if test="@perform='skipto'"> 
      <xsl:text>skip </xsl:text><xsl:value-of select="@number"/>
    </xsl:if>

    <xsl:text> </xsl:text>

  </xsl:template>
 

  <!-- The protocol specification to match -->
  <!-- tcp udp tcp/udp icmp or decnumber -->
  <xsl:template match="fw:protocol">
    <xsl:choose>
      <xsl:when test="@type='tcp'"> <xsl:text>proto tcp </xsl:text> </xsl:when>
      <xsl:when test="@type='udp'"> <xsl:text>proto udp </xsl:text> </xsl:when>
      <xsl:when test="@type='icmp'"> <xsl:text>proto icmp </xsl:text> </xsl:when>
      <xsl:when test="@type='other'"> 
        <xsl:text>proto </xsl:text> 
        <xsl:value-of select="@name"/> 
        <xsl:text> </xsl:text>
      </xsl:when>
    </xsl:choose>
  </xsl:template>


  <!-- The source address -->
  <xsl:template match="fw:src">
    <xsl:if test="@negate = 'true'"> <xsl:text>! </xsl:text> </xsl:if>

    <xsl:choose>
      <xsl:when test="@type='ip'">
        <xsl:value-of select="@address"/> <xsl:text> mask </xsl:text> <xsl:value-of select="@mask"/>
      </xsl:when>
      <xsl:otherwise> <xsl:value-of select="@type"/> </xsl:otherwise>
    </xsl:choose>

    <xsl:text> </xsl:text>

    <!-- For sets of ports -->
    <xsl:if test="@ports='set'">
      <xsl:for-each select="./fw:port-num">
         <xsl:if test="position() = 1"> <xsl:text>port = </xsl:text> <xsl:value-of select="@port"/> </xsl:if>
         <!-- WE NEED TO LOOP ROUND TO INCLUDE A NEW RULE DUE TO MULTIPLE PORT REFERENCES -->
         
      </xsl:for-each>
      <xsl:text> </xsl:text>
    </xsl:if>

    <!-- For inclusive port ranges -->
    <xsl:if test="@ports='inc-range'">

      <!-- WHAT TO DO ABOUT MULTIPLE PORT RANGES? ARE THESE INCLUSIVE OR NOT?? -->
      <xsl:for-each select="./fw:inc-port-range">
         <xsl:value-of select="@lower"/> <xsl:text disable-output-escaping="yes">&gt;&lt;</xsl:text> <xsl:value-of select="@upper"/>
      </xsl:for-each>
      <xsl:text> </xsl:text>
    </xsl:if>

  </xsl:template>


  <!-- The destination address -->
  <xsl:template match="fw:dst">
    <xsl:if test="@negate = 'true'"> <xsl:text>! </xsl:text> </xsl:if>

    <xsl:choose>
      <xsl:when test="@type='ip'">
        <xsl:value-of select="@address"/> <xsl:text> mask </xsl:text> <xsl:value-of select="@mask"/>
      </xsl:when>
      <xsl:otherwise> <xsl:value-of select="@type"/> </xsl:otherwise>
    </xsl:choose>

    <xsl:text> </xsl:text>

    <!-- For sets of ports -->
    <xsl:if test="@ports='set'">

      <xsl:for-each select="./fw:port-num">
         <xsl:if test="position() = 1"> <xsl:text>port = </xsl:text> <xsl:value-of select="@port"/> </xsl:if>

<xsl:if test="position() = 2">

            <!-- This will process the whole top levelrule again -->
     <!--       <xsl:apply-templates select="../../">
              <xsl:with-param name="fw:Rule/fw:dst/fw:port-num">out</xsl:with-param>
            </xsl:apply-templates>-->
</xsl:if>

         <!-- WE NEED TO LOOP ROUND TO INCLUDE A NEW RULE DUE TO MULTIPLE PORT REFERENCES -->
         
      </xsl:for-each>
      <xsl:text> </xsl:text>
    </xsl:if>

    <!-- For inclusive port ranges -->
    <xsl:if test="@ports='inc-range'">

      <!-- WHAT TO DO ABOUT MULTIPLE PORT RANGES? ARE THESE INCLUSIVE OR NOT?? -->
      <xsl:for-each select="./fw:inc-port-range">
         <xsl:value-of select="@lower"/> <xsl:text disable-output-escaping="yes">&gt;&lt;</xsl:text> <xsl:value-of select="@upper"/>
      </xsl:for-each>
      <xsl:text> </xsl:text>
    </xsl:if>

  </xsl:template>


  <!-- The interface specifications -->
  <xsl:template match="fw:interface">

    <xsl:if test="self::node()[@via]"><xsl:text>via </xsl:text>
      <xsl:value-of select="@via"/><xsl:text> </xsl:text>
    </xsl:if>
A
<!-- XMIT MEANS NAUT IN IPFILTER -->
    <xsl:if test="self::node()[@xmit]">
NOOOOOOOOOO
    </xsl:if>

 <!-- XMIT AND RECV HAVE NO MEANING IN IPFILTER 
    <xsl:if test="self::node()[@xmit]"><xsl:text>xmit </xsl:text>
      <xsl:value-of select="@xmit"/><xsl:text> </xsl:text>
    </xsl:if>

    <xsl:if test="self::node()[@recv]"><xsl:text>recv </xsl:text>
      <xsl:value-of select="@recv"/><xsl:text> </xsl:text>
    </xsl:if>
-->

  </xsl:template>


  <!-- The IP and TCP options -->
  <xsl:template match="fw:options">

    <!-- WHAT ABOUT TCP OPTIONS???? -->

    <!-- Process all the TCP flag elements -->
    <xsl:if test="count(fw:tcpflag) > 0"> 
      <xsl:text>flags </xsl:text>

      <!-- First output the list of flags that we want to be present -->
      <xsl:for-each select="./fw:tcpflag">
        <xsl:if test="@absent='no'"> 
          <xsl:choose>
            <xsl:when test="@flag='syn'"> <xsl:text>S</xsl:text> </xsl:when>
            <xsl:when test="@flag='fin'"> <xsl:text>F</xsl:text> </xsl:when>
            <xsl:when test="@flag='rst'"> <xsl:text>R</xsl:text> </xsl:when>
            <xsl:when test="@flag='push'"> <xsl:text>P</xsl:text> </xsl:when>
            <xsl:when test="@flag='ack'"> <xsl:text>A</xsl:text> </xsl:when>
            <xsl:when test="@flag='urg'"> <xsl:text>U</xsl:text> </xsl:when>
          </xsl:choose>
        </xsl:if>
      </xsl:for-each>

      <xsl:text>/</xsl:text>

      <!-- These are the flags that we are interested in -->
      <xsl:for-each select="./fw:tcpflag">
        <xsl:choose>
          <xsl:when test="@flag='syn'"> <xsl:text>S</xsl:text> </xsl:when>
          <xsl:when test="@flag='fin'"> <xsl:text>F</xsl:text> </xsl:when>
          <xsl:when test="@flag='rst'"> <xsl:text>R</xsl:text> </xsl:when>
          <xsl:when test="@flag='push'"> <xsl:text>P</xsl:text> </xsl:when>
          <xsl:when test="@flag='ack'"> <xsl:text>A</xsl:text> </xsl:when>
          <xsl:when test="@flag='urg'"> <xsl:text>U</xsl:text> </xsl:when>
        </xsl:choose>
      </xsl:for-each>

      <xsl:text> </xsl:text>
    </xsl:if>

    <!-- WHAT IF THEY HAVE SPECIFIED SOME OTHER TCP FLAGS!! -->
    <xsl:if test="@established = 'true' or @setup = 'true'">
       <xsl:text>flags </xsl:text>
       <xsl:if test="@established = 'true'"> <xsl:text>S/SA</xsl:text> </xsl:if>  <!-- CHECK -->
       <xsl:if test="@setup = 'true'"> <xsl:text>S/AUPRFS</xsl:text> </xsl:if>    <!-- CHECK -->
       <xsl:text> </xsl:text>
    </xsl:if>

    <!-- Process all the IP options elements -->
    <xsl:if test="count(fw:ipoption) > 0"> 
      <xsl:text>with </xsl:text>
      <xsl:for-each select="./fw:ipoption">
        <xsl:if test="@absent='yes'"> <xsl:text>not </xsl:text> </xsl:if>
        <xsl:choose>
          <xsl:when test="@spec='ssrr'"> <xsl:text>opt ssrr</xsl:text> </xsl:when>
          <xsl:when test="@spec='lsrr'"> <xsl:text>opt lsrr</xsl:text> </xsl:when>
          <xsl:when test="@spec='rr'"> <xsl:text>opt rr</xsl:text> </xsl:when>
          <xsl:when test="@spec='ts'"> <xsl:text>opt ts</xsl:text> </xsl:when>
        </xsl:choose>
        <xsl:if test="not(position()=last())"> <xsl:text>,</xsl:text> </xsl:if>
      </xsl:for-each>
      <xsl:text> </xsl:text>
    </xsl:if>

    <!-- Process all the ICMP elements -->
    <xsl:if test="count(fw:icmptype) > 0"> 
      <xsl:text>icmp-type </xsl:text>
      <xsl:for-each select="./fw:icmptype">
<xsl:if test="position()=1">
        <!-- WHAT ABOUT MULTIPLE ICMP REQUESTS? -->
        <xsl:choose>
          <xsl:when test="@type='echo-reply'"> <xsl:text>0</xsl:text> </xsl:when>
          <xsl:when test="@type='dst-unreach'"> <xsl:text>3</xsl:text> </xsl:when>
          <xsl:when test="@type='source-quench'"> <xsl:text>4</xsl:text> </xsl:when>
          <xsl:when test="@type='redirect'"> <xsl:text>5</xsl:text> </xsl:when>
          <xsl:when test="@type='echo-request'"> <xsl:text>8</xsl:text> </xsl:when>
          <xsl:when test="@type='router-ad'"> <xsl:text>9</xsl:text> </xsl:when>
          <xsl:when test="@type='router-sol'"> <xsl:text>10</xsl:text> </xsl:when>
          <xsl:when test="@type='ttl-exceed'"> <xsl:text>11</xsl:text> </xsl:when>
          <xsl:when test="@type='header-bad'"> <xsl:text>12</xsl:text> </xsl:when>
          <xsl:when test="@type='ts-request'"> <xsl:text>13</xsl:text> </xsl:when>
          <xsl:when test="@type='ts-reply'"> <xsl:text>14</xsl:text> </xsl:when>
          <xsl:when test="@type='info-request'"> <xsl:text>15</xsl:text> </xsl:when>
          <xsl:when test="@type='info-reply'"> <xsl:text>16</xsl:text> </xsl:when>
          <xsl:when test="@type='add-mask-request'"> <xsl:text>17</xsl:text> </xsl:when>
          <xsl:when test="@type='add-mask-reply'"> <xsl:text>18</xsl:text> </xsl:when>
        </xsl:choose>
</xsl:if>        
      </xsl:for-each>
      <xsl:text> </xsl:text>
    </xsl:if>

    <!-- KEEP FRAGS ??? -->
    <xsl:if test="self::node()[@keep-state]">
      <xsl:text>keep state </xsl:text>
    </xsl:if>


<!--
    <xsl:if test="@frag = 'true'"> <xsl:text>frag </xsl:text> </xsl:if>

    <xsl:if test="self::node()[@tcpoptions]"> 
      <xsl:text>tcpoptions </xsl:text> <xsl:value-of select="@tcpoptions"/> <xsl:text> </xsl:text>
    </xsl:if>
-->

	<!-- THIS OPTION IS NOT SUPPORTED WITHIN IPFILTER -->
    <xsl:if test="self::node()[@uid]">

    </xsl:if>

<!--
    <xsl:if test="self::node()[@gid]">
      <xsl:text>gid </xsl:text> <xsl:value-of select="@gid"/> <xsl:text> </xsl:text>
    </xsl:if>
-->
  </xsl:template>

</xsl:stylesheet>

