<?xml version="1.0"?> 

<!-- Simon Chudley (src299) XML Network Manager -->
<!-- XST document to describe the translation from a generic firewall implementation into IPFW 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 IPFW 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>

      <!-- Now process all the Rules -->
      <xsl:for-each select="fw:Rule">

        <xsl:if test="@Desc != ''">
          <xsl:text>  # </xsl:text><xsl:value-of select="@Desc"/><xsl:text>&#xa;</xsl:text>
        </xsl:if>

        <xsl:text>  add </xsl:text>
        <xsl:if test="@RuleID != ''"><xsl:value-of select="@RuleID"/><xsl:text> </xsl:text></xsl:if>

        <!-- Process all the main components -->    
        <xsl:apply-templates select="fw:action"/>   
        <xsl:apply-templates select="fw:protocol"/>

        <xsl:if test="self::node()[fw:src]">
          <xsl:text>from </xsl:text>
          <xsl:apply-templates select="fw:src"/>
        </xsl:if>

        <xsl:if test="self::node()[fw:dst]">
          <xsl:text>to </xsl:text>
          <xsl:apply-templates select="fw:dst"/>
        </xsl:if>

        <xsl:if test="self::node()[fw:options]">
          <xsl:apply-templates select="fw:options"/>
        </xsl:if>

        <xsl:if test="self::node()[fw:interface]">
          <xsl:apply-templates select="fw:interface"/>
        </xsl:if>
 
        <xsl:text>&#xa;</xsl:text>
        <xsl:if test="@Desc != ''"><xsl:text>&#xa;</xsl:text></xsl:if>
 
      </xsl:for-each>

      <xsl:text>&#xa;</xsl:text>
      <xsl:text>&#xa;</xsl:text>

    </xsl:for-each>
  </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>deny</xsl:text> </xsl:if>
    <xsl:if test="@perform='reset'"> <xsl:text>reset</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>unreach </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>tee </xsl:text><xsl:value-of select="@port"/>
    </xsl:if>

    <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>

    <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>skipto </xsl:text><xsl:value-of select="@number"/>
    </xsl:if>

    <xsl:text> </xsl:text>

  </xsl:template>
 

  <!-- The protocol specification to match -->
  <xsl:template match="fw:protocol">
    <xsl:choose>
      <xsl:when test="@type='all'"> <xsl:text>all</xsl:text> </xsl:when>
      <xsl:when test="@type='tcp'"> <xsl:text>tcp</xsl:text> </xsl:when>
      <xsl:when test="@type='udp'"> <xsl:text>udp</xsl:text> </xsl:when>
      <xsl:when test="@type='icmp'"> <xsl:text>icmp</xsl:text> </xsl:when>
      <xsl:when test="@type='other'"> <xsl:value-of select="@name"/> </xsl:when>
    </xsl:choose>
    <xsl:text> </xsl:text>
  </xsl:template>


  <!-- The source address -->
  <xsl:template match="fw:src">
    <xsl:if test="@negate = 'true'"> <xsl:text>not </xsl:text> </xsl:if>

    <xsl:choose>
      <xsl:when test="@type='ip'">
        <xsl:value-of select="@address"/>:<xsl:value-of select="@mask"/>
      </xsl:when>
      <xsl:when test="@type='any'"> <xsl:text>any</xsl:text> </xsl:when>
    </xsl:choose>

    <xsl:text> </xsl:text>
     
    <!-- For sets of ports -->
    <xsl:if test="@ports='set'">
      <xsl:for-each select="./fw:port-num">
         <xsl:value-of select="@port"/> 
         <xsl:if test="not(position()=last())"> <xsl:text>,</xsl:text> </xsl:if>
      </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? -->
      <xsl:for-each select="./fw:inc-port-range">
         <xsl:value-of select="@lower"/> <xsl:text>-</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>not </xsl:text> </xsl:if>

    <xsl:choose>
      <xsl:when test="@type='ip'">
        <xsl:value-of select="@address"/>:<xsl:value-of select="@mask"/>
      </xsl:when>
      <xsl:when test="@type='any'"> <xsl:text>any</xsl:text> </xsl:when>
    </xsl:choose>

    <xsl:text> </xsl:text>
     
    <!-- For sets of ports -->
    <xsl:if test="@ports='set'">
      <xsl:for-each select="./fw:port-num">
         <xsl:value-of select="@port"/> 
         <xsl:if test="not(position()=last())"> <xsl:text>,</xsl:text> </xsl:if>
      </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? -->
      <xsl:for-each select="./fw:inc-port-range">
         <xsl:value-of select="@lower"/> <xsl:text>-</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="@direction = 'in'"><xsl:text>in </xsl:text></xsl:if>
    <xsl:if test="@direction = 'out'"><xsl:text>out </xsl:text></xsl:if>

    <xsl:if test="self::node()[@via]"><xsl:text>via </xsl:text>
      <xsl:value-of select="@via"/><xsl:text> </xsl:text>
    </xsl:if>

    <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">

    <xsl:if test="@frag = 'true'"> <xsl:text>frag </xsl:text> </xsl:if>
    <xsl:if test="@established = 'true'"> <xsl:text>established </xsl:text> </xsl:if>
    <xsl:if test="@setup = 'true'"> <xsl:text>setup </xsl:text> </xsl:if>

    <xsl:if test="self::node()[@keep-state]">
      <xsl:text>keep-state </xsl:text>
      <xsl:if test="@keep-state != ''">
        <xsl:value-of select="@keep-state"/><xsl:text> </xsl:text>
      </xsl:if>
    </xsl:if>

    <!-- Process all the IP options elements -->
    <xsl:if test="count(fw:ipoption) > 0"> 
      <xsl:text>ipoptions </xsl:text>
      <xsl:for-each select="./fw:ipoption">
        <xsl:if test="@absent='yes'"> <xsl:text>!</xsl:text> </xsl:if>
        <xsl:choose>
          <xsl:when test="@spec='ssrr'"> <xsl:text>ssrr</xsl:text> </xsl:when>
          <xsl:when test="@spec='lsrr'"> <xsl:text>lsrr</xsl:text> </xsl:when>
          <xsl:when test="@spec='rr'"> <xsl:text>rr</xsl:text> </xsl:when>
          <xsl:when test="@spec='ts'"> <xsl:text>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 TCP options elements -->
    <xsl:if test="count(fw:tcpoption) > 0"> 
      <xsl:text>tcpoptions </xsl:text>
      <xsl:for-each select="./fw:tcpoption">
        <xsl:if test="@absent='yes'"> <xsl:text>!</xsl:text> </xsl:if>
        <xsl:choose>
          <xsl:when test="@spec='mss'"> <xsl:text>mss</xsl:text> </xsl:when>
          <xsl:when test="@spec='window'"> <xsl:text>window</xsl:text> </xsl:when>
          <xsl:when test="@spec='sack'"> <xsl:text>sack</xsl:text> </xsl:when>
          <xsl:when test="@spec='ts'"> <xsl:text>ts</xsl:text> </xsl:when>
          <xsl:when test="@spec='cc'"> <xsl:text>cc</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 TCP flag elements -->
    <xsl:if test="count(fw:tcpflag) > 0"> 
      <xsl:text>tcpflags </xsl:text>
      <xsl:for-each select="./fw:tcpflag">
        <xsl:if test="@absent='yes'"> <xsl:text>!</xsl:text> </xsl:if>
        <xsl:choose>
          <xsl:when test="@flag='syn'"> <xsl:text>syn</xsl:text> </xsl:when>
          <xsl:when test="@flag='fin'"> <xsl:text>fin</xsl:text> </xsl:when>
          <xsl:when test="@flag='rst'"> <xsl:text>rst</xsl:text> </xsl:when>
          <xsl:when test="@flag='push'"> <xsl:text>push</xsl:text> </xsl:when>
          <xsl:when test="@flag='ack'"> <xsl:text>ack</xsl:text> </xsl:when>
          <xsl:when test="@flag='urg'"> <xsl:text>urg</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>icmptypes </xsl:text>
      <xsl:for-each select="./fw:icmptype">
        <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 test="not(position()=last())"> <xsl:text>,</xsl:text> </xsl:if>
      </xsl:for-each>
      <xsl:text> </xsl:text>
    </xsl:if>
    
    <xsl:if test="self::node()[@uid]">
      <xsl:text>uid </xsl:text> <xsl:value-of select="@uid"/> <xsl:text> </xsl:text>
    </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>
