<?xml version="1.0" encoding="iso-8859-1"?>
<!--
     The FreeBSD Documentation Project

     $FreeBSD$
-->

<chapter id="firewalls">
  <chapterinfo>
    <authorgroup>
      <author>
	<firstname>Joseph J.</firstname>
	<surname>Barbish</surname>
	<contrib>Contributed by </contrib>
      </author>
    </authorgroup>
    <authorgroup>
      <author>
	<firstname>Brad</firstname>
	<surname>Davis</surname>
	<contrib>Converted to SGML and updated by </contrib>
      </author>
    </authorgroup>
  </chapterinfo>

  <title>Firewalls</title>

  <indexterm><primary>firewall</primary></indexterm>

  <indexterm>
    <primary>security</primary>

    <secondary>firewalls</secondary>
  </indexterm>

  <sect1 id="firewalls-intro">
    <title>Introduction</title>

    <para>Firewalls make it possible to filter the incoming and
      outgoing traffic that flows through a system.  A firewall can
      use one or more sets of <quote>rules</quote> to inspect network
      packets as they come in or go out of network connections and
      either allows the traffic through or blocks it.  The rules of
      a firewall can inspect one or more characteristics of the
      packets such as the protocol type, source or destination host
      address, and source or destination port.</para>

    <para>Firewalls can enhance the security of a host or a network.
      They can be used to do one or more of the following:</para>

    <itemizedlist>
      <listitem>
	<para>Protect and insulate the applications, services, and
	  machines of an internal network from unwanted traffic from
	  the public Internet.</para>
      </listitem>

      <listitem>
	<para>Limit or disable access from hosts of the internal
	  network to services of the public Internet.</para>
      </listitem>

      <listitem>
	<para>Support network address translation
	  (<acronym>NAT</acronym>), which allows an internal network
	  to use private <acronym>IP</acronym> addresses and share a
	  single connection to the public Internet using either a
	  single <acronym>IP</acronym> address or a shared pool of
	  automatically assigned public addresses.</para>
      </listitem>
    </itemizedlist>

    <para>After reading this chapter, you will know:</para>

    <itemizedlist>
      <listitem>
	<para>How to define packet filtering rules.</para>
      </listitem>

      <listitem>
	<para>The differences between the firewalls built into
	  &os;.</para>
      </listitem>

      <listitem>
	<para>How to use and configure the
	  <application>PF</application> firewall.</para>
      </listitem>

      <listitem>
	<para>How to use and configure the
	  <application>IPFILTER</application> firewall.</para>
      </listitem>

      <listitem>
	<para>How to use and configure the
	  <application>IPFW</application> firewall.</para>
      </listitem>
    </itemizedlist>

    <para>Before reading this chapter, you should:</para>

    <itemizedlist>
      <listitem>
	<para>Understand basic &os; and Internet concepts.</para>
      </listitem>
    </itemizedlist>
  </sect1>

  <sect1 id="firewalls-concepts">
    <title>Firewall Concepts</title>

    <indexterm>
      <primary>firewall</primary>

      <secondary>rulesets</secondary>
    </indexterm>

    <para>A firewall ruleset can be either
      <quote>exclusive</quote> or <quote>inclusive</quote>.  An
      exclusive firewall allows all traffic through except for the
      traffic matching the ruleset.  An inclusive firewall does the
      reverse as it only allows traffic matching the rules through and
      blocks everything else.</para>

    <para>An inclusive firewall offers better control of the outgoing
      traffic, making it a better choice for systems that offer
      services to the public Internet.  It also controls the type of
      traffic originating from the public Internet that can gain
      access to a private network.  All traffic that does not match
      the rules is blocked and logged.  Inclusive firewalls are
      generally safer than exclusive firewalls because they
      significantly reduce the risk of allowing unwanted
      traffic.</para>

    <note>
      <para>Unless noted otherwise, all configuration and example
	rulesets in this chapter create inclusive firewall
	rulesets.</para>
    </note>

    <para>Security can be tightened further using a <quote>stateful
	firewall</quote>.  This type of firewall keeps track of open
      connections and only allows traffic which either matches an
      existing connection or opens a new, allowed connection.  The
      disadvantage of a stateful firewall is that it can be vulnerable
      to Denial of Service (<acronym>DoS</acronym>) attacks if a lot
      of new connections are opened very fast.  Most firewalls use a
      combination of stateful and non-stateful behavior.</para>
  </sect1>

  <sect1 id="firewalls-apps">
    <title>Firewall Packages</title>

    <para>&os; has three firewalls built into the base system:
      <emphasis>IPFILTER</emphasis>, also known as
      <acronym>IPF</acronym>, <emphasis>IPFIREWALL</emphasis>, also
      known as <acronym>IPFW</acronym>, and <acronym>PF</acronym>).
      &os; also provides two traffic shapers for controlling bandwidth
      usage: &man.altq.4; and &man.dummynet.4;.  Dummynet has
      traditionally been closely tied with <acronym>IPFW</acronym>,
      and <acronym>ALTQ</acronym> with <acronym>PF</acronym>.  Each
      firewall uses rules to control the access of packets to and from
      a &os; system, although they go about it in different ways and
      each has a different rule syntax.</para>

    <para>&os; provides multiple firewalls in order to meet the
      different requirements and preferences for a wide variety of
      users.  Each user should evaluate which firewall best meets
      their needs.</para>

    <para>Since all firewalls are based on inspecting the values of
      selected packet control fields, the creator of the firewall
      ruleset must have an understanding of how
      <acronym>TCP/IP</acronym> works, what the different values in
      the packet control fields are, and how these values are used in
      a normal session conversation.  For a good introduction, refer
      to <ulink
	url="http://www.ipprimer.com/overview.cfm">Daryl's TCP/IP
	Primer</ulink>.</para>
  </sect1>

  <sect1 id="firewalls-pf">
    <sect1info>
      <authorgroup>
	<author>
	  <firstname>John</firstname>
	  <surname>Ferrell</surname>
	  <contrib>Revised and updated by </contrib>
	  <!-- 24 March 2008 -->
	</author>
      </authorgroup>
    </sect1info>

    <title>PF and <acronym>ALTQ</acronym></title>

    <indexterm>
      <primary>firewall</primary>

      <secondary>PF</secondary>
    </indexterm>

    <para>Since &os;&nbsp;5.3, a ported version of OpenBSD's
      <acronym>PF</acronym> firewall has been included as an
      integrated part of the base system.  <acronym>PF</acronym> is a
      complete, full-featured firewall that has optional support for
      <acronym>ALTQ</acronym> (Alternate Queuing), which provides
      Quality of Service (<acronym>QoS</acronym>).</para>

    <para>Since the OpenBSD Project maintains the definitive
      reference for <acronym>PF</acronym> in the<ulink
	url="http://www.openbsd.org/faq/pf/">PF FAQ</ulink>, this
      section of the Handbook focuses on <acronym>PF</acronym> as it
      pertains to &os;, while providing some general usage
      information.</para>

    <para>More information about porting <acronym>PF</acronym> to &os;
      can be found at <ulink
	url="http://pf4freebsd.love2party.net/"></ulink>.</para>

    <sect2>
      <title>Using the PF Loadable Kernel Modules</title>

      <para>In order to use PF, the PF kernel module must be first
	loaded.  Add the following line to
	<filename>/etc/rc.conf</filename>:</para>

        <programlisting>pf_enable="YES"</programlisting>

	<para>Then, run the startup script to load the module:</para>

        <screen>&prompt.root; <userinput>service pf start</userinput></screen>

	<para>The PF module will not load if it cannot find the
	  ruleset configuration file.  The default location is
	  <filename>/etc/pf.conf</filename>.  If the PF ruleset is
	  located somewhere else, add a line to
	  <filename>/etc/rc.conf</filename> which specifies the full
	  path to the file:</para>

        <programlisting>pf_rules="<replaceable>/path/to/pf.conf</replaceable>"</programlisting>

	<para>The sample <filename>pf.conf</filename>
	  can be found in <filename
	    class="directory">/usr/share/examples/pf/</filename>.</para>

	<para>The <acronym>PF</acronym> module can also be loaded
	  manually from the command line:</para>

      <screen>&prompt.root; <userinput>kldload pf.ko</userinput></screen>

      <para>Logging support for PF is provided by
	<varname>pflog.ko</varname> which can be loaded by adding the
	following line to <filename>/etc/rc.conf</filename>:</para>

      <programlisting>pflog_enable="YES"</programlisting>

      <para>Then, run the startup script to load the module:</para>

      <screen>&prompt.root; <userinput>service pflog start</userinput></screen>

    </sect2>

    <sect2>
      <title>PF Kernel Options</title>

      <indexterm>
	<primary>kernel options</primary>

	<secondary>device pf</secondary>
      </indexterm>

      <indexterm>
	<primary>kernel options</primary>

	<secondary>device pflog</secondary>
      </indexterm>

      <indexterm>
	<primary>kernel options</primary>

	<secondary>device pfsync</secondary>
      </indexterm>

      <para>While it is not necessary to compile
	<acronym>PF</acronym> support into the &os; kernel, some of
	PF's advanced features are not included in the loadable
	module, namely &man.pfsync.4;, which is a pseudo-device that
	exposes certain changes to the state table used by
	<acronym>PF</acronym>.  It can be paired with &man.carp.4; to
	create failover firewalls using <acronym>PF</acronym>.  More
	information on <acronym>CARP</acronym> can be found in <link
	  linkend="carp">of the Handbook</link>.</para>

      <para>The following <acronym>PF</acronym> kernel options can be
	found in <filename>/usr/src/sys/conf/NOTES</filename>:</para>

      <programlisting>device pf
device pflog
device pfsync</programlisting>

      <para><literal>device pf</literal> enables PF support.</para>

      <para><literal>device pflog</literal> enables the optional
	&man.pflog.4; pseudo network device which can be used to log
	traffic to a &man.bpf.4; descriptor.  The &man.pflogd.8;
	daemon can then be used to store the logging information to
	disk.</para>

      <para><literal>device pfsync</literal> enables the optional
	&man.pfsync.4; pseudo-network device that is used to monitor
	<quote>state changes</quote>.</para>
    </sect2>

    <sect2>
      <title>Available <filename>rc.conf</filename> Options</title>

      <para>The following &man.rc.conf.5; statements can be used to
	configure <acronym>PF</acronym> and &man.pflog.4; at
	boot:</para>

      <programlisting>pf_enable="YES"                 # Enable PF (load module if required)
pf_rules="/etc/pf.conf"         # rules definition file for pf
pf_flags=""                     # additional flags for pfctl startup
pflog_enable="YES"              # start pflogd(8)
pflog_logfile="/var/log/pflog"  # where pflogd should store the logfile
pflog_flags=""                  # additional flags for pflogd startup</programlisting>

      <para>If there is a LAN behind the firewall and packets need to
	be forwarded for the computers on the LAN, or NAT is required,
	add the following option:</para>

      <programlisting>gateway_enable="YES"            # Enable as LAN gateway</programlisting>
    </sect2>

    <sect2>
      <title>Creating Filtering Rules</title>

      <para>By default, <acronym>PF</acronym> reads its configuration
	rules from <filename>/etc/pf.conf</filename> and modifies,
	drops, or passes packets according to the rules or definitions
	specified in this file.  The &os; installation includes
	several sample files located in
	<filename>/usr/share/examples/pf/</filename>.  Refer to the
	<ulink url="http://www.openbsd.org/faq/pf/">PF FAQ</ulink> for
	complete coverage of <acronym>PF</acronym> rulesets.</para>

      <warning>
	<para>When reading the <ulink
	    url="http://www.openbsd.org/faq/pf/">PF FAQ</ulink>,
	  keep in mind that different versions of &os; contain
	  different versions of PF.  Currently,
	  &os;&nbsp;8.<replaceable>X</replaceable> is using the
	  same version of <acronym>PF</acronym> as
	  OpenBSD&nbsp;4.1.  &os;&nbsp;9.<replaceable>X</replaceable>
	  and later is using the same version of <acronym>PF</acronym>
	  as OpenBSD&nbsp;4.5.</para>
      </warning>

      <para>The &a.pf; is a good place to ask questions about
	configuring and running the <acronym>PF</acronym> firewall.
	Do not forget to check the mailing list archives before asking
	questions.</para>
      <para>To control <acronym>PF</acronym>, use &man.pfctl.8;.
	Below are some useful options to this command.  Review
	&man.pfctl.8; for a description of all available
	options:</para>

      <informaltable frame="none" pgwide="1">
	<tgroup cols="2">
	  <thead>
	    <row>
	      <entry>Command</entry>
	      <entry>Purpose</entry>
	    </row>
	  </thead>

	  <tbody>
	    <row>
	      <entry><command>pfctl
		  <option>-e</option></command></entry>
	      <entry>Enable PF.</entry>
	    </row>

	    <row>
	      <entry><command>pfctl
		  <option>-d</option></command></entry>
	      <entry>Disable PF.</entry>
	    </row>

	    <row>
	      <entry><command>pfctl <option>-F</option> all
		  <option>-f</option> /etc/pf.conf</command></entry>
	      <entry>Flush all NAT, filter, state, and table
		rules and reload
		<filename>/etc/pf.conf</filename>.</entry>
	    </row>

	    <row>
	      <entry><command>pfctl <option>-s</option> [ rules | nat
		  state ]</command></entry>
	      <entry>Report on the filter rules, NAT rules, or state
		table.</entry>
	    </row>

	    <row>
	      <entry><command>pfctl <option>-vnf</option>
		  /etc/pf.conf</command></entry>
	      <entry>Check <filename>/etc/pf.conf</filename> for
		errors, but do not load ruleset.</entry>
	    </row>
	  </tbody>
	</tgroup>
      </informaltable>
    </sect2>

    <sect2>
      <title>Enabling <acronym>ALTQ</acronym></title>

      <para><acronym>ALTQ</acronym> is only available by compiling its
	support into the &os; kernel.  <acronym>ALTQ</acronym> is not
	supported by all network card drivers.  Refer to &man.altq.4;
	for a list of drivers that are supported by the release of
	&os;.</para>

      <para>The following kernel options will enable
	<acronym>ALTQ</acronym> and add additional
	functionality:</para>

      <programlisting>options         ALTQ
options         ALTQ_CBQ        # Class Based Queuing (CBQ)
options         ALTQ_RED        # Random Early Detection (RED)
options         ALTQ_RIO        # RED In/Out
options         ALTQ_HFSC       # Hierarchical Packet Scheduler (HFSC)
options         ALTQ_PRIQ       # Priority Queuing (PRIQ)
options         ALTQ_NOPCC      # Required for SMP build</programlisting>

      <para><literal>options ALTQ</literal> enables the
	<acronym>ALTQ</acronym> framework.</para>

      <para><literal>options ALTQ_CBQ</literal> enables
	<emphasis>Class Based Queuing</emphasis>
	(<acronym>CBQ</acronym>).  <acronym>CBQ</acronym>
	can be used to divide a connection's bandwidth into different
	classes or queues to prioritize traffic based on filter
	rules.</para>

      <para><literal>options ALTQ_RED</literal> enables
	<emphasis>Random Early Detection</emphasis>
	(<acronym>RED</acronym>).  <acronym>RED</acronym> is
	used to avoid network congestion by measuring the length of
	the queue and comparing it to the minimum and maximum
	thresholds for the queue.  If the queue is over the maximum,
	all new packets will be dropped.  <acronym>RED</acronym> drops
	packets from different connections randomly.</para>

      <para><literal>options ALTQ_RIO</literal> enables
	<emphasis>Random Early Detection In and Out</emphasis>.</para>

      <para><literal>options ALTQ_HFSC</literal> enables the
	<emphasis>Hierarchical Fair Service Curve Packet
	  Scheduler</emphasis> <acronym>HFSC</acronym>.  For more
	information, refer to <ulink
	  url="http://www-2.cs.cmu.edu/~hzhang/HFSC/main.html"></ulink>.</para>

      <para><literal>options ALTQ_PRIQ</literal> enables
	<emphasis>Priority Queuing</emphasis>
	(<acronym>PRIQ</acronym>).  <acronym>PRIQ</acronym> will
	always pass traffic that is in a higher queue first.</para>

      <para><literal>options ALTQ_NOPCC</literal> enables
	<acronym>SMP</acronym> support for <acronym>ALTQ</acronym>.
	This option is required on <acronym>SMP</acronym>
	systems.</para>
    </sect2>

    <sect2 id="pf-tutorial">
      <sect2info>
	<authorgroup>
	  <author>
	    <firstname>Peter</firstname>
	    <surname>Hansteen</surname>
	    <othername>N. M.</othername>
	    <contrib>Contributed by </contrib>
	  </author>
	</authorgroup>
      </sect2info>

      <title><acronym>PF</acronym> Rule Sets and Tools</title>

      <para>This section demonstrates some useful
	<acronym>PF</acronym> features and <acronym>PF</acronym>
	related tools in a series of examples.  A more thorough
	tutorial is available at <ulink
	  url="http://home.nuug.no/~peter/pf/">http://home.nuug.no/~peter/pf/</ulink>.</para>

      <tip>
	<para><filename role="package">security/sudo</filename> is
	  useful for running commands like <command>pfctl</command>
	  that require elevated privileges.  It can be installed from
	  the Ports Collection.</para>
      </tip>

      <sect3 id="pftut-simplest">
	<title>The Simplest Rule Set Ever</title>

	<para>The simplest possible setup is for a single machine
	  which will not run any services, and which will talk to one
	  network which may be the Internet.  A minimal
	  <filename>/etc/pf.conf</filename> looks like this:</para>

	<programlisting>block in all
pass out all keep state</programlisting>

	<para>Here we deny any incoming traffic, allow traffic we make
	  ourselves to pass, and retain state information on our
	  connections.  Keeping state information allows return
	  traffic for all connections we have initiated to pass back
	  to us.  This rule set is used on machines that can be
	  trusted.  The rule set can be loaded with</para>

	<screen>&prompt.root; <userinput>pfctl -e ; pfctl -f /etc/pf.conf</userinput></screen>
      </sect3>

      <sect3>
	<title>Tighter and More Elegant</title>

	<para>For a slightly more structured and complete setup, we
	  start by denying everything and then allowing only those
	  things we know that we need
	  <footnote><para>Why write the rule set to default deny?  The
	      short answer is, it gives better control at the expense
	      of some thinking.  The point of packet filtering is to
	      take control, not to run catch-up with what the bad guys
	      do.  Marcus Ranum has written a very entertaining and
	      informative article about this, <ulink
		url="http://www.ranum.com/security/computer_security/editorials/dumb/index.html">The
		Six Dumbest Ideas in Computer Security</ulink>, and
	      it is well written too.</para></footnote>.  This gives
	  us the opportunity to introduce two of the features which
	  make <acronym>PF</acronym> such a wonderful tool:
	  <firstterm>lists</firstterm> and
	  <firstterm>macros</firstterm>.</para>

	<para>We will make some changes to
	  <filename>/etc/pf.conf</filename>, starting with</para>

	<programlisting>block all</programlisting>

	<para>Then we back up a little.  Macros need to be defined
	  before use, so at the very top of the file, we add:</para>

	<programlisting>tcp_services = "{ ssh, smtp, domain, www, pop3, auth, pop3s }"
udp_services = "{ domain }"</programlisting>

	<para>Now we have demonstrated several things at once - what
	  macros look like, that macros may be lists, and that
	  <acronym>PF</acronym> understands rules using port names
	  equally well as it does port numbers.  The names are the
	  ones listed in <filename>/etc/services</filename>.  This
	  gives us something to put in our rules, which we edit
	  slightly to look like this:</para>

	<programlisting>block all
pass out proto tcp to any port $tcp_services keep state
pass proto udp to any port $udp_services keep state</programlisting>

	<para>At this point some of us will point out that UDP is
	  stateless, but <acronym>PF</acronym> actually manages to
	  maintain state information despite this.  Keeping state for
	  a UDP connection means that for example when you ask a name
	  server about a domain name, you will be able to receive its
	  answer.</para>

	<para>Since we have made changes to our
	  <filename>pf.conf</filename>, we load the new
	  rules:</para>

	<screen>&prompt.root; <userinput>pfctl -f /etc/pf.conf</userinput></screen>

	<para>and the new rules are applied.  If there are no syntax
	  errors, <command>pfctl</command> will not output any
	  messages during the rule load.  The <option>-v</option> flag
	  will produce more verbose <command>pfctl</command>
	  output.</para>

	<para>If there have been extensive changes to the rule set,
	  the rules can be tested before attempting to load them.  The
	  command to do this is</para>

	<screen>&prompt.root; <userinput>pfctl -nf /etc/pf.conf</userinput></screen>

	<para><option>-n</option> causes the rules to be interpreted
	  only, but does not load them.  This provides an opportunity
	  to correct any errors.  Under any circumstances, the last
	  valid rule set loaded will be in force until
	  <acronym>PF</acronym> is disabled or a new rule set is
	  loaded.</para>

	<tip>
	  <title>Use <command>pfctl -v</command> to Show the Parsed
	    Rule Set</title>

	  <para>Adding the <option>-v</option> to a
	    <command>pfctl</command> ruleset load (even a dry run with
	    <option>-n</option>) will display the fully parsed rules
	    exactly the way they will be loaded.  This is extremely
	    useful when debugging rules.</para>
	</tip>
      </sect3>

      <sect3 id="pftut-gateway">
	<title>A Simple Gateway with NAT</title>

	<para>To most users, a single machine setup will be of limited
	  interest, and at this point we move on to more realistic or
	  at least more common setups, concentrating on a machine
	  which is running <acronym>PF</acronym> and also acts as a
	  gateway for at least one other machine.</para>

	<sect4 id="pftut-gwpitfalls">
	  <title>Gateways and the Pitfalls of <literal>in</literal>,
	    <literal>out</literal> and <literal>on</literal></title>

	  <para>In the single machine setup, life is relatively
	    simple.  Traffic created on it should either pass out to
	    the rest of the world or not, and the administrator
	    decides what to let in from elsewhere.</para>

	  <para>On a gateway, the perspective changes from
	    <quote>me versus the network out there</quote> to
	    <quote>I am the one who decides what to pass to or from
	      all the networks I am connected to</quote>.  The machine
	    has at least two network interfaces, each connected to a
	    separate net.</para>

	  <para>It is very reasonable to think that for traffic to
	    pass from the network connected to
	    <devicename>xl1</devicename> to hosts on the network
	    connected to <devicename>xl0</devicename>, a rule like
	    this is needed:</para>

	  <programlisting>pass in on xl1 from xl1:network to xl0:network port $ports keep state</programlisting>

	  <para>This rule keeps track of states as well.</para>

	  <para>However, one of the most common and most
	    complained-about mistakes in firewall configuration is not
	    realizing that the <quote>to</quote> keyword does not in
	    itself guarantee passage all the way there.  The rule we
	    just wrote only lets the traffic pass in to the gateway on
	    the internal interface.  To let the packets get a bit
	    further, a matching rule is needed which says</para>

	  <programlisting>pass out on xl0 from xl1:network to xl0:network port $ports keep state</programlisting>

	  <para>These rules will work, but they will not necessarily
	    achieve the desired effect.</para>

	  <para>Rules this specific are rarely needed.  For the basic
	    gateway configurations we will be dealing with here, a
	    better rule says</para>

	  <programlisting>pass from xl1:network to any port $ports keep state</programlisting>

	  <para>This provides local net access to the Internet and
	    leaves the detective work to the
	    <firstterm>antispoof</firstterm> and
	    <firstterm>scrub</firstterm> code.  They are both pretty
	    good these days, and we will get back to them later.  For
	    now we just accept the fact that for simple setups,
	    interface-bound rules with in/out rules tend to add more
	    clutter than they are worth to rule sets.</para>

	  <para>For a busy network admin, a readable rule set is a
	    safer rule set.</para>

	  <para>For the remainder of this section, with some
	    exceptions, we will keep the rules as simple as possible
	    for readability.</para>
	</sect4>

	<sect4 id="pftut-whatsthelocalnet">
	  <title>What is the Local Network, Anyway?</title>

	  <para>Above, we introduced the
	    <literal>interface:network</literal> notation.  That is a
	    nice piece of shorthand, but the rule set can be made even
	    more readable and maintainable by taking the macro use a
	    tiny bit further.</para>

	  <para>For example, a <literal>$localnet</literal> macro
	    could be defined as the network directly attached to your
	    internal interface (<literal>$xl1:network</literal> in the
	    examples above).</para>

	  <para>Alternatively, the definition of
	    <literal>$localnet</literal> could be changed to an
	    <emphasis>IP address/netmask</emphasis> notation to denote
	    a network, such as <literal>192.168.100.1/24</literal> for
	    a subnet of private addresses.</para>

	  <para>If required, <literal>$localnet</literal> could even
	    be defined as a list of networks.  Whatever the specific
	    needs, a sensible <literal>$localnet</literal> definition
	    and a typical pass rule of the type</para>

	  <programlisting>pass from $localnet to any port $ports keep state</programlisting>

	  <para>could end up saving you a few headaches.  We will
	    stick to that convention from here on.</para>
	</sect4>

	<sect4 id="pftut-gwsimplesetup">
	  <title>Setting Up</title>

	  <para>We assume that the machine has acquired another
	    network card or at any rate there is a network
	    connection from the local network, via PPP or other
	    means.  We will not consider the specific interface
	    configurations.</para>

	  <para>For the discussion and examples below, only the
	    interface names will differ between a PPP setup and an
	    Ethernet one, and we will do our best to get rid of the
	    actual interface names as quickly as possible.</para>

	  <para>First, we need to turn on gatewaying in order to let
	    the machine forward the network traffic it receives on one
	    interface to other networks via a separate interface.
	    Initially we will do this on the command line with
	    &man.sysctl.8;, for traditional
	    <emphasis>IP version four</emphasis>.</para>

	  <screen>&prompt.root; <userinput>sysctl net.inet.ip.forwarding=1</userinput></screen>

	  <para>If we need to forward <emphasis>IP version
	      six</emphasis> traffic, the command is</para>

	  <screen>&prompt.root; <userinput>sysctl net.inet6.ip6.forwarding=1</userinput></screen>

	  <para>In order for this to continue working after the
	    computer has been restarted at some time in the future,
	    enter these settings into
	    <filename>/etc/rc.conf</filename>:</para>

	  <programlisting>gateway_enable="YES"		#for ipv4
ipv6_gateway_enable="YES"	#for ipv6</programlisting>

	  <para>Use <command>ifconfig -a</command>, or
	    <command>ifconfig
	      <replaceable>interface_name</replaceable></command> to
	    find out if both of the interfaces to be used are up and
	    running.</para>

	  <para>If all traffic initiated by machines on the inside is
	    to be allowed, <filename>/etc/pf.conf</filename> could
	    look roughly like this
	    <footnote>
	      <para>For dialup users, the external interface is the
		<filename>tun0</filename> pseudo-device.  Broadband
		users such as ADSL subscribers tend to have an
		Ethernet interface to play with, however for a
		significant subset of ADSL users, specifically those
		using PPP over Ethernet (PPPoE), the correct external
		interface will be the <filename>tun0</filename>
		pseudo-device, not the physical Ethernet
		interface.</para>
	      </footnote>:</para>

	  <programlisting>ext_if = "xl0"	# macro for external interface - use tun0 for PPPoE
int_if = "xl1"	# macro for internal interface
localnet = $int_if:network
# ext_if IP address could be dynamic, hence ($ext_if)
nat on $ext_if from $localnet to any -&gt; ($ext_if)
block all
pass from { lo0, $localnet } to any keep state</programlisting>

	  <para>Note the use of macros to assign logical names to the
	    network interfaces.  Here 3Com cards are used, but this is
	    the last time during this tutorial we will find this of
	    any interest whatsoever.  In truly simple setups like this
	    one, we may not gain very much by using macros like these,
	    but once the rule sets grow somewhat larger, you will
	    learn to appreciate the readability this provides.</para>

	  <para>Also note the <literal>nat</literal> rule.  This is
	    where we handle the network address translation from the
	    non-routable address inside the local net to the sole
	    official address we assume has been assigned.</para>

	  <para>The parentheses surrounding the last part of the nat
	    rule <literal>($ext_if)</literal> are there to compensate
	    for the possibility that the IP address of the external
	    interface may be dynamically assigned.  This detail will
	    ensure that network traffic runs without serious
	    interruptions even if the external IP address
	    changes.</para>

	  <para>On the other hand, this rule set probably allows more
	    traffic to pass out of the network than actually desired.
	    One reasonable setup could contain the macro</para>

	  <programlisting>client_out = "{ ftp-data, ftp, ssh, domain, pop3, auth, nntp, http, \
    https, cvspserver, 2628, 5999, 8000, 8080 }"</programlisting>

	  <para>and the main pass rule</para>

	  <programlisting>pass inet proto tcp from $localnet to any port $client_out \
    flags S/SA keep state</programlisting>

	  <para>This may be a somewhat peculiar selection of ports,
	    but it is based on a real life example.  Individual needs
	    probably differ at least in some specifics, but this
	    should cover at least some of the more useful
	    services.</para>

	  <para>In addition, we have a few other pass rules.  We will
	    be returning to some of the more interesting ones rather
	    soon.  One pass rule which is useful to those of us who
	    want the ability to administer our machines from elsewhere
	    is</para>

	  <programlisting>pass in inet proto tcp to port ssh</programlisting>

	  <para>or for that matter</para>

	  <programlisting>pass in inet proto tcp to $ext_if port ssh</programlisting>

	  <para>whichever is preferred.  Lastly we need to make the
	    name service work for our clients:</para>

	  <programlisting>udp_services = "{ domain, ntp }"</programlisting>

	  <para>This is supplemented with a rule which passes the
	    traffic we want through our firewall:</para>

	  <programlisting>pass quick inet proto { tcp, udp } to any port $udp_services keep state</programlisting>

	  <para>Note the <literal>quick</literal> keyword in this
	    rule.  We have started writing rule sets which consist of
	    several rules, and it is time to take a look at the
	    relationships between the rules in a rule set.  The rules
	    are evaluated from top to bottom, in the sequence they are
	    written in the configuration file.  For each packet or
	    connection evaluated by <acronym>PF</acronym>,
	    <emphasis>the last matching rule</emphasis> in the rule
	    set is the one which is applied.  The
	    <literal>quick</literal> keyword offers an escape from the
	    ordinary sequence.  When a packet matches a quick rule,
	    the packet is treated according to the present rule.  The
	    rule processing stops without considering any further
	    rules which might have matched the packet.  This is very
	    useful when a few isolated exceptions to the general rules
	    are needed.</para>

	  <para>This rule also takes care of <acronym>NTP</acronym>,
	    which is used for time synchronization.  One thing common
	    to both protocols is that they may under certain
	    circumstances communicate alternately over TCP and
	    UDP.</para>
	</sect4>
      </sect3>

      <sect3 id="pftut-ftp">
	<title>That Sad Old <acronym>FTP</acronym> Thing</title>

	<para>The short list of real life <acronym>TCP</acronym> ports
	  above contained, among other things, <acronym>FTP</acronym>.
	  <acronym>FTP</acronym> is a sad old thing and a problem
	  child, emphatically so for anyone trying to combine
	  <acronym>FTP</acronym> and firewalls.
	  <acronym>FTP</acronym> is an old and weird protocol, with a
	  lot to not like.  The most common points against it
	  are</para>

	<itemizedlist>
	  <listitem>
	    <para>Passwords are transferred in the clear</para>
	  </listitem>

	  <listitem>
	    <para>The protocol demands the use of at least two
	      <acronym>TCP</acronym> connections (control and data) on
	      separate ports</para>
	  </listitem>

	  <listitem>
	    <para>When a session is established, data is communicated
	      via ports selected at random</para>
	  </listitem>
	</itemizedlist>

	<para>All of these points make for challenges security-wise,
	  even before considering any potential weaknesses in client
	  or server software which may lead to security issues.  These
	  things have tended to happen.</para>

	<para>Under any circumstances, other more modern and more
	  secure options for file transfer exist, such as &man.sftp.1;
	  or &man.scp.1;, which feature both authentication and data
	  transfer via encrypted connections.  Competent
	  <acronym>IT</acronym> professionals should have a preference
	  for some other form of file transfer than
	  <acronym>FTP</acronym>.</para>

	<para>Regardless of our professionalism and preferences, we
	  are all too aware that at times we will need to handle
	  things we would prefer not to.  In the case of
	  <acronym>FTP</acronym> through firewalls, the main part of
	  our handling consists of redirecting the traffic to a small
	  program which is written specifically for this
	  purpose.</para>

	<sect4 id="pftut-ftp-proxy">
	  <title><acronym>FTP</acronym> Via Redirect:
	    <application>ftp-proxy</application></title>

	  <para>Enabling <acronym>FTP</acronym> transfers through your
	    gateway is amazingly simple, thanks to the
	    <acronym>FTP</acronym> proxy program (called
	    &man.ftp-proxy.8;) included in the base system on &os; and
	    other systems which offer <acronym>PF</acronym>. </para>

	  <para>The <acronym>FTP</acronym> protocol being what it is,
	    the proxy needs to dynamically insert rules in your rule
	    set.  &man.ftp-proxy.8; interacts with your configuration
	    via a set of anchors where the proxy inserts and deletes
	    the rules it constructs to handle your
	    <acronym>FTP</acronym> traffic.</para>

	  <para>To enable &man.ftp-proxy.8;, add this line to
	    <filename>/etc/rc.conf</filename>:</para>

	  <programlisting>ftpproxy_flags=""</programlisting>

	  <para>Starting the proxy manually by running
	    <command>/usr/sbin/ftp-proxy</command> allows testing of
	    the <acronym>PF</acronym> configuration changes we are
	    about to make.</para>

	  <para>For a basic configuration, only three elements need to
	    be added to <filename>/etc/pf.conf</filename>.  First, the
	    anchors:</para>

	  <programlisting>nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"</programlisting>

	  <para>The proxy will insert the rules it generates for the
	    <acronym>FTP</acronym> sessions here.  A pass rule is
	    needed to let <acronym>FTP</acronym> traffic in to the
	    proxy.</para>

	  <para>Now for the actual redirection.  Redirection rules and
	    <acronym>NAT</acronym> rules fall into the same rule
	    class.  These rules may be referenced directly by other
	    rules, and filtering rules may depend on these rules.
	    Logically, <literal>rdr</literal> and
	    <literal>nat</literal> rules need to be defined before the
	    filtering rules.</para>

	  <para>We insert our <literal>rdr</literal> rule immediately
	    after the <literal>nat</literal> rule in
	    <filename>/etc/pf.conf</filename></para>

	  <programlisting>rdr pass on $int_if proto tcp from any to any port ftp -> 127.0.0.1 port 8021</programlisting>

	  <para>In addition, the redirected traffic must be allowed to
	    pass.  We achieve this with</para>

	  <programlisting>pass out proto tcp from $proxy to any port ftp</programlisting>

	  <para>where <literal>$proxy</literal> expands to the address
	    the proxy daemon is bound to.</para>

	  <para>Save <filename>pf.conf</filename>, then load the new
	    rules with</para>

	  <screen>&prompt.root; <userinput>pfctl -f /etc/pf.conf</userinput></screen>

	  <para>At this point, users will probably begin noticing
	    that <acronym>FTP</acronym> works before they have been
	    told.</para>

	  <para>This example covers a basic setup where the clients in
	    the local net need to contact <acronym>FTP</acronym>
	    servers elsewhere.  The basic configuration here should
	    work well with most combinations of <acronym>FTP</acronym>
	    clients and servers.  As shown in the man page, the
	    proxy's behavior can be changed in various ways by adding
	    options to the <literal>ftpproxy_flags=</literal> line.
	    Some clients or servers may have specific quirks that must
	    be compensated for in the configuration, or there may be a
	    need to integrate the proxy in specific ways such as
	    assigning <acronym>FTP</acronym> traffic to a specific
	    queue.  For these and other finer points of
	    &man.ftp-proxy.8; configuration, start by studying the man
	    page.</para>

	  <para>For ways to run an <acronym>FTP</acronym> server
	    protected by <acronym>PF</acronym> and &man.ftp-proxy.8;,
	    look into running a separate <command>ftp-proxy</command>
	    in reverse mode (using <option>-R</option>), on a separate
	    port with its own redirecting pass rule.</para>
	</sect4>
      </sect3>

      <sect3 id="pftut-icmp">
	<title>Easing Troubleshooting</title>

	<para>Making network troubleshooting friendly is a potentially
	  large subject.  At most times, the debugging or
	  troubleshooting friendliness of a <acronym>TCP/IP</acronym>
	  network depends on treatment of the Internet protocol which
	  was designed specifically with debugging in mind, the
	  <emphasis>Internet Control Message Protocol</emphasis>, or
	  <acronym>ICMP</acronym> as it is usually abbreviated.</para>

	<para><acronym>ICMP</acronym> is the protocol for sending and
	  receiving <emphasis>control messages</emphasis> between
	  hosts and gateways, mainly to provide feedback to a sender
	  about any unusual or difficult conditions enroute to the
	  target host.</para>

	<para>There is a lot of <acronym>ICMP</acronym> traffic which
	  usually just happens in the background while users are
	  surfing the web, reading mail or transferring files.
	  Routers use <acronym>ICMP</acronym> to negotiate packet
	  sizes and other transmission parameters in a process often
	  referred to as <emphasis>path <acronym>MTU</acronym>
	    discovery</emphasis>.</para>

	<para>Some admins refer to <acronym>ICMP</acronym> as either
	  <quote>just evil</quote>, or, if their understanding runs a
	  little deeper, <quote>a necessary evil</quote>.  The reason
	  for this attitude is purely historical.  The reason can be
	  found a few years back when it was discovered that several
	  operating systems contained code in their networking stack
	  which could make a machine running one of the affected
	  systems crash and fall over, or in some cases just do really
	  strange things, with a sufficiently large
	  <acronym>ICMP</acronym> request.</para>

	<para>One of the companies which was hit hard was Microsoft,
	  and you can find rather a lot of material on the
	  <quote>ping of death</quote> bug by using your favorite
	  search engine.  This all happened in the second half of the
	  1990s, and all modern operating systems, at least the ones
	  we can read, have thoroughly sanitized their network code
	  since then.  At least that is what we are led to
	  believe.</para>

	<para>One of the early workarounds was to simply block either
	  all <acronym>ICMP</acronym> traffic or at least
	  <acronym>ICMP</acronym> ECHO, which is what ping uses.  Now
	  these rule sets have been around for roughly fifteen years,
	  and the people who put them there are still scared.</para>

	<sect4 id="pftut-dowepass">
	  <title>Then, Do We Let it All Through?</title>

	  <para>The obvious question then becomes, if
	    <acronym>ICMP</acronym> is such a good and useful thing,
	    should we not let it all through, all the time?  The
	    answer is <quote>It depends</quote>.</para>

	  <para>Letting diagnostic traffic pass unconditionally of
	    course makes debugging easier, but also makes it
	    relatively easy for others to extract information about
	    your network.  That means that a rule like</para>

	  <programlisting>pass inet proto icmp from any to any</programlisting>

	  <para>might not be optimal if the internal workings of the
	    local network should be cloaked in a bit of mystery.  In
	    all fairness it should also be said that some
	    <acronym>ICMP</acronym> traffic might be found quite
	    harmlessly riding piggyback on
	    <literal>keep state</literal> rules.</para>
	</sect4>

	<sect4 id="pftut-icmpstopatgw">
	  <title>The Easy Way Out: the Buck Stops Here</title>

	  <para>The easiest solution could very well be to let all
	    <acronym>ICMP</acronym> traffic from the local net through
	    and stop probes from elsewhere at the gateway:</para>

	  <programlisting>pass inet proto icmp from $localnet to any keep state
pass inet proto icmp from any to $ext_if keep state</programlisting>

	  <para>Stopping probes at the gateway might be an attractive
	    option anyway, but let us have a look at a few other
	    options which will show some of <acronym>PF</acronym>'s
	    flexibility.</para>
	</sect4>

	<sect4 id="pftut-letpingthru">
	  <title>Letting <command>ping</command> Through</title>

	  <para>The rule set we have developed so far has one clear
	    disadvantage: common troubleshooting commands such as
	    &man.ping.8; and &man.traceroute.8; will not work.  That
	    may not matter too much to end users, and since it was
	    <command>ping</command> which scared people into
	    filtering or blocking <acronym>ICMP</acronym> traffic in
	    the first place, there are apparently some people who feel
	    we are better off without it.  If you are in my perceived
	    target audience, you will be rather fond of having those
	    troubleshooting tools avalable.  With a couple of small
	    additions to the rule set, they will be.  &man.ping.8;
	    uses <acronym>ICMP</acronym>, and in order to keep our
	    rule set tidy, we start by defining another macro:</para>

	  <programlisting>icmp_types = "echoreq"</programlisting>

	  <para>and a rule which uses the definition,</para>

	  <programlisting>pass inet proto icmp all icmp-type $icmp_types keep state</programlisting>

	  <para>More or other types of <acronym>ICMP</acronym> packets
	    may need to go through, and <literal>icmp_types</literal>
	    can be expanded to a list of those packet types that are
	    allowed.</para>
	</sect4>

	<sect4 id="pftut-helptraceroute">
	  <title>Helping &man.traceroute.8;</title>

	  <para>&man.traceroute.8; is another command which is quite
	    useful when users claim that the Internet is not working.
	    By default, Unix <command>traceroute</command> uses UDP
	    connections according to a set formula based on
	    destination.  The rule below works with
	    <command>traceroute</command> on all unixes I've had
	    access to, including GNU/Linux:</para>

	  <programlisting># allow out the default range for traceroute(8):
# "base+nhops*nqueries-1" (33434+64*3-1)
pass out on $ext_if inet proto udp from any to any port 33433 &gt;&lt; 33626 keep state</programlisting>

	  <para>Experience so far indicates that
	    <command>traceroute</command> implementations on other
	    operating systems work roughly the same.  Except, of
	    course, on Microsoft Windows.  On that platform,
	    <command>TRACERT.EXE</command> uses ICMP ECHO for this
	    purpose.  So to let Windows traceroutes through, only the
	    first rule is needed.  Unix <command>traceroute</command>
	    can be instructed to use other protocols as well, and will
	    behave remarkably like its Microsoft counterpart if
	    <option>-I</option> is used.  Check the &man.traceroute.8;
	    man page (or its source code, for that matter) for all the
	    details.</para>

	  <para>Under any circumstances, this solution was lifted
	    from an openbsd-misc post.  I've found that list, and
	    the searchable list archives (accessible among other
	    places from <ulink
	      url="http://marc.theaimsgroup.com/">http://marc.theaimsgroup.com/</ulink>),
	    to be a very valuable resource whenever you need OpenBSD
	    or <acronym>PF</acronym> related information.</para>
	</sect4>

	<sect4 id="pftut-pathmtudisc">
	  <title>Path <acronym>MTU</acronym> Discovery</title>

	  <para>Internet protocols are designed to be device
	    independent, and one consequence of device independence is
	    that the optimal packet size for a given connection cannot
	    always be predicted reliably.  The main constraint on
	    packet size is called the
	    <firstterm>Maximum Transmission Unit</firstterm>, or
	    <acronym>MTU</acronym>, which sets the upper limit on the
	    packet size for an interface.  &man.ifconfig.8; shows the
	    <acronym>MTU</acronym> for the network interfaces.</para>

	  <para>Modern TCP/IP implementations expect to be able to
	    determine the right packet size for a connection through a
	    process which, simply put, involves sending packets of
	    varying sizes with the <quote>Do not fragment</quote> flag
	    set, expecting an <acronym>ICMP</acronym> return packet
	    indicating <quote>type 3, code 4</quote> when the upper
	    limit has been reached.  Now do not dive for the RFCs
	    right away.  Type 3 means <quote>destination
	      unreachable</quote>, while code 4 is short for
	    <quote>fragmentation needed, but the do-not-fragment flag
	      is set</quote>.  So if connections to networks which may
	    have other <acronym>MTU</acronym>s than the local network
	    seem sub-optimal, and there is no need to be that
	    specific, the list of <acronym>ICMP</acronym> types can be
	    changed slightly to let the
	    <quote>destination unreachable</quote> packets through,
	    too:</para>

	  <programlisting>icmp_types = "{ echoreq, unreach }"</programlisting>

	  <para>As we can see, this means we do not need to change
	    the pass rule itself:</para>

	  <programlisting>pass inet proto icmp all icmp-type $icmp_types keep state</programlisting>

	  <para><acronym>PF</acronym> allows filtering on all
	    variations of <acronym>ICMP</acronym> types and codes.
	    For those who want to delve into what to pass (or not) of
	    <acronym>ICMP</acronym> traffic, the list of possible
	    types and codes are documented in the &man.icmp.4; and
	    &man.icmp6.4; man pages.  The background information is
	    available in the <acronym>RFC</acronym>s
	    <footnote><para>The main internet <acronym>RFC</acronym>s
		describing <acronym>ICMP</acronym> and
		some related techhiques are RFC792, RFC950, RFC1191,
		RFC1256, RFC2521, rfc2765, while necessary updates for
		ICMP for IPv6 are found in RFC1885, RFC2463, RFC2466.
		These documents are available in a number of places on
		the net, such as the
		<ulink url="http://www.ietf.org">ietf.org</ulink>
		and
		<ulink url="http://www.faqs.org">faqs.org</ulink>
		web sites.</para></footnote>.</para>
	</sect4>
      </sect3>

      <sect3 id="pftut-tables">
	<title>Tables Make Life Easier</title>

	<para>By this time it may appear that this gets awfully static
	  and rigid.  There will after all be some kinds of data which
	  are relevant to filtering and redirection at a given time,
	  but do not deserve to be put into a configuration file!
	  Quite right, and <acronym>PF</acronym> offers mechanisms for
	  handling these situations as well.  Tables are one such
	  feature, mainly useful as lists which can be manipulated
	  without needing to reload the entire rule set, and where
	  fast lookups are desirable.  Table names are always enclosed
	  in <literal>&lt; &gt;</literal>, like this:</para>

	<programlisting>table &lt;clients&gt; { 192.168.2.0/24, !192.168.2.5 }</programlisting>

	<para>Here, the network <literal>192.168.2.0/24</literal>
	  is part of the table, except the address
	  <literal>192.168.2.5</literal>, which is excluded using
	  the <literal>!</literal> operator (logical NOT).  It is
	  also possible to load tables from files where each item is
	  on a separate line, such as the file
	  <filename>/etc/clients</filename>.</para>

	<programlisting>192.168.2.0/24
!192.168.2.5</programlisting>

	<para>which in turn is used to initialize the table in
	  <filename>/etc/pf.conf</filename>:</para>

	<programlisting>table &lt;clients&gt; persist file /etc/clients</programlisting>

	<para>Then, for example, one of our earlier rules can be
	  changed to read</para>

	<programlisting>pass inet proto tcp from &lt;clients&gt; to any port $client_out flags S/SA keep state</programlisting>

	<para>to manage outgoing traffic from client computers.  With
	  this in hand, the table's contents can be manipulated live,
	  such as</para>

	<screen>&prompt.root; <userinput>pfctl -t clients -T add 192.168.1/16</userinput></screen>

	<para>Note that this changes the in-memory copy of the table
	  only, meaning that the change will not survive a power
	  failure or other reboot unless there are arrangements to
	  store the changes.</para>

	<para>One might opt to maintain the on-disk copy of the table
	  using a &man.cron.8; job which dumps the table content to
	  disk at regular intervals, using a command such as
	  <command>pfctl -t clients -T show
	    &gt;/etc/clients</command>.  Alternatively,
	  <filename>/etc/clients</filename> could be edited, replacing
	  the in-memory table contents with the file data:</para>

	<screen>&prompt.root; <userinput>pfctl -t clients -T replace -f /etc/clients</userinput></screen>

	<para>For operations performed frequently, administrators will
	  sooner or later end up writing shell scripts for tasks
	  such as inserting or removing items or replacing table
	  contents.  The only real limitations lie in individual needs
	  and creativity.</para>
      </sect3>

      <sect3 id="pftut-overload">
	<title>Overload Tables</title>

	<para>Those who run a Secure Shell login service which is
	  accessible from the Internet have probably seen something
	  like this in the authentication logs:</para>

	<programlisting>Sep 26 03:12:34 skapet sshd[25771]: Failed password for root from 200.72.41.31 port 40992 ssh2
Sep 26 03:12:34 skapet sshd[5279]: Failed password for root from 200.72.41.31 port 40992 ssh2
Sep 26 03:12:35 skapet sshd[5279]: Received disconnect from 200.72.41.31: 11: Bye Bye
Sep 26 03:12:44 skapet sshd[29635]: Invalid user admin from 200.72.41.31
Sep 26 03:12:44 skapet sshd[24703]: input_userauth_request: invalid user admin
Sep 26 03:12:44 skapet sshd[24703]: Failed password for invalid user admin from 200.72.41.31 port 41484 ssh2</programlisting>

	<para>And so on.  This is what a brute force attack looks
	  like.  Essentially somebody, or more likely, a cracked
	  computer somewhere, is trying by brute force to find a
	  combination of user name and password which will let them
	  into your system.</para>

	<para>The simplest response would be to write a
	  <filename>pf.conf</filename> rule which blocks all access.
	  This leads to another class of problems, including what to
	  do in order to let people with legitimate business on the
	  system access it anyway.  Some might consider moving the
	  service to another port, but then again, the ones flooding
	  on port 22 would probably be able to scan their way to port
	  22222 for a repeat performance.</para>

	<para>Since OpenBSD 3.7, and soon after in &os; version 6.0,
	  <acronym>PF</acronym> has offered a slightly more elegant
	  solution.  Pass rules can be written so they maintain
	  certain limits on what connecting hosts can do.  For good
	  measure, violators can be banished to a table of addresses
	  which are denied some or all access.  If desired, it's even
	  possible to drop all existing connections from machines
	  which overreach the limits.  Here is how it is done:</para>

	<para>First, set up the table.  In the tables section,
	  add</para>

	<programlisting>table &lt;bruteforce&gt; persist</programlisting>

	<para>Then somewhere fairly early in the rule set, add a rule
	  to block the bruteforcers:</para>

	<programlisting>block quick from &lt;bruteforce&gt;</programlisting>

	<para>And finally, the pass rule.</para>

	<programlisting>pass inet proto tcp from any to $localnet port $tcp_services \
    flags S/SA keep state \
    (max-src-conn 100, max-src-conn-rate 15/5, \
    overload &lt;bruteforce&gt; flush global)</programlisting>

	<para>The first part here is identical to the main rule we
	  constructed earlier.  The part in parentheses is the new
	  stuff which will ease network load even further.</para>

	<para><literal>max-src-conn</literal> is the number of
	  simultaneous connections allowed from one host.  In this
	  example, it is set at 100.  Other setups may want a slightly
	  higher or lower value.</para>

	<para><literal>max-src-conn-rate</literal> is the rate of new
	  connections allowed from any single host, here 15
	  connections per 5 seconds.  Again, the administrator is the
	  one to judge what suits their setup.</para>

	<para><literal>overload &lt;bruteforce&gt;</literal> means
	  that any host which exceeds these limits gets its address
	  added to the table <literal>bruteforce</literal>.  Our rule
	  set blocks all traffic from addresses in the bruteforce
	  table.</para>

	<para>Finally, <literal>flush global</literal> says that when
	  a host reaches the limit, that host's connections will be
	  terminated (flushed).  The global part says that for good
	  measure, this applies to connections which match other pass
	  rules too.</para>

	<para>The effect is dramatic.  From here on, bruteforcers
	  more often than not will end up with
	  <computeroutput>&quot;Fatal: timeout before
	    authentication&quot;</computeroutput> messages, getting
	  nowhere.</para>

	<note>
	  <para>These rules will <emphasis>not</emphasis> block slow
	    bruteforcers, sometimes referred to as <ulink
	      url="http://home.nuug.no/~peter/hailmary2013/">the Hail
	      Mary Cloud</ulink>.</para>
	</note>

	<para>Once again, please keep in mind that this example rule
	  is intended mainly as an illustration.  It is not unlikely
	  that a particular network's needs are better served by
	  rather different rules or combinations of rules.</para>

	<para>If, for example, a generous number of connections in
	  general are wanted, but the desire is to be a little more
	  tight fisted when it comes to
	  <application>ssh</application>, supplement the rule above
	  with something like the one below, early on in the rule
	  set:</para>

	<programlisting>pass quick proto { tcp, udp } from any to any port ssh \
    flags S/SA keep state \
    (max-src-conn 15, max-src-conn-rate 5/3, \
    overload &lt;bruteforce&gt; flush global)</programlisting>

	<para>It should be possible to find the set of parameters
	  which is just right for individual situations by reading the
	  relevant man pages and the
	  <ulink url="http://www.openbsd.org/faq/pf/">PF User
	    Guide</ulink>, and perhaps a bit of
	  experimentation.</para>

	<note>
	  <title>It May Not be Necessary to Block All
	    Overloaders</title>

	  <para>It is probably worth noting at this point that the
	    <emphasis>overload</emphasis> mechanism is a general
	    technique which does not have to apply exclusively to the
	    <emphasis>ssh</emphasis> service, and it is not always
	    optimal to block all traffic from offenders
	    entirely.</para>

	  <para>For example, an overload rule could be used to
	    protect a mail service or a web service, and the overload
	    table could be used in a rule to assign offenders to a
	    queue with a minimal bandwidth allocation or, in the web
	    case, to redirect to a specific web page.</para>
	</note>

	<sect4 id="pftut-expire">
	  <title>Expiring Table Entries with
	    <application>pfctl</application></title>

	  <para>At this point, we have tables which will be filled by
	    our <literal>overload</literal> rules, and since we could
	    reasonably expect our gateways to have months of uptime,
	    the tables will grow incrementally, taking up more memory
	    as time goes by.</para>

	  <para>Sometimes an IP address that was blocked last week due
	    to a brute force attack was in fact a dynamically assigned
	    one, which is now assigned to a different ISP customer who
	    has a legitimate reason to try communicating with hosts in
	    the local network.</para>

	  <para>Situations like these were what caused Henning Brauer
	    to add to <application>pfctl</application> the ability to
	    expire table entries not referenced in a specified number
	    of seconds (in OpenBSD 4.1).  For example, the
	    command</para>

	  <screen>&prompt.root; <userinput>pfctl -t bruteforce -T expire 86400</userinput></screen>

	  <para>will remove <literal>&lt;bruteforce&gt;</literal>
	    table entries which have not been referenced for 86400
	    seconds.</para>
	</sect4>

	<sect4 id="pftut-expiretable">
	  <title>The <application>expiretable</application>
	    Tool</title>

	  <para>Before <application>pfctl</application> acquired the
	    ability to expire table entries, Henrik Gustafsson had
	    written <application>expiretable</application>, which
	    removes table entries which have not been accessed for a
	    specified period of time.</para>

	  <para>One useful example is to use the
	    <application>expiretable</application> program as a way of
	    removing outdated <literal>&lt;bruteforce&gt;</literal>
	    table entries.</para>

	  <para>For example, let
	    <application>expiretable</application> remove
	    <literal>&lt;bruteforce&gt;</literal> table entries older
	    than 24 hours by adding an entry containing the following
	    to <filename>/etc/rc.local</filename>:</para>

	  <programlisting>/usr/local/sbin/expiretable -v -d -t 24h bruteforce</programlisting>

	  <para><application>expiretable</application> is in the
	    Ports&nbsp;Collection on &os; as <filename
	      role="package">security/expiretable</filename>.</para>
	</sect4>
      </sect3>

      <sect3 id="pftut-tools">
	<title>Other <acronym>PF</acronym> Tools</title>

	<para>Over time, a number of tools have been developed which
	  interact with <acronym>PF</acronym> in various ways.</para>

	<sect4 id="pftut-pftop">
	  <title>The <application>pftop</application> Traffic
	    Viewer</title>

	  <para>Can Erkin Acar's <application>pftop</application>
	    makes it possible to keep an eye on what passes into and
	    out of the network.  <application>pftop</application> is
	    available through the ports system as
	    <filename role="package">sysutils/pftop</filename>.  The
	    name is a strong hint at what it does -
	    <application>pftop</application> shows a running snapshot
	    of traffic in a format which is strongly inspired by
	    &man.top.1;.</para>
	</sect4>

	<sect4 id="pftut-spamd">
	  <title>The <application>spamd</application> Spam Deferral
	    Daemon</title>

	  <para>Not to be confused with the
	    <application>spamd</application> daemon which comes
	    bundled with <application>spamassassin</application>, the
	    <acronym>PF</acronym> companion
	    <application>spamd</application> was designed to run on a
	    PF gateway to form part of the outer defense against spam.
	    <application>spamd</application> hooks into the
	    <acronym>PF</acronym> configuration via a set of
	    redirections.</para>

	  <para>The main point underlying the
	    <application>spamd</application> design is the fact that
	    spammers send a large number of messages, and the
	    probability that you are the first person receiving a
	    particular message is incredibly small.  In addition,
	    spam is mainly sent via a few spammer friendly networks
	    and a large number of hijacked machines.  Both the
	    individual messages and the machines will be reported to
	    blacklists fairly quickly, and this is the kind of data
	    <application>spamd</application> can use to our advantage
	    with <firstterm>blacklists</firstterm>.</para>

	  <para>What <application>spamd</application> does to SMTP
	    connections from addresses in the blacklist is to
	    present its banner and immediately switch to a mode
	    where it answers SMTP traffic one byte at the time.  This
	    technique, which is intended to waste as much time as
	    possible on the sending end while costing the receiver
	    pretty much nothing, is called
	    <firstterm>tarpitting</firstterm>.  The specific
	    implementation with one byte SMTP replies is often
	    referred to as <firstterm>stuttering</firstterm>.</para>

	  <sect5 id="pftut-spamd-allblack">
	    <title>A Basic Blacklisting
	      <application>spamd</application></title>

	    <para>Here is the basic procedure for setting up
	      <application>spamd</application> with automatically
	      updated blacklists:</para>

	    <procedure>
	      <step>
		<para>Install the <filename
		    role="package">mail/spamd/</filename> port.  In
		  particular, be sure to read the package message and
		  act upon what it says.  Specifically, to use
		  <application>spamd</application>'s greylisting
		  features, a file descriptor file system (see <ulink
		    url="http://www.freebsd.org/cgi/man.cgi?query=fdescfs&amp;sektion=5">fdescfs(5)</ulink>)
		  must be mounted at <filename>/dev/fd/</filename>.
		  Do this by adding the following line to
		  <filename>/etc/fstab</filename>:</para>

		<programlisting> fdescfs /dev/fd fdescfs rw 0 0</programlisting>

		<para>Make sure the <filename>fdescfs</filename> code
		  is in the kernel, either compiled in or by loading
		  the module with &man.kldload.8;.</para>
	      </step>

	      <step>
		<para>Next, edit the rule set to include</para>

		<programlisting>table &lt;spamd&gt; persist
table &lt;spamd-white&gt; persist
rdr pass on $ext_if inet proto tcp from &lt;spamd&gt; to \
    { $ext_if, $localnet } port smtp -&gt; 127.0.0.1 port 8025
rdr pass on $ext_if inet proto tcp from !&lt;spamd-white&gt; to \
    { $ext_if, $localnet } port smtp -&gt; 127.0.0.1 port 8025</programlisting>

		<para>The two tables &lt;spamd&gt; and
		  &lt;spamd-white&gt; are essential.  SMTP traffic
		  from the addresses in the first table plus the ones
		  which are not in the other table are redirected to a
		  daemon listening at port 8025.</para>
	      </step>

	      <step>
		<para>The next step is to set up
		  <application>spamd</application>'s own configuration
		  in <filename>/usr/local/etc/spamd.conf</filename>
		  supplemented by <filename>rc.conf</filename>
		  parameters.</para>

		<para>The supplied sample file offers quite a bit of
		  explanation, and the man page offers additional
		  information, but we will recap the essentials
		  here.</para>

		<para>One of the first lines without a
		  <literal>#</literal> comment sign at the start
		  contains the block which defines the
		  <literal>all</literal> list, which specifies the
		  lists actually used:</para>

		<programlisting>all:\
    :traplist:whitelist:</programlisting>

		<para>Here, all the desired black lists are added,
		  separated by colons (<literal>:</literal>).  To use
		  whitelists to subtract addresses from the blacklist,
		  add the name of the whitelist immediately after the
		  name of each blacklist, i.e.,
		  <literal>:blacklist:whitelist:</literal>.</para>

		<para>Next up is a blacklist definition:</para>

		<programlisting>traplist:\
    :black:\
    :msg="SPAM. Your address %A has sent spam within the last 24 hours":\
    :method=http:\
    :file=www.openbsd.org/spamd/traplist.gz</programlisting>

		<para>Following the name, the first data field
		  specifies the list type, in this case
		  <literal>black</literal>.  The
		  <literal>msg</literal> field contains the message to
		  display to blacklisted senders during the SMTP
		  dialogue.  The <literal>method</literal> field
		  specifies how spamd-setup fetches the list data,
		  here <literal>http</literal>.  The other options are
		  fetching via <literal>ftp</literal>, from a
		  <literal>file</literal> in a mounted file system or
		  via <literal>exec</literal> of an external program.
		  Finally the <literal>file</literal> field specifies
		  the name of the file spamd expects to
		  receive.</para>

		<para>The definition of a whitelist follows much the
		  same pattern:</para>

		<programlisting>whitelist:\
    :white:\
    :method=file:\
    :file=/var/mail/whitelist.txt</programlisting>

		<para>but omits the message parameters since a
		  message is not needed.</para>

		<tip>
		  <title>Choose Data Sources with Care</title>

		  <para>Using all the blacklists in the sample
		    <filename>spamd.conf</filename> will end up
		    blacklisting large blocks of the Internet,
		    including several Asian nations.  Administrators
		    need to edit the file to end up with an optimal
		    configuration.  The administrator is the judge of
		    which data sources to use, and using lists other
		    than the ones suggested in the sample file is
		    possible.</para>
		</tip>

		<para>Put the lines for spamd and any startup
		  parameters desired in
		  <filename>/etc/rc.conf</filename>, for
		  example:</para>

		<programlisting>spamd_flags="-v" # for normal use: "" and see spamd-setup(8)</programlisting>

		<para>When done with editing the setup,
		  reload the rule set, start
		  <application>spamd</application> with the options
		  desired using the
		  <filename>/usr/local/etc/rc.d/obspamd</filename>
		  script, and complete the configuration using
		  <command>spamd-setup</command>.  Finally, create a
		  &man.cron.8; job which calls
		  <command>spamd-setup</command> to update the tables
		  at reasonable intervals.</para>
	      </step>
	    </procedure>

	    <para>On a typical gateway in front of a mail server,
	      hosts will start getting trapped within a few seconds to
	      several minutes.</para>
	  </sect5>

	  <sect5 id="pftut-spamd-greylist">
	    <title>Adding Greylisting to the
	      <application>spamd</application> Setup</title>

	    <para><application>spamd</application> also supports
	      <firstterm>greylisting</firstterm>, which works by
	      rejecting messages from unknown hosts temporarily with
	      <replaceable>45n</replaceable> codes, letting messages
	      from hosts which try again within a reasonable time
	      through.  Traffic from well behaved hosts, that is,
	      senders which are set up to behave within the limits set
	      up in the relevant RFCs
	      <footnote><para>The relevant RFCs are mainly RFC1123
		  and RFC2821.</para></footnote>, will be let
	      through.</para>

	    <para>Greylisting as a technique was presented in a 2003
	      paper by Evan Harris
	      <footnote><para>The original
		  Harris paper and a number of other useful articles
		  and resources can be found at the <ulink
		    url="http://www.greylisting.org/">greylisting.org</ulink>
		  web site.</para></footnote>, and a number of
	      implementations followed over the next few months.
	      OpenBSD's <application>spamd</application> acquired its
	      ability to greylist in OpenBSD 3.5, which was released
	      in May 2004.</para>

	    <para>The most amazing thing about greylisting, apart
	      from its simplicity, is that it still works.  Spammers
	      and malware writers have been very slow to adapt.</para>

	    <para>The basic procedure for adding greylisting to your
	      setup follows below.</para>

	    <procedure>
	      <step>
		<para>If not done already, make sure the
		  file descriptor file system (see &man.fdescfs.5;) is
		  mounted at <filename>/dev/fd/</filename>.  Do this
		  by adding the following line to
		  <filename>/etc/fstab</filename>:</para>

		<programlisting>fdescfs /dev/fd fdescfs rw 0 0</programlisting>

		<para>and make sure the &man.fdescfs.5; code is in the
		  kernel, either compiled in or by loading the module
		  with &man.kldload.8;.</para>
	      </step>

	      <step>
		<para>To run <application>spamd</application> in
		  greylisting mode, <filename>/etc/rc.conf</filename>
		  must be changed slightly by adding</para>

		<programlisting>spamd_grey="YES"  # use spamd greylisting if YES</programlisting>

		<para>Several greylisting related parameters can be
		  fine-tuned with <command>spamd</command>'s command
		  line parameters and the corresponding
		  <filename>/etc/rc.conf</filename> settings.  Check
		  the <application>spamd</application> man page to see
		  what the parameters mean.</para>
	      </step>

	      <step>
		<para>To complete the greylisting setup, restart
		  <application>spamd</application> using the
		  <filename>/usr/local/etc/rc.d/obspamd</filename>
		  script.</para>
	      </step>
	    </procedure>

	    <para>Behind the scenes, rarely mentioned and barely
	      documented are two of <application>spamd</application>'s
	      helpers, the <application>spamdb</application> database
	      tool and the <application>spamlogd</application>
	      whitelist updater, which both perform essential
	      functions for the greylisting feature.  Of the two
	      <application>spamlogd</application> works quietly in the
	      background, while <application>spamdb</application> has
	      been developed to offer some interesting
	      features.</para>

	    <note>
	      <title>Restart <application>spamd</application> to
		Enable Greylisting</title>

	      <para>After following all steps in the tutorial
		exactly up to this point,
		<application>spamlogd</application> has been started
		automatically already.  However, if the initial
		<application>spamd</application> configuration did not
		include greylisting,
		<application>spamlogd</application> may not have been
		started, and there may be strange symptoms, such as
		greylists and whitelists not getting updated
		properly.</para>

	      <para>Under normal circumstances, it should not be
		necessary to start <application>spamlogd</application>
		by hand.  Restarting <application>spamd</application>
		after enabling greylisting ensures
		<application>spamlogd</application> is loaded and
		available too.</para>
	    </note>

	    <para><application>spamdb</application> is the
	      administrator's main interface to managing the black,
	      grey and white lists via the contents of the
	      <filename>/var/db/spamdb</filename> database.</para>
	  </sect5>
	</sect4>

	<sect4 id="pftut-hygiene">
	  <title>Network Hygiene: Blocking, Scrubbing and so
	    On</title>

	  <para>Our gateway does not feel quite complete without a few
	    more items in the configuration which will make it behave
	    a bit more sanely towards hosts on the wide net and our
	    local network.</para>

	  <sect5 id="pftut-blockpolicy">
	    <title><literal>block-policy</literal></title>

	    <para><literal>block-policy</literal> is an option which
	      can be set in the <literal>options</literal> part of the
	      ruleset, which precedes the redirection and filtering
	      rules.  This option determines which feedback, if any,
	      <acronym>PF</acronym> will give to hosts which try to
	      create connections which are subsequently blocked.  The
	      option has two possible values, <literal>drop</literal>,
	      which drops blocked packets with no feedback, and
	      <literal>return</literal>, which returns with status
	      codes such as <computeroutput>Connection
		refused</computeroutput> or similar.</para>

	    <para>The correct strategy for block policies has been the
	      subject of rather a lot of discussion.  We choose to
	      play nicely and instruct our firewall to issue
	      returns:</para>

	    <programlisting>set block-policy return</programlisting>
	  </sect5>

	  <sect5 id="pftut-scrub">
	    <title><literal>scrub</literal></title>

	    <para>In <acronym>PF</acronym> versions up to OpenBSD 4.5
	      inclusive, <literal>scrub</literal> is a keyword which
	      enables network packet normalization, causing fragmented
	      packets to be assembled and removing ambiguity.
	      Enabling <literal>scrub</literal> provides a measure of
	      protection against certain kinds of attacks based on
	      incorrect handling of packet fragments.  A number of
	      supplementing options are available, but we choose the
	      simplest form which is suitable for most
	      configurations.</para>

	    <programlisting>scrub in all</programlisting>

	    <para>Some services, such as NFS, require some specific
	      fragment handling options.  This is extensively
	      documented in the <acronym>PF</acronym> user guide and
	      man pages provide all the information you could
	      need.</para>

	    <para>One fairly common example is this,</para>

	    <programlisting>scrub in all fragment reassemble no-df max-mss 1440</programlisting>

	    <para>meaning, we reassemble fragments, clear the
	      <quote>do not fragment</quote> bit and set the maximum
	      segment size to 1440 bytes.  Other variations are
	      possible, and you should be able to cater to various
	      specific needs by consulting the man pages and some
	      experimentation.</para>
	  </sect5>

	  <sect5 id="pftut-antispoof">
	    <title><literal>antispoof</literal></title>

	    <para><literal>antispoof</literal> is a common special
	      case of filtering and blocking.  This mechanism protects
	      against activity from spoofed or forged IP addresses,
	      mainly by blocking packets appearing on interfaces and
	      in directions which are logically not possible.</para>

	    <para>We specify that we want to weed out spoofed traffic
	      coming in from the rest of the world and any spoofed
	      packets which, however unlikely, were to originate in
	      our own network:</para>

	    <programlisting>antispoof for $ext_if
antispoof for $int_if</programlisting>
	  </sect5>

	  <sect5 id="pftut-unrouteables">
	    <title>Handling Non-Routable Addresses from
	      Elsewhere</title>

	    <para>Even with a properly configured gateway to handle
	      network address translation for your own network, you
	      may find yourself in the unenviable position of having
	      to compensate for other people's
	      misconfigurations.</para>

	    <para>One depressingly common class of misconfigurations
	      is the kind which lets traffic with non-routable
	      addresses out to the Internet.  Traffic from
	      non-routeable addresses have also played a part in
	      several DOS attack techniques, so it may be worth
	      considering explicitly blocking traffic from
	      non-routeable addresses from entering your
	      network.</para>

	    <para>One possible solution is the one outlined below,
	      which for good measure also blocks any attempt to
	      initiate contact to non-routable addresses through the
	      gateway's external interface:</para>

	    <programlisting>martians = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, \
	      10.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, \
	      0.0.0.0/8, 240.0.0.0/4 }"

block drop in quick on $ext_if from $martians to any
block drop out quick on $ext_if from any to $martians</programlisting>

	    <para>Here, the <literal>martians</literal> macro denotes
	      the RFC 1918 addresses and a few other ranges which are
	      mandated by various RFCs not to be in circulation on the
	      open Internet.  Traffic to and from such addresses is
	      quietly dropped on the gateway's external
	      interface.</para>

	    <para>The specific details of how to implement this kind
	      of protection will vary, among other things according to
	      your specific network configuration.  Your network
	      design could for example dictate that you include or
	      exclude other address ranges than these.</para>

	    <para>This completes our simple NATing firewall for a
	      small local network.  A more thorough tutorial is
	      available at <ulink
		url="http://home.nuug.no/~peter/pf/">http://home.nuug.no/~peter/pf/</ulink>,
	      where you will also find slides from related
	      presentations.</para>
	  </sect5>
	</sect4>
      </sect3>
    </sect2>
  </sect1>

  <sect1 id="firewalls-ipf">
    <title>The IPFILTER (IPF) Firewall</title>

    <indexterm>
      <primary>firewall</primary>

      <secondary>IPFILTER</secondary>
    </indexterm>

    <para>IPFILTER is a cross-platform, open source firewall which
      has been ported to &os;, NetBSD, OpenBSD, &sunos;, HP/UX, and
      &solaris; operating systems.</para>

    <para>IPFILTER is based on a kernel-side firewall and
      <acronym>NAT</acronym> mechanism that can be controlled and
      monitored by userland interface programs.  The firewall rules
      can be set or deleted using &man.ipf.8;.  The
      <acronym>NAT</acronym> rules can be set or deleted using
      &man.ipnat.8;.  Run-time statistics for the kernel parts of
      IPFILTER can be printed using &man.ipfstat.8;.  To log IPFILTER
      actions to the system log files, use &man.ipmon.8;.</para>

    <para>IPF was originally written using a rule processing logic
      of <quote>the last matching rule wins</quote> and only used
      stateless rules.  Over time, IPF has been enhanced to include a
      <quote>quick</quote> option and a stateful
      <quote>keep state</quote> option which modernized the rules
      processing logic.  IPF's official documentation covers only the
      legacy rule coding parameters and rule file processing logic and
      the modernized functions are only included as additional
      options.</para>

    <para>The instructions contained in this section are based on
      using rules that contain <quote>quick</quote> and
      <quote>keep state</quote> as these provide the basic framework
      for configuring an inclusive firewall ruleset.</para>

    <para>For a detailed explanation of the legacy rules processing
      method, refer to <ulink
	url="http://www.munk.me.uk/ipf/ipf-howto.html"></ulink>
      and <ulink
	url="http://coombs.anu.edu.au/~avalon/ip-filter.html"></ulink>.</para>

    <para>The IPF FAQ is at <ulink
	url="http://www.phildev.net/ipf/index.html"></ulink>.</para>

    <para>A searchable archive of the IPFilter mailing list is
      available at <ulink
	url="http://marc.theaimsgroup.com/?l=ipfilter"></ulink>.</para>

    <sect2>
      <title>Enabling IPF</title>

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>enabling</secondary>
      </indexterm>

      <para>IPF is included in the basic &os; install as a kernel
	loadable module.  The system will dynamically load
	this module at boot time when
	<varname>ipfilter_enable="YES"</varname> is added to
	<filename>rc.conf</filename>.  The module enables logging and
	<literal>default pass all</literal>.  To change the
	default to <literal>block all</literal>, add a
	<literal>block all</literal> rule at the end of the
	ruleset.</para>
    </sect2>

    <sect2>
      <title>Kernel Options</title>

      <indexterm>
	<primary>kernel options</primary>

	<secondary>IPFILTER</secondary>
      </indexterm>

      <indexterm>
	<primary>kernel options</primary>

	<secondary>IPFILTER_LOG</secondary>
      </indexterm>

      <indexterm>
	<primary>kernel options</primary>

	<secondary>IPFILTER_DEFAULT_BLOCK</secondary>
      </indexterm>

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>kernel options</secondary>
      </indexterm>

      <para>For users who prefer to statically compile IPF support
	into a custom kernel, the following IPF option statements,
	listed in <filename>/usr/src/sys/conf/NOTES</filename>, are
	available:</para>

      <programlisting>options IPFILTER
options IPFILTER_LOG
options IPFILTER_DEFAULT_BLOCK</programlisting>

      <para><literal>options IPFILTER</literal> enables support for
	the <quote>IPFILTER</quote> firewall.</para>

      <para><literal>options IPFILTER_LOG</literal> enables IPF
	logging using the <devicename>ipl</devicename> packet logging
	pseudo&mdash;device for every rule that has the
	<literal>log</literal> keyword.</para>

      <para><literal>options IPFILTER_DEFAULT_BLOCK</literal> changes
	the default behavior so that any packet not matching a
	firewall <literal>pass</literal> rule gets blocked.</para>

      <para>These settings will take effect only after installing a
	kernel that has been built with the above options set.</para>
    </sect2>

    <sect2>
      <title>Available <filename>rc.conf</filename> Options</title>

      <para>To activate IPF at boot time, the following statements
	need to be added to <filename>/etc/rc.conf</filename>:</para>

      <programlisting>ipfilter_enable="YES"             # Start ipf firewall
ipfilter_rules="/etc/ipf.rules"   # loads rules definition text file
ipmon_enable="YES"                # Start IP monitor log
ipmon_flags="-Ds"                 # D = start as daemon
                                  # s = log to syslog
                                  # v = log tcp window, ack, seq
                                  # n = map IP &amp; port to names</programlisting>

      <para>If there is a LAN behind the firewall that uses the
	reserved private IP address ranges, the following lines have
	to be added to enable <acronym>NAT</acronym>
	functionality:</para>

      <programlisting>gateway_enable="YES"              # Enable as LAN gateway
ipnat_enable="YES"                # Start ipnat function
ipnat_rules="/etc/ipnat.rules"    # rules definition file for ipnat</programlisting>
    </sect2>

    <sect2>
      <title>IPF</title>

      <indexterm><primary><command>ipf</command></primary></indexterm>

      <para>To load the ruleset file, use &man.ipf.8;.  Custom rules
	are normally placed in a file, and the following command can
	be used to replace the currently running firewall
	rules:</para>

      <screen>&prompt.root; <userinput>ipf -Fa -f /etc/ipf.rules</userinput></screen>

      <para><option>-Fa</option> flushes all the internal rules
	tables.</para>

      <para><option>-f</option> specifies the file containing the
	rules to load.</para>

      <para>This provides the ability to make changes to a custom
	rules file, run the above IPF command, and thus update the
	running firewall with a fresh copy of the rules without having
	to reboot the system.  This method is convenient for testing
	new rules as the procedure can be executed as many times as
	needed.</para>

      <para>Refer to &man.ipf.8; for details on the other flags
	available with this command.</para>

      <para>&man.ipf.8; expects the rules file to be a standard text
	file.  It will not accept a rules file written as a script
	with symbolic substitution.</para>

      <para>There is a way to build IPF rules that utilize the power
	of script symbolic substitution.  For more information, see
	<xref linkend="firewalls-ipf-rules-script"/>.</para>
    </sect2>

    <sect2>
      <title>IPFSTAT</title>

      <indexterm><primary><command>ipfstat</command></primary></indexterm>

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>statistics</secondary>
      </indexterm>

      <para>The default behavior of &man.ipfstat.8; is to retrieve
	and display the totals of the accumulated statistics gathered
	by applying the rules against packets going in and out of the
	firewall since it was last started, or since the last time the
	accumulators were reset to zero using <command>ipf
	  -Z</command>.</para>

      <para>Refer to &man.ipfstat.8; for details.</para>

      <para>The default &man.ipfstat.8; output will look something
	like this:</para>

      <screen>input packets: blocked 99286 passed 1255609 nomatch 14686 counted 0
 output packets: blocked 4200 passed 1284345 nomatch 14687 counted 0
 input packets logged: blocked 99286 passed 0
 output packets logged: blocked 0 passed 0
 packets logged: input 0 output 0
 log failures: input 3898 output 0
 fragment state(in): kept 0 lost 0
 fragment state(out): kept 0 lost 0
 packet state(in): kept 169364 lost 0
 packet state(out): kept 431395 lost 0
 ICMP replies: 0 <acronym>TCP</acronym> RSTs sent: 0
 Result cache hits(in): 1215208 (out): 1098963
 IN Pullups succeeded: 2 failed: 0
 OUT Pullups succeeded: 0 failed: 0
 Fastroute successes: 0 failures: 0
 <acronym>TCP</acronym> cksum fails(in): 0 (out): 0
 Packet log flags set: (0)</screen>

      <para>When supplied with either <option>-i</option> for inbound
	or <option>-o</option> for outbound, the command will retrieve
	and display the appropriate list of filter rules currently
	installed and in use by the kernel.</para>

      <para><command>ipfstat -in</command> displays the inbound
	internal rules table with rule numbers.</para>

      <para><command>ipfstat -on</command> displays the outbound
	internal rules table with rule numbers.</para>

      <para>The output will look something like this:</para>

      <screen>@1 pass out on xl0 from any to any
@2 block out on dc0 from any to any
@3 pass out quick on dc0 proto tcp/udp from any to any keep state</screen>

      <para><command>ipfstat -ih</command> displays the inbound
	internal rules table, prefixing each rule with a count of how
	many times the rule was matched.</para>

      <para><command>ipfstat -oh</command> displays the outbound
	internal rules table, prefixing each rule with a count of how
	many times the rule was matched.</para>

      <para>The output will look something like this:</para>

      <screen>2451423 pass out on xl0 from any to any
354727 block out on dc0 from any to any
430918 pass out quick on dc0 proto tcp/udp from any to any keep state</screen>

      <para>One of the most important options of
	<command>ipfstat</command> is <option>-t</option> which
	displays the state table in a way similar to how &man.top.1;
	shows the &os; running process table.  When a firewall is
	under attack, this function provides the ability to identify
	and see the attacking packets.  The optional sub-flags give
	the ability to select the destination or source IP, port, or
	protocol to be monitored in real time.  Refer to
	&man.ipfstat.8; for details.</para>
    </sect2>

    <sect2>
      <title>IPMON</title>

      <indexterm><primary><command>ipmon</command></primary></indexterm>

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>logging</secondary>
      </indexterm>

      <para>In order for <command>ipmon</command> to work properly,
	the kernel option <literal>IPFILTER_LOG</literal> must be
	turned on.  This command has two different modes.  Native mode
	is the default mode when the command is used without
	<option>-D</option>.</para>

      <para>Daemon mode provides a continuous system log file so that
	logging of past events may be reviewed.  &os; has a built in
	facility to automatically rotate system logs.  This is why
	outputting the log information to &man.syslogd.8; is better
	than the default of outputting to a regular file.  The default
	<filename>rc.conf</filename>
	<literal>ipmon_flags</literal> statement uses
	<option>-Ds</option>:</para>

      <programlisting>ipmon_flags="-Ds" # D = start as daemon
                  # s = log to syslog
                  # v = log tcp window, ack, seq
                  # n = map IP &amp; port to names</programlisting>

      <para>Logging provides the ability to review, after the fact,
	information such as which packets were dropped, what addresses
	they came from and where they were going.  These can all
	provide a significant edge in tracking down attackers.</para>

      <para>Even with the logging facility enabled, IPF will not
	generate any rule logging by default.  The firewall
	administrator decides which rules in the ruleset should be
	logged and adds the log keyword to those rules.  Normally,
	only deny rules are logged.</para>

      <para>It is customary to include a <quote>default deny
	  everything</quote> rule with the log keyword included as the
	last rule in the ruleset.  This makes it possible to see all
	the packets that did not match any of the rules in the
	ruleset.</para>
    </sect2>

    <sect2>
      <title>IPMON Logging</title>

      <para>&man.syslogd.8; uses its own method for segregation of log
	data.  It uses groupings called <quote>facility</quote> and
	<quote>level</quote>.  By default, IPMON in
	<option>-Ds</option> mode uses <literal>local0</literal> as
	the <quote>facility</quote> name.  The following levels can be
	used to further segregate the logged data:</para>

      <screen>LOG_INFO - packets logged using the "log" keyword as the action rather than pass or block.
LOG_NOTICE - packets logged which are also passed
LOG_WARNING - packets logged which are also blocked
LOG_ERR - packets which have been logged and which can be considered short</screen>

      <!-- XXX: "can be considered short" == "with incomplete header" -->

      <para>In order to setup IPFILTER to log all data to
	<filename>/var/log/ipfilter.log</filename>, first
	create the empty file:</para>

       <screen>&prompt.root; <userinput>touch /var/log/ipfilter.log</userinput></screen>

      <para>&man.syslogd.8; is controlled by definition statements in
	<filename>/etc/syslog.conf</filename>.  This file offers
	considerable flexibility in how
	<application>syslog</application> will deal with system
	messages issued by software applications like IPF.</para>

      <para>To write all logged messages to the specified file,
	add the following statement to
	<filename>/etc/syslog.conf</filename>:</para>

      <programlisting>local0.* /var/log/ipfilter.log</programlisting>

      <para>To activate the changes and instruct &man.syslogd.8;
	to read the modified <filename>/etc/syslog.conf</filename>,
	run <command>service syslogd reload</command>.</para>

      <para>Do not forget to change
	<filename>/etc/newsyslog.conf</filename> to rotate the new
	log file.</para>
    </sect2>

    <sect2>
      <title>The Format of Logged Messages</title>

      <para>Messages generated by <command>ipmon</command> consist
	of data fields separated by white space.  Fields common to
	all messages are:</para>

      <orderedlist>
	<listitem>
	  <para>The date of packet receipt.</para>
	</listitem>

	<listitem>
	  <para>The time of packet receipt.  This is in the form
	    HH:MM:SS.F, for hours, minutes, seconds, and fractions
	    of a second.</para>
	</listitem>

	<listitem>
	  <para>The name of the interface that processed the
	    packet.</para>
	</listitem>

	<listitem>
	  <para>The group and rule number of the rule in the format
	    <literal>@0:17</literal>.</para>
	</listitem>
      </orderedlist>

      <para>These can be viewed with
	<command>ipfstat -in</command>.</para>

      <orderedlist>
	<listitem>
	  <para>The action: <literal>p</literal> for passed,
	    <literal>b</literal> for blocked, <literal>S</literal> for
	    a short packet, <literal>n</literal> did not match any
	    rules, and <literal>L</literal> for a log rule.  The order
	    of precedence in showing flags is: <literal>S</literal>,
	    <literal>p</literal>, <literal>b</literal>,
	    <literal>n</literal>, <literal>L</literal>.  A capital
	    <literal>P</literal> or <literal>B</literal> means that
	    the packet has been logged due to a global logging
	    setting, not a particular rule.</para>
	</listitem>

	<listitem>
	  <para>The addresses written as three fields: the source
	    address and port separated by a comma, the -&gt; symbol,
	    and the destination address and port.  For example:
	    <literal>209.53.17.22,80 -&gt;
	      198.73.220.17,1722</literal>.</para>
	</listitem>

	<listitem>
	  <para><literal>PR</literal> followed by the protocol name
	    or number: for example, <literal>PR tcp</literal>.</para>
	</listitem>

	<listitem>
	  <para><literal>len</literal> followed by the header length
	    and total length of the packet: for example,
	    <literal>len 20 40</literal>.</para>
	</listitem>
      </orderedlist>

      <para>If the packet is a <acronym>TCP</acronym> packet, there
	will be an additional field starting with a hyphen followed by
	letters corresponding to any flags that were set.  Refer to
	&man.ipf.5; for a list of letters and their flags.</para>

      <para>If the packet is an ICMP packet, there will be two fields
	at the end:  the first always being <quote>ICMP</quote> and
	the next being the ICMP message and sub-message type,
	separated by a slash.  For example:  ICMP 3/3 for a port
	unreachable message.</para>
    </sect2>

    <sect2 id="firewalls-ipf-rules-script">
      <title>Building the Rule Script with Symbolic
	Substitution</title>

      <para>Some experienced IPF users create a file containing the
	rules and code them in a manner compatible with running them
	as a script with symbolic substitution.  The major benefit
	of doing this is that only the value associated with the
	symbolic name needs to be changed, and when the script is
	run all the rules containing the symbolic name will have the
	value substituted in the rules.  Being a script, symbolic
	substitution can be used to code frequently used values and
	substitute them in multiple rules.  This can be seen in the
	following example.</para>

      <para>The script syntax used here is compatible with the
	&man.sh.1;, &man.csh.1;, and &man.tcsh.1; shells.</para>

      <para>Symbolic substitution fields are prefixed with a
	<literal>&dollar;</literal>.</para>

      <para>Symbolic fields do not have the &dollar; prefix.</para>

      <para>The value to populate the symbolic field must be enclosed
	between double quotes (<literal>"</literal>).</para>

      <para>Start the rule file with something like this:</para>

      <programlisting>############# Start of IPF rules script ########################

oif="dc0"            # name of the outbound interface
odns="192.0.2.11"    # ISP's DNS server IP address
myip="192.0.2.7"     # my static IP address from ISP
ks="keep state"
fks="flags S keep state"

# You can choose between building /etc/ipf.rules file
# from this script or running this script "as is".
#
# Uncomment only one line and comment out another.
#
# 1) This can be used for building /etc/ipf.rules:
#cat &gt; /etc/ipf.rules &lt;&lt; EOF
#
# 2) This can be used to run script "as is":
/sbin/ipf -Fa -f - &lt;&lt; EOF

# Allow out access to my ISP's Domain name server.
pass out quick on &dollar;oif proto tcp from any to &dollar;odns port = 53 &dollar;fks
pass out quick on &dollar;oif proto udp from any to &dollar;odns port = 53 &dollar;ks

# Allow out non-secure standard www function
pass out quick on &dollar;oif proto tcp from &dollar;myip to any port = 80 &dollar;fks

# Allow out secure www function https over TLS SSL
pass out quick on &dollar;oif proto tcp from &dollar;myip to any port = 443 &dollar;fks
EOF
################## End of IPF rules script ########################</programlisting>

      <para>The rules are not important in this example as it instead
	focuses on how the symbolic substitution fields are populated.
	If this example was in a file named
	<filename>/etc/ipf.rules.script</filename>, these rules could
	be reloaded by running:</para>

      <screen>&prompt.root; <userinput>sh /etc/ipf.rules.script</userinput></screen>

      <para>There is one problem with using a rules file with embedded
	symbolics: IPF does not understand symbolic substitution, and
	cannot read such scripts directly.</para>

      <para>This script can be used in one of two ways:</para>

      <itemizedlist>
	<listitem>
	  <para>Uncomment the line that begins with
	    <literal>cat</literal>, and comment out the line that
	    begins with <literal>/sbin/ipf</literal>.  Place
	    <literal>ipfilter_enable="YES"</literal> into
	    <filename>/etc/rc.conf</filename>, and run the script
	    once after each modification to create or update
	    <filename>/etc/ipf.rules</filename>.</para>
	</listitem>

	<listitem>
	  <para>Disable IPFILTER in the system startup scripts by
	    adding <literal>ipfilter_enable="NO"</literal>to
	    <filename>/etc/rc.conf</filename>.</para>

	  <para>Then, add a script like the following to <filename
	      class="directory">/usr/local/etc/rc.d/</filename>.
	    The script should have an obvious name like
	    <filename>ipf.loadrules.sh</filename>, where the
	    <filename>.sh</filename> extension is mandatory.</para>

	  <programlisting>#!/bin/sh
sh /etc/ipf.rules.script</programlisting>

	  <para>The permissions on this script file must be read,
	    write, execute for owner <username>root</username>:</para>

	  <screen>&prompt.root; <userinput>chmod 700 /usr/local/etc/rc.d/ipf.loadrules.sh</userinput></screen>
	</listitem>
      </itemizedlist>

      <para>Now, when the system boots, the IPF rules will be
	loaded.</para>
    </sect2>

    <sect2>
      <title>IPF Rulesets</title>

      <para>A ruleset contains a group of IPF rules which pass or
	block packets based on the values contained in the packet.
	The bi-directional exchange of packets between hosts comprises
	a session conversation.  The firewall ruleset processes both
	the packets arriving from the public Internet, as well as the
	packets produced by the system as a response to them.
	Each <acronym>TCP/IP</acronym> service is predefined by its
	protocol and listening port.  Packets destined for a specific
	service originate from the source address using an
	unprivileged port and target the specific service port on the
	destination address.  All the above parameters can be used as
	selection criteria to create rules which will pass or block
	services.</para>

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>rule processing order</secondary>
      </indexterm>

      <warning>
	<para>When working with the firewall rules, be <emphasis>very
	    careful</emphasis>.  Some configurations <emphasis>can
	  lock the administrator out</emphasis> of the server.  To be
	  on the safe side, consider performing the initial firewall
	  configuration from the local console rather than doing it
	  remotely over <application>ssh</application>.</para>
      </warning>
    </sect2>

    <sect2>
      <title>Rule Syntax</title>

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>rule syntax</secondary>
      </indexterm>

      <para>The rule syntax presented here has been simplified to
	only address the modern stateful rule context and <quote>first
	matching rule wins</quote> logic.  For the complete legacy
	rule syntax, refer to &man.ipf.8;.</para>

      <para>A <literal>#</literal> character is used to mark the
	start of a comment and may appear at the end of a rule line
	or on its own line.  Blank lines are ignored.</para>

      <para>Rules contain keywords which must be written in a specific
	order from left to right on the line.  Keywords are identified
	in bold type.  Some keywords have sub-options which may be
	keywords themselves and also include more sub-options.  Each
	of the headings in the below syntax has a bold section header
	which expands on the content.</para>

      <!-- This section is probably wrong. See the OpenBSD flag -->
      <!-- What is the "OpenBSD flag"?  Reference please -->

      <para><replaceable>ACTION IN-OUT OPTIONS SELECTION STATEFUL
	  PROTO SRC_ADDR,DST_ADDR OBJECT PORT_NUM TCP_FLAG
	  STATEFUL</replaceable></para>

      <para><replaceable>ACTION</replaceable> = block | pass</para>

      <para><replaceable>IN-OUT</replaceable> = in | out</para>

      <para><replaceable>OPTIONS</replaceable> = log | quick | on
	interface-name</para>

      <para><replaceable>SELECTION</replaceable> = proto value |
	source/destination IP | port = number | flags
	flag-value</para>

      <para><replaceable>PROTO</replaceable> = tcp/udp | udp | tcp |
	icmp</para>

      <para><replaceable>SRC_ADD,DST_ADDR</replaceable> = all | from
	object to object</para>

      <para><replaceable>OBJECT</replaceable> = IP address |
	any</para>

      <para><replaceable>PORT_NUM</replaceable> = port number</para>

      <para><replaceable>TCP_FLAG</replaceable> = S</para>

      <para><replaceable>STATEFUL</replaceable> = keep state</para>

      <sect3>
	<title>ACTION</title>

	<para>The action keyword indicates what to do with the packet
	  if it matches the rest of the filter rule.  Each rule
	  <emphasis>must</emphasis> have an action.  The following
	  actions are recognized:</para>

	<para><literal>block</literal> indicates that the packet
	  should be dropped if the selection parameters match the
	  packet.</para>

	<para><literal>pass</literal> indicates that the packet should
	  exit the firewall if the selection parameters match the
	  packet.</para>
      </sect3>

      <sect3>
	<title>IN-OUT</title>

	<para>A mandatory requirement is that each filter rule
	  explicitly state which side of the I/O it is to be used
	  on.  The next keyword must be either <literal>in</literal>
	  or <literal>out</literal> and one or the other has to be
	  included or the rule will not pass syntax checks.</para>

	<para><literal>in</literal> means this rule is being applied
	  against an inbound packet which has just been received on
	  the interface facing the public Internet.</para>

	<para><literal>out</literal> means this rule is being applied
	  against an outbound packet destined for the interface facing
	  the public Internet.</para>
      </sect3>

      <sect3>
	<title>OPTIONS</title>

	<note>
	  <para>These options must be used in the order shown
	    here.</para>
	</note>

	<para><literal>log</literal> indicates that the packet header
	  will be written to the &man.ipl.4; packet log pseudo-device
	  if the selection parameters match the packet.</para>

	<para><literal>quick</literal> indicates that if the selection
	  parameters match the packet, this rule will be the last
	  rule checked, and no further processing of any following
	  rules will occur for this packet.</para>

	<para><literal>on</literal> indicates the interface name to
	  be incorporated into the selection parameters.  Interface
	  names are as displayed by &man.ifconfig.8;.  Using this
	  option, the rule will only match if the packet is going
	  through that interface in the specified direction.</para>

	<para>When a packet is logged, the headers of the packet are
	  written to the &man.ipl.4; packet logging pseudo-device.
	  Immediately following the <literal>log</literal> keyword,
	  the following qualifiers may be used in this order:</para>

	<para><literal>body</literal> indicates that the first 128
	  bytes of the packet contents will be logged after the
	  headers.</para>

	<para><literal>first</literal>.  If the <literal>log</literal>
	  keyword is being used in conjunction with a <literal>keep
	    state</literal> option, this option is recommended so that
	  only the triggering packet is logged and not every packet
	  which matches the stateful connection.</para>
      </sect3>

      <sect3>
	<title>SELECTION</title>

	<para>The keywords described in this section are used to
	  describe attributes of the packet to be checked when
	  determining whether or not rules match.  There is a
	  keyword subject, and it has sub-option keywords, one of
	  which has to be selected.  The following general-purpose
	  attributes are provided for matching, and must be used in
	  this order:</para>
      </sect3>

      <sect3>
	<title>PROTO</title>

	<para><literal>proto</literal> is the subject keyword which
	  must include one of its corresponding keyword sub-option
	  values.  The sub-option indicates a specific protocol to be
	  matched against.</para>

	<para><literal>tcp/udp | udp | tcp | icmp</literal> or any
	  protocol names found in <filename>/etc/protocols</filename>
	  are recognized and may be used.  The special protocol
	  keyword <literal>tcp/udp</literal> may be used to match
	  either a <acronym>TCP</acronym> or a <acronym>UDP</acronym>
	  packet, and has been added as a convenience to save
	  duplication of otherwise identical rules.</para>
      </sect3>

      <sect3>
	<title>SRC_ADDR/DST_ADDR</title>

	<para>The <literal>all</literal> keyword is equivalent to
	  <quote>from any to any</quote> with no other match
	  parameters.</para>

	<para><literal>from | to src to dst</literal>: the
	  <literal>from</literal> and <literal>to</literal>
	  keywords are used to match against IP addresses.  Rules
	  must specify <emphasis>both</emphasis> the source and
	  destination parameters.  <literal>any</literal> is a special
	  keyword that matches any IP address.  Examples include:
	  <literal>from any to any</literal>, <literal>from 0.0.0.0/0
	    to any</literal>, <literal>from any to
	    0.0.0.0/0</literal>, <literal>from 0.0.0.0 to
	    any</literal>, and <literal>from any to
	    0.0.0.0</literal>.</para>

	<para>There is no way to match ranges of IP addresses which
	  do not express themselves easily using the dotted numeric
	  form / mask-length notation.  The <filename
	    role="package">net-mgmt/ipcalc</filename> port may be
	  used to ease the calculation.  Additional information
	  is available at the utility's web page: <ulink
	    url="http://jodies.de/ipcalc"></ulink>.</para>
      </sect3>

      <sect3>
	<title>PORT</title>

	<para>If a port match is included, for either or both of
	  source and destination, it is only applied to
	  <acronym>TCP</acronym> and <acronym>UDP</acronym> packets.
	  When composing port comparisons, either the service name
	  from <filename>/etc/services</filename> or an integer port
	  number may be used.  When the port appears as part of the
	  <literal>from</literal> object, it matches the source port
	  number.  When it appears as part of the
	  <literal>to</literal> object, it matches the destination
	  port number.  An example usage is <literal>from any to any
	    port = 80</literal></para>

	<para>Single port comparisons may be done in a number of ways,
	  using a number of different comparison operators.  Instead
	  of the <literal>=</literal> shown in the example above,
	  the following operators may be used: <literal>!=</literal>,
	  <literal>&lt;</literal>, <literal>&gt;</literal>,
	  <literal>&lt;=</literal>, <literal>&gt;=</literal>,
	  <literal>eq</literal>, <literal>ne</literal>,
	  <literal>lt</literal>, <literal>gt</literal>,
	  <literal>le</literal>, and <literal>ge</literal>.</para>

	<para>To specify port ranges, place the two port numbers
	  between <literal>&lt;&gt;</literal> or
	  <literal>&gt;&lt;</literal></para>
      </sect3>

      <sect3>
	<title><acronym>TCP</acronym>_FLAG</title>

	<para>Flags are only effective for <acronym>TCP</acronym>
	  filtering.  The letters represent one of the possible flags
	  that can be matched against the <acronym>TCP</acronym>
	  packet header.</para>

	<para>The modernized rules processing logic uses the
	  <literal>flags S</literal> parameter to identify the TCP
	  session start request.</para>
      </sect3>

      <sect3>
	<title>STATEFUL</title>

	<para><literal>keep state</literal> indicates that on a pass
	  rule, any packets that match the rules selection parameters
	  should activate the stateful filtering facility.</para>
      </sect3>
    </sect2>

    <sect2>
      <title>Stateful Filtering</title>

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>stateful filtering</secondary>
      </indexterm>

      <!-- XXX: duplicated -->

      <para>Stateful filtering treats traffic as a bi-directional
	exchange of packets comprising a session.  When activated,
	<literal>keep-state</literal> dynamically generates
	internal rules for each anticipated packet being exchanged
	during the session.  It has sufficient matching capabilities
	to determine if a packet is valid for a session.  Any packets
	that do not properly fit the session template are
	automatically rejected.</para>

      <para>IPF stateful filtering will also allow
	<acronym>ICMP</acronym> packets related to an existing
	<acronym>TCP</acronym> or <acronym>UDP</acronym> session.  So,
	if an <acronym>ICMP</acronym> type 3 code 4 packet is a
	response in a session started by a keep state rule, it will
	automatically be allowed.  Any packet that IPF can be certain
	is part of an active session, even if it is a different
	protocol, will be allowed.</para>

      <para>Packets destined to go out through the interface connected
	to the public Internet are first checked against the dynamic
	state table.  If the packet matches the next expected packet
	comprising an active session conversation, it exits the
	firewall and the state of the session conversation flow is
	updated in the dynamic state table.  Packets that do not
	belong to an already active session, are checked against the
	outbound ruleset.</para>

      <para>Packets coming in from the interface connected to the
	public Internet are first checked against the dynamic state
	table.  If the packet matches the next expected packet
	comprising an active session, it exits the firewall and the
	state of the session conversation flow is updated in the
	dynamic state table.  Packets that do not belong to an already
	active session, are checked against the inbound
	ruleset.</para>

      <para>When the session completes, it is removed from the
	dynamic state table.</para>

      <para>Stateful filtering allows one to focus on blocking/passing
	new sessions.  If the new session is passed, all its
	subsequent packets are allowed automatically and any impostor
	packets are automatically rejected.  If a new session is
	blocked, none of its subsequent packets are allowed.  Stateful
	filtering provides advanced matching abilities capable of
	defending against the flood of different attack methods
	employed by attackers.</para>
    </sect2>

    <sect2>
      <!-- XXX: This section needs a rewrite -->

      <title>Inclusive Ruleset Example</title>

      <para>The following ruleset is an example of an inclusive type
	of firewall which only allows services matching
	<literal>pass</literal> rules and blocks all others by
	default.  Network firewalls intended to protect other machines
	should have at least two interfaces, and are generally
	configured to trust the <acronym>LAN</acronym> and to not
	trust the public Internet.  Alternatively, a host based
	firewall might be configured to protect only the system it is
	running on, and is appropriate for servers on an untrusted
	network or a desktop system not protected by firewall on the
	network.</para>

      <para>&os; uses interface <devicename>lo0</devicename> and IP
	address <hostid role="ipaddr">127.0.0.1</hostid> for internal
	communication within the operating system.  The firewall rules
	must contain rules to allow free movement of these internally
	used packets.</para>

      <para>The interface which faces the public Internet is the one
	specified in the rules that authorize and control access of
	the outbound and inbound connections.</para>

      <para>In cases where one or more NICs are cabled to private
	network segments, those interfaces may require rules to allow
	packets originating from those LAN interfaces transit to each
	other or to the Internet.</para>

      <para>The rules should be organized into three major
	sections: the trusted interfaces, then the public
	interface outbound, and lastly, the public untrusted interface
	inbound.</para>

      <para>The rules in each of the public interface sections should
	have the most frequently matched rules placed before less
	commonly matched rules, with the last rule in the section
	blocking and logging all packets on that interface and
	direction.</para>

      <para>The outbound section in the following ruleset only
	contains <literal>pass</literal> rules which uniquely identify
	the services that are authorized for public Internet access.
	All the rules use <literal>quick</literal>,
	<literal>on</literal>, <literal>proto</literal>,
	<literal>port</literal>, and <literal>keep state</literal>.
	The <literal>proto tcp</literal> rules include
	<literal>flag</literal> to identify the session start request
	as the triggering packet to activate the stateful
	facility.</para>

      <para>The inbound section blocks undesirable packets first, for
	two different reasons.  The first is that malicious packets
	may be partial matches for legitimate traffic.  These packets
	have to be discarded rather than allowed, based on their
	partial matches against the <literal>allow</literal> rules.
	The second reason is that known and uninteresting rejects may
	be blocked silently, rather than being logged by the last rule
	in the section.</para>

      <para>The ruleset should ensure that there is no response
	returned for any undesirable traffic.  Invalid packets should
	be silently dropped so that the attacker has no knowledge if
	the packets reached the system.  Rules that include a
	<literal>log first</literal> option, will only log the event
	the first time they are triggered.  This option is included in
	the sample <literal>nmap OS fingerprint</literal> rule.  The
	<filename role="package">security/nmap</filename> utility is
	commonly used by attackers who attempt to identify the
	operating system of the server.</para>

      <para>Any time there are logged messages on a rule with
	the <literal>log first</literal> option,
	<command>ipfstat -hio</command> should be executed
	to evaluate how many times the rule has been matched.  A
	large number of matches usually indicates that the system is
	being flooded or is under attack.</para>

      <para>To lookup unknown port numbers, refer to
	<filename>/etc/services</filename>. Alternatively, visit
	<ulink
	  url="http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers"></ulink>
	and do a port number lookup to find the purpose of a
	particular port number.</para>

      <para>Check out this link for port numbers used by Trojans
	<ulink
	  url="http://www.sans.org/security-resources/idfaq/oddports.php"></ulink>.</para>

      <para>The following ruleset creates an
	<literal>inclusive</literal> firewall ruleset which can be
	easily customized by commenting out
	<literal>pass</literal> rules for services that should not
	be authorized.</para>

      <para>To avoid logging unwanted messages, add a
	<literal>block</literal> rule in the inbound section.</para>

      <para>Change the <devicename>dc0</devicename> interface name in
	every rule to the interface name that connects the system to
	the public Internet.</para>

      <para>The following statements were added to
	<filename>/etc/ipf.rules</filename>:</para>

      <programlisting>#################################################################
# No restrictions on Inside LAN Interface for private network
# Not needed unless you have LAN
#################################################################

#pass out quick on xl0 all
#pass in quick on xl0 all

#################################################################
# No restrictions on Loopback Interface
#################################################################
pass in quick on lo0 all
pass out quick on lo0 all

#################################################################
# Interface facing Public Internet (Outbound Section)
# Match session start requests originating from behind the
# firewall on the private network
# or from this gateway server destined for the public Internet.
#################################################################

# Allow out access to my ISP's Domain name server.
# xxx must be the IP address of your ISP's DNS.
# Dup these lines if your ISP has more than one DNS server
# Get the IP addresses from /etc/resolv.conf file
pass out quick on dc0 proto tcp from any to xxx port = 53 flags S keep state
pass out quick on dc0 proto udp from any to xxx port = 53 keep state

# Allow out access to my ISP's DHCP server for cable or DSL networks.
# This rule is not needed for 'user ppp' type connection to the
# public Internet, so you can delete this whole group.
# Use the following rule and check log for IP address.
# Then put IP address in commented out rule &amp; delete first rule
pass out log quick on dc0 proto udp from any to any port = 67 keep state
#pass out quick on dc0 proto udp from any to z.z.z.z port = 67 keep state


# Allow out non-secure standard www function
pass out quick on dc0 proto tcp from any to any port = 80 flags S keep state

# Allow out secure www function https over TLS SSL
pass out quick on dc0 proto tcp from any to any port = 443 flags S keep state

# Allow out send &amp; get email function
pass out quick on dc0 proto tcp from any to any port = 110 flags S keep state
pass out quick on dc0 proto tcp from any to any port = 25 flags S keep state

# Allow out Time
pass out quick on dc0 proto tcp from any to any port = 37 flags S keep state

# Allow out nntp news
pass out quick on dc0 proto tcp from any to any port = 119 flags S keep state

# Allow out gateway &amp; LAN users' non-secure FTP ( both passive &amp; active modes)
# This function uses the IP<acronym>NAT</acronym> built in FTP proxy function coded in
# the nat rules file to make this single rule function correctly.
# If you want to use the pkg_add command to install application packages
# on your gateway system you need this rule.
pass out quick on dc0 proto tcp from any to any port = 21 flags S keep state

# Allow out ssh/sftp/scp (telnet/rlogin/FTP replacements)
# This function is using SSH (secure shell)
pass out quick on dc0 proto tcp from any to any port = 22 flags S keep state

# Allow out insecure Telnet
pass out quick on dc0 proto tcp from any to any port = 23 flags S keep state

# Allow out FreeBSD CVSup
pass out quick on dc0 proto tcp from any to any port = 5999 flags S keep state

# Allow out ping to public Internet
pass out quick on dc0 proto icmp from any to any icmp-type 8 keep state

# Allow out whois from LAN to public Internet
pass out quick on dc0 proto tcp from any to any port = 43 flags S keep state

# Block and log only the first occurrence of everything
# else that's trying to get out.
# This rule implements the default block
block out log first quick on dc0 all

#################################################################
# Interface facing Public Internet (Inbound Section)
# Match packets originating from the public Internet
# destined for this gateway server or the private network.
#################################################################

# Block all inbound traffic from non-routable or reserved address spaces
block in quick on dc0 from 192.168.0.0/16 to any    #RFC 1918 private IP
block in quick on dc0 from 172.16.0.0/12 to any     #RFC 1918 private IP
block in quick on dc0 from 10.0.0.0/8 to any        #RFC 1918 private IP
block in quick on dc0 from 127.0.0.0/8 to any       #loopback
block in quick on dc0 from 0.0.0.0/8 to any         #loopback
block in quick on dc0 from 169.254.0.0/16 to any    #DHCP auto-config
block in quick on dc0 from 192.0.2.0/24 to any      #reserved for docs
block in quick on dc0 from 204.152.64.0/23 to any   #Sun cluster interconnect
block in quick on dc0 from 224.0.0.0/3 to any       #Class D &amp; E multicast

##### Block a bunch of different nasty things. ############
# That I do not want to see in the log

# Block frags
block in quick on dc0 all with frags

# Block short tcp packets
block in quick on dc0 proto tcp all with short

# block source routed packets
block in quick on dc0 all with opt lsrr
block in quick on dc0 all with opt ssrr

# Block nmap OS fingerprint attempts
# Log first occurrence of these so I can get their IP address
block in log first quick on dc0 proto tcp from any to any flags FUP

# Block anything with special options
block in quick on dc0 all with ipopts

# Block public pings
block in quick on dc0 proto icmp all icmp-type 8

# Block ident
block in quick on dc0 proto tcp from any to any port = 113

# Block all Netbios service. 137=name, 138=datagram, 139=session
# Netbios is MS/Windows sharing services.
# Block MS/Windows hosts2 name server requests 81
block in log first quick on dc0 proto tcp/udp from any to any port = 137
block in log first quick on dc0 proto tcp/udp from any to any port = 138
block in log first quick on dc0 proto tcp/udp from any to any port = 139
block in log first quick on dc0 proto tcp/udp from any to any port = 81

# Allow traffic in from ISP's DHCP server. This rule must contain
# the IP address of your ISP's DHCP server as it is the only
# authorized source to send this packet type. Only necessary for
# cable or DSL configurations. This rule is not needed for
# 'user ppp' type connection to the public Internet.
# This is the same IP address you captured and
# used in the outbound section.
pass in quick on dc0 proto udp from z.z.z.z to any port = 68 keep state

# Allow in standard www function because I have apache server
pass in quick on dc0 proto tcp from any to any port = 80 flags S keep state

# Allow in non-secure Telnet session from public Internet
# labeled non-secure because ID/PW passed over public Internet as clear text.
# Delete this sample group if you do not have telnet server enabled.
#pass in quick on dc0 proto tcp from any to any port = 23 flags S keep state

# Allow in secure FTP, Telnet, and SCP from public Internet
# This function is using SSH (secure shell)
pass in quick on dc0 proto tcp from any to any port = 22 flags S keep state

# Block and log only first occurrence of all remaining traffic
# coming into the firewall. The logging of only the first
# occurrence avoids filling up disk with Denial of Service logs.
# This rule implements the default block.
block in log first quick on dc0 all
################### End of rules file #####################################</programlisting>
    </sect2>

    <sect2>
      <title><acronym>NAT</acronym></title>

      <indexterm><primary>NAT</primary></indexterm>

      <indexterm>
	<primary>IP masquerading</primary>

	<see>NAT</see>
      </indexterm>

      <indexterm>
	<primary>network address translation</primary>

	<see>NAT</see>
      </indexterm>

      <para><acronym>NAT</acronym> stands for <emphasis>Network
	  Address Translation</emphasis>.  In &linux;, NAT is called
	<quote>IP Masquerading</quote>.  The IPF
	<acronym>NAT</acronym> function enables the private LAN behind
	the firewall to share a single ISP-assigned IP address, even
	if that address is dynamically assigned.  NAT allows each
	computer in the LAN to have Internet access, without
	having to pay the ISP for multiple Internet accounts or IP
	addresses.</para>

      <para><acronym>NAT</acronym> will automatically translate the
	private LAN IP address for each system on the LAN to the
	single public IP address as packets exit the firewall bound
	for the public Internet.  It also performs the reverse
	translation for returning packets.</para>

      <para>According to RFC 1918, the following IP address ranges are
	reserved for private networks which will never be routed
	directly to the public Internet, and therefore are available
	for use with NAT:</para>

      <itemizedlist>
	<listitem>
	  <para><literal>10.0.0.0/8</literal>.</para>
	</listitem>

	<listitem>
	  <para><literal>172.16.0.0/12</literal>.</para>
	</listitem>

	<listitem>
	  <para><literal>192.168.0.0/16</literal>.</para>
	</listitem>
      </itemizedlist>

    </sect2>

    <sect2>
      <title>IP<acronym>NAT</acronym></title>

      <indexterm>
	<primary>NAT</primary>

	<secondary>and IPFILTER</secondary>
      </indexterm>

      <indexterm><primary><command>ipnat</command></primary></indexterm>

      <para><acronym>NAT</acronym> rules are loaded using
	<command>ipnat</command>.  Typically, the
	<acronym>NAT</acronym> rules are stored in
	<filename>/etc/ipnat.rules</filename>.  See &man.ipnat.8; for
	details.</para>

      <para>When the file containing the <acronym>NAT</acronym> rules
	is edited after <acronym>NAT</acronym> has been started, run
	<command>ipnat</command> with <option>-CF</option> to delete
	the internal in use <acronym>NAT</acronym> rules and flush the
	contents of the translation table of all active
	entries.</para>

      <para>To reload the <acronym>NAT</acronym> rules, issue a
	command like this:</para>

      <screen>&prompt.root; <userinput>ipnat -CF -f
	  /etc/ipnat.rules</userinput></screen>

      <para>To display some <acronym>NAT</acronym> statistics, use
	this command:</para>

      <screen>&prompt.root; <userinput>ipnat -s</userinput></screen>

      <para>To list the <acronym>NAT</acronym> table's current
	mappings, use this command:</para>

      <screen>&prompt.root; <userinput>ipnat -l</userinput></screen>

      <para>To turn verbose mode on and display information relating
	to rule processing and active rules/table entries:</para>

      <screen>&prompt.root; <userinput>ipnat -v</userinput></screen>
    </sect2>

    <sect2>
      <title>IP<acronym>NAT</acronym> Rules</title>

      <para><acronym>NAT</acronym> rules are flexible and can
	accomplish many different things to fit the needs of
	commercial and home users.</para>

      <para>The rule syntax presented here has been simplified to
	what is most commonly used in a non-commercial environment.
	For a complete rule syntax description, refer to
	&man.ipnat.5;.</para>

      <para>The syntax for a <acronym>NAT</acronym> rule looks like
	this:</para>

      <programlisting>map <replaceable>IF</replaceable> <replaceable>LAN_IP_RANGE</replaceable> -&gt; <replaceable>PUBLIC_ADDRESS</replaceable></programlisting>

      <para>The keyword <literal>map</literal> starts the rule.</para>

      <para>Replace <replaceable>IF</replaceable> with the external
	interface.</para>

      <para>The <replaceable>LAN_IP_RANGE</replaceable> is used by the
	internal clients use for IP Addressing.  Usually, this is
	something like <hostid
	  role="ipaddr">192.168.1.0/24</hostid>.</para>

      <para>The <replaceable>PUBLIC_ADDRESS</replaceable> can either
	be the static external IP address or the special keyword
	<literal>0/32</literal> which uses the IP address assigned to
	<replaceable>IF</replaceable>.</para>
    </sect2>

    <sect2>
      <title>How <acronym>NAT</acronym> Works</title>

      <para>In IPF, when a packet arrives at the firewall from the LAN
	with a public destination, it passes through the outbound
	filter rules.  <acronym>NAT</acronym> gets its turn at the
	packet and applies its rules top down, where the first
	matching rule wins.  <acronym>NAT</acronym> tests each of its
	rules against the packet's interface name and source IP
	address.  When a packet's interface name matches a
	<acronym>NAT</acronym> rule, the packet's source IP address in
	the private LAN is checked to see if it falls within the IP
	address range specified to the left of the arrow symbol on the
	<acronym>NAT</acronym> rule.  On a match, the packet has its
	source IP address rewritten with the public IP address
	obtained by the <literal>0/32</literal> keyword.
	<acronym>NAT</acronym> posts an entry in its internal
	<acronym>NAT</acronym> table so when the packet returns from
	the public Internet it can be mapped back to its original
	private IP address and then passed to the filter rules for
	processing.</para>
    </sect2>

    <sect2>
      <title>Enabling IP<acronym>NAT</acronym></title>

      <para>To enable IP<acronym>NAT</acronym>, add these statements
	to <filename>/etc/rc.conf</filename>.</para>

      <para>To enable the machine to route traffic between
	interfaces:</para>

      <programlisting>gateway_enable="YES"</programlisting>

      <para>To start IP<acronym>NAT</acronym> automatically each
	time:</para>

      <programlisting>ipnat_enable="YES"</programlisting>

      <para>To specify where to load the IP<acronym>NAT</acronym>
	rules from:</para>

      <programlisting>ipnat_rules="/etc/ipnat.rules"</programlisting>
    </sect2>

    <sect2>
      <title><acronym>NAT</acronym> for a Large LAN</title>

      <para>For networks that have large numbers of systems on the LAN
	or networks with more than a single LAN, the process of
	funneling all those private IP addresses into a single public
	IP address becomes a resource problem that may cause problems
	with the same port numbers being used many times across many
	connections, causing collisions.  There are two ways to
	relieve this resource problem.</para>

      <sect3>
	<title>Assigning Ports to Use</title>

	<para>A normal NAT rule would look like:</para>

	<programlisting>map dc0 192.168.1.0/24 -&gt; 0/32</programlisting>

	<para>In the above rule, the packet's source port is unchanged
	  as the packet passes through IP<acronym>NAT</acronym>.  By
	  adding the <literal>portmap</literal> keyword,
	  IP<acronym>NAT</acronym> can be directed to only use
	  source ports in the specified range.  For example, the
	  following rule will tell IP<acronym>NAT</acronym> to modify
	  the source port to be within the range shown:</para>

	<programlisting>map dc0 192.168.1.0/24 -&gt; 0/32 portmap tcp/udp 20000:60000</programlisting>

	<para>Additionally, the <literal>auto</literal> keyword tells
	  IP<acronym>NAT</acronym> to determine which ports are
	  available for use:</para>

	<programlisting>map dc0 192.168.1.0/24 -&gt; 0/32 portmap tcp/udp auto</programlisting>
      </sect3>

      <sect3>
	<title>Using a Pool of Public Addresses</title>

	<para>In very large LANs there comes a point where there are
	  just too many LAN addresses to fit into a single public
	  address.  If a block of public IP addresses is available,
	  these addresses can be used as a <quote>pool</quote>, and
	  IP<acronym>NAT</acronym> may pick one of the public IP
	  addresses as packet addresses are mapped on their way
	  out.</para>

	<para>For example, instead of mapping all packets through a
	  single public IP address:</para>

	<programlisting>map dc0 192.168.1.0/24 -&gt; 204.134.75.1</programlisting>

	<para>A range of public IP addresses can be specified either
	  with a netmask:</para>

	<programlisting>map dc0 192.168.1.0/24 -&gt; 204.134.75.0/255.255.255.0</programlisting>

	<para>or using CIDR notation:</para>

	<programlisting>map dc0 192.168.1.0/24 -&gt; 204.134.75.0/24</programlisting>
      </sect3>
    </sect2>

    <sect2>
      <title>Port Redirection</title>

      <para>A common practice is to have a web server, email server,
	database server, and DNS server each segregated to a different
	system on the LAN.  In this case, the traffic from these
	servers still has to undergo <acronym>NAT</acronym>, but there
	has to be some way to direct the inbound traffic to the
	correct server.  For example, a web server operating on LAN
	address <hostid
	  role="ipaddr">10.0.10.25</hostid> and using a single public
	IP address of <hostid role="ipaddr">20.20.20.5</hostid>, would
	use this rule:</para>

	<programlisting>rdr dc0 20.20.20.5/32 port 80 -&gt; 10.0.10.25 port 80</programlisting>

	<para>or:</para>

	<programlisting>rdr dc0 0.0.0.0/0 port 80 -&gt; 10.0.10.25 port 80</programlisting>

	<para>For a LAN DNS server on a private address of <hostid
	    role="ipaddr">10.0.10.33</hostid> that needs to receive
	  public DNS requests:</para>

	<programlisting>rdr dc0 20.20.20.5/32 port 53 -&gt; 10.0.10.33 port 53 udp</programlisting>
    </sect2>

    <sect2>
      <title>FTP and <acronym>NAT</acronym></title>

      <para>FTP has two modes:  active mode and passive mode.  The
	difference is in how the data channel is acquired.  Passive
	mode is more secure as the data channel is acquired by the
	ordinal ftp session requester.  For a good explanation of FTP
	and the different modes, see <ulink
	  url="http://www.slacksite.com/other/ftp.html"></ulink>.</para>

      <sect3>
	<title>IP<acronym>NAT</acronym> Rules</title>

	<para>IP<acronym>NAT</acronym> has a built in FTP proxy option
	  which can be specified on the <acronym>NAT</acronym> map
	  rule.  It can monitor all outbound packet traffic for FTP
	  active or passive start session requests and dynamically
	  create temporary filter rules containing the port number
	  being used by the data channel.  This eliminates the
	  security risk FTP normally exposes the firewall to as it no
	  longer needs to open large ranges of high order ports for
	  FTP connections.</para>

	<para>This rule will handle all the traffic for the internal
	  LAN:</para>

	<programlisting>map dc0 10.0.10.0/29 -&gt; 0/32 proxy port 21 ftp/tcp</programlisting>

	<para>This rule handles the FTP traffic from the
	  gateway:</para>

	<programlisting>map dc0 0.0.0.0/0 -&gt; 0/32 proxy port 21 ftp/tcp</programlisting>

	<para>This rule handles all non-FTP traffic from the internal
	  LAN:</para>

	<programlisting>map dc0 10.0.10.0/29 -&gt; 0/32</programlisting>

	<para>The FTP <literal>map</literal> rules go before the
	  <acronym>NAT</acronym> rule so that when a packet matches an
	  FTP rule, the FTP proxy creates temporary filter rules to
	  let the FTP session packets pass and undergo
	  <acronym>NAT</acronym>.  All LAN packets that are not FTP
	  will not match the FTP rules but will undergo
	  <acronym>NAT</acronym> if they match the third rule.</para>
      </sect3>

      <sect3>
	<title>IP<acronym>NAT</acronym> FTP Filter Rules</title>

	<para>Only one filter rule is needed for FTP if the
	  <acronym>NAT</acronym> FTP proxy is used.</para>

	<para>Without the FTP proxy, the following three rules will be
	  needed:</para>

	<programlisting># Allow out LAN PC client FTP to public Internet
# Active and passive modes
pass out quick on rl0 proto tcp from any to any port = 21 flags S keep state

# Allow out passive mode data channel high order port numbers
pass out quick on rl0 proto tcp from any to any port &gt; 1024 flags S keep state

# Active mode let data channel in from FTP server
pass in quick on rl0 proto tcp from any to any port = 20 flags S keep state</programlisting>
      </sect3>
    </sect2>
  </sect1>

  <sect1 id="firewalls-ipfw">
    <title>IPFW</title>

    <indexterm>
      <primary>firewall</primary>

      <secondary>IPFW</secondary>
    </indexterm>

    <para><acronym>IPFW</acronym> is a stateful firewall written for
      &os; which also provides a traffic shaper, packet scheduler,
      and in-kernel NAT.</para>

    <para>&os; provides a sample ruleset in
      <filename>/etc/rc.firewall</filename>.  The sample ruleset
      define several firewall types for common scenarios to assist
      novice users in generating an appropriate ruleset.
      &man.ipfw.8; provides a powerful syntax which advanced users can
      use to craft customized rulesets that meet the security
      requirements of a given environment.</para>

    <para>IPFW is composed of several components:  the kernel firewall
      filter rule processor and its integrated packet accounting
      facility, the logging facility, the
      <literal>divert</literal> rule which triggers
      <acronym>NAT</acronym>, the dummynet traffic shaper facilities,
      the <literal>fwd rule</literal> forward facility, the bridge
      facility, and the ipstealth facility.  IPFW supports both IPv4
      and IPv6.</para>

    <sect2 id="firewalls-ipfw-enable">
      <title>Enabling IPFW</title>

      <indexterm>
	<primary>IPFW</primary>

	<secondary>enabling</secondary>
      </indexterm>

      <para>IPFW is included in the basic &os; install as a run time
	loadable module.  The system will dynamically load the kernel
	module when <filename>rc.conf</filename> contains the
	statement <literal>firewall_enable="YES"</literal>.  After
	rebooting the system, the following white highlighted message
	is displayed on the screen as part of the boot process:</para>

      <screen>ipfw2 initialized, divert disabled, rule-based forwarding disabled, default to deny, logging disabled</screen>

      <para>The loadable module includes logging ability.  To enable
	logging and set the verbose logging limit, add these
	statements to
	<filename>/etc/sysctl.conf</filename> before rebooting:</para>

      <programlisting>net.inet.ip.fw.verbose=1
net.inet.ip.fw.verbose_limit=5</programlisting>
    </sect2>

    <sect2 id="firewalls-ipfw-kernel">
      <title>Kernel Options</title>

      <indexterm>
	<primary>kernel options</primary>

	<secondary>IPFIREWALL</secondary>
      </indexterm>

      <indexterm>
	<primary>kernel options</primary>

	<secondary>IPFIREWALL_VERBOSE</secondary>
      </indexterm>

      <indexterm>
	<primary>kernel options</primary>

	<secondary>IPFIREWALL_VERBOSE_LIMIT</secondary>
      </indexterm>

      <indexterm>
	<primary>IPFW</primary>

	<secondary>kernel options</secondary>
      </indexterm>

      <para>For those users who wish to statically compile kernel
	IPFW support, the following options are available for the
	custom kernel configuration file:</para>

      <programlisting>options    IPFIREWALL</programlisting>

      <para>This option enables IPFW as part of the kernel.</para>

      <programlisting>options    IPFIREWALL_VERBOSE</programlisting>

      <para>This option enables logging of packets that pass through
	IPFW and have the <literal>log</literal> keyword specified in
	the ruleset.</para>

      <programlisting>options    IPFIREWALL_VERBOSE_LIMIT=5</programlisting>

      <para>This option limits the number of packets logged through
	&man.syslogd.8;, on a per-entry basis.  This option may be
	used in hostile environments, when firewall activity logging
	is desired.  This will close a possible denial of service
	attack via syslog flooding.</para>

      <indexterm>
	<primary>kernel options</primary>

	<secondary>IPFIREWALL_DEFAULT_TO_ACCEPT</secondary>
      </indexterm>

      <programlisting>options    IPFIREWALL_DEFAULT_TO_ACCEPT</programlisting>

      <para>This option allows everything to pass through the firewall
	by default, which is a good idea when the firewall is being
	set up for the first time.</para>

      <indexterm>
	<primary>kernel options</primary>

	<secondary>IPDIVERT</secondary>
      </indexterm>

      <programlisting>options    IPDIVERT</programlisting>

      <para>This option enables the use of <acronym>NAT</acronym>
	functionality.</para>

      <note>
	<para>The firewall will block all incoming and outgoing
	  packets if either the
	  <literal>IPFIREWALL_DEFAULT_TO_ACCEPT</literal> kernel
	  option or a rule to explicitly allow these connections is
	  missing.</para>
      </note>
    </sect2>

    <sect2 id="firewalls-ipfw-rc">
      <title><filename>/etc/rc.conf</filename> Options</title>

      <para>Enables the firewall:</para>

      <programlisting>firewall_enable="YES"</programlisting>

      <para>To select one of the default firewall types provided by
	&os;, select one by reading
	<filename>/etc/rc.firewall</filename> and specify it in
	the following:</para>

      <programlisting>firewall_type="open"</programlisting>

      <para>Available values for this setting are:</para>

      <itemizedlist>
	<listitem>
	  <para><literal>open</literal>: passes all traffic.</para>
	</listitem>
	<listitem>
	  <para><literal>client</literal>: protects only this
	    machine.</para>
	</listitem>
	<listitem>
	  <para><literal>simple</literal>: protects the whole
	    network.</para>
	</listitem>
	<listitem>
	  <para><literal>closed</literal>: entirely disables IP
	    traffic except for the loopback interface.</para>
	</listitem>
	<listitem>
	  <para><literal>UNKNOWN</literal>: disables the loading of
	    firewall rules.</para>
	</listitem>
	<listitem>
	  <para><filename><replaceable>filename</replaceable></filename>:
	    absolute path of the file containing the firewall
	    rules.</para>
	</listitem>
      </itemizedlist>

      <para>Two methods are available for loading custom
	<application>ipfw</application> rules.  One is to set the
	<literal>firewall_type</literal> variable to the absolute
	path of the file which contains the firewall rules.</para>

      <para>The other method is to set the
	<literal>firewall_script</literal> variable to the absolute
	path of an executable script that includes
	<command>ipfw</command> commands.  A ruleset script that
	blocks all incoming and outgoing traffic would look like
	this:</para>

      <programlisting>#!/bin/sh

ipfw -q flush

ipfw add deny in
ipfw add deny out</programlisting>

      <note>
	<para>If <literal>firewall_type</literal> is set to either
	  <literal>client</literal> or <literal>simple</literal>,
	  modify the default rules found in
	  <filename>/etc/rc.firewall</filename> to fit the
	  configuration of the system.  The examples used in this
	  section assume that the <literal>firewall_script</literal>
	  is set to <filename>/etc/ipfw.rules</filename>.</para>
      </note>

      <para>Enable logging:</para>

      <programlisting>firewall_logging="YES"</programlisting>

      <warning>
	<para><varname>firewall_logging</varname> sets the
	  <varname>net.inet.ip.fw.verbose</varname> sysctl
	  variable to the value of <literal>1</literal>.  There is no
	  <filename>rc.conf</filename> variable to set log
	  limitations, but the desired value can be set using
	  <command>sysctl</command> or by adding the following
	  variable and desired value to
	  <filename>/etc/sysctl.conf</filename>:</para>

	<programlisting>net.inet.ip.fw.verbose_limit=5</programlisting>
      </warning>

      <para>If the machine is acting as a gateway providing
	<acronym>NAT</acronym> using &man.natd.8;,
	refer to <xref linkend="network-natd"/> for information
	regarding the required <filename>/etc/rc.conf</filename>
	options.</para>
    </sect2>

    <sect2 id="firewalls-ipfw-cmd">
      <title>The IPFW Command</title>

      <indexterm><primary><command>ipfw</command></primary></indexterm>

      <para><command>ipfw</command> can be used to make manual,
	single rule additions or deletions to the active firewall
	while it is running.  The problem with using this method is
	that all the changes are lost when the system reboots.  It is
	recommended to instead write all the rules in a file and to
	use that file to load the rules at boot time and to replace
	the currently running firewall rules whenever that file
	changes.</para>

      <para><command>ipfw</command> is a useful way to display the
	running firewall rules to the console screen.  The IPFW
	accounting facility dynamically creates a counter for each
	rule that counts each packet that matches the rule.  During
	the process of testing a rule, listing the rule with its
	counter is one way to determine if the rule is
	functioning as expected.</para>

      <para>To list all the running rules in sequence:</para>

      <screen>&prompt.root; <userinput>ipfw list</userinput></screen>

      <para>To list all the running rules with a time stamp of when
	the last time the rule was matched:</para>

      <screen>&prompt.root; <userinput>ipfw -t list</userinput></screen>

      <para>The next example lists accounting information and the
	packet count for matched rules along with the rules
	themselves.  The first column is the rule number, followed by
	the number of matched packets and bytes, followed by the rule
	itself.</para>

      <screen>&prompt.root; <userinput>ipfw -a list</userinput></screen>

      <para>To list dynamic rules in addition to static rules:</para>

      <screen>&prompt.root; <userinput>ipfw -d list</userinput></screen>

      <para>To also show the expired dynamic rules:</para>

      <screen>&prompt.root; <userinput>ipfw -d -e list</userinput></screen>

      <para>To zero the counters:</para>

      <screen>&prompt.root; <userinput>ipfw zero</userinput></screen>

      <para>To zero the counters for just the rule with number
	<replaceable>NUM</replaceable>:</para>

      <screen>&prompt.root; <userinput>ipfw zero <replaceable>NUM</replaceable></userinput></screen>
    </sect2>

    <sect2 id="firewalls-ipfw-rules">
      <title>IPFW Rulesets</title>

      <indexterm>
	<primary>IPFW</primary>

	<secondary>rule processing order</secondary>
      </indexterm>

      <para>When a packet enters the <acronym>IPFW</acronym> firewall,
	it is compared against the first rule in the ruleset and
	progresses one rule at a time, moving from top to bottom of
	the set in ascending rule number sequence order.  When the
	packet matches the selection parameters of a rule, the rule's
	action field value is executed and the search of the ruleset
	terminates for that packet.  This is referred to as
	<quote>first match wins</quote>.  If the packet does not match
	any of the rules, it gets caught by the mandatory IPFW default
	rule, number 65535, which denies all packets and silently
	discards them.  However, if the packet matches a rule that
	contains the <literal>count</literal>,
	<literal>skipto</literal>, or <literal>tee</literal> keywords,
	the search continues.  Refer to &man.ipfw.8; for details on
	how these keywords affect rule processing.</para>

      <para>The examples in this section create an inclusive type
	firewall ruleset containing the stateful <literal>keep
	  state</literal>, <literal>limit</literal>,
	<literal>in</literal>, <literal>out</literal> and
	<literal>via</literal> options.  For a complete rule syntax
	description, refer to &man.ipfw.8;.</para>

      <warning>
	<para>Be careful when working with firewall rules, as it is
	  easy to lock out even the administrator.</para>
      </warning>

      <sect3 id="firewalls-ipfw-rules-syntax">
	<title>Rule Syntax</title>

	<indexterm>
	  <primary>IPFW</primary>

	  <secondary>rule syntax</secondary>
	</indexterm>

	<para>This section describes the keywords which comprise an
	  <acronym>IPFW</acronym> rule.  Keywords must be written in
	  the following order.  <literal>#</literal> is used to mark
	  the start of a comment and may appear at the end of a rule
	  line or on its own line.  Blank lines are ignored.</para>

	<para><replaceable>CMD RULE_NUMBER ACTION LOGGING SELECTION
	    STATEFUL</replaceable></para>

	<sect4>
	  <title>CMD</title>

	  <para>Each new rule has to be prefixed with
	    <parameter>add</parameter> to add the rule to the internal
	    table.</para>
	</sect4>

	<sect4>
	  <title>RULE_NUMBER</title>

	  <para>Each rule is associated with a rule_number in the
	    range of <literal>1</literal> to
	    <literal>65535</literal>.</para>
	</sect4>

	<sect4>
	  <title>ACTION</title>

	  <para>A rule can be associated with one of the following
	    actions.  The specified action will be executed when the
	    packet matches the selection criterion of the rule.</para>

	  <para><parameter>allow | accept | pass |
	      permit</parameter></para>

	  <para>These keywords are equivalent as they allow packets
	    that match the rule to exit the firewall rule processing.
	    The search terminates at this rule.</para>

	  <para><parameter>check-state</parameter></para>

	  <para>Checks the packet against the dynamic rules table.
	    If a match is found, execute the action associated with
	    the rule which generated this dynamic rule, otherwise
	    move to the next rule.  A <literal>check-state</literal>
	    rule does not have selection criterion.  If no
	    <literal>check-state</literal> rule is present in the
	    ruleset, the dynamic rules table is checked at the first
	    <literal>keep-state</literal> or <literal>limit</literal>
	    rule.</para>

	  <para><parameter>deny | drop</parameter></para>

	  <para>Both words mean the same thing, which is to discard
	    packets that match this rule.  The search
	    terminates.</para>
	</sect4>

	<sect4>
	  <title>Logging</title>

	  <para>When a packet matches a rule with the
	    <literal>log</literal> keyword, a message will be logged
	    to &man.syslogd.8; with a facility name of
	    <literal>SECURITY</literal>.  Logging only occurs if the
	    number of packets logged for that particular rule does not
	    exceed the <literal>logamount</literal> parameter.  If no
	    <literal>logamount</literal> is specified, the limit is
	      taken from the <command>sysctl</command> value of
	    <varname>net.inet.ip.fw.verbose_limit</varname>.  In both
	    cases, a value of zero removes the logging limit.  Once
	    the limit is reached, logging can be re-enabled by
	    clearing the logging counter or the packet counter for
	    that rule, using <command>ipfw reset log</command>.</para>

	  <note>
	    <para>Logging is done after all other packet matching
	      conditions have been met, and before performing the
	      final action on the packet.  The administrator decides
	      which rules to enable logging on.</para>
	  </note>
	</sect4>

	<sect4>
	  <title>Selection</title>

	  <para>The keywords described in this section are used to
	    describe attributes of the packet to be checked when
	    determining whether rules match the packet or not.
	    The following general-purpose attributes are provided for
	    matching, and must be used in this order:</para>

	  <para><parameter>udp | tcp | icmp</parameter></para>

	  <para>Any other protocol names found in
	    <filename>/etc/protocols</filename> can be used.  The
	    value specified is the protocol to be matched against.
	    This is a mandatory keyword.</para>

	  <para><parameter>from src to dst</parameter></para>

	  <para>The <literal>from</literal> and <literal>to</literal>
	    keywords are used to match against IP addresses.  Rules
	    must specify <emphasis>both</emphasis> source and
	    destination parameters.  <literal>any</literal> is a
	    special keyword that matches any IP address.
	    <literal>me</literal> is a special keyword that matches
	    any IP address configured on an interface in the &os;
	    system to represent the PC the firewall is running on.
	    Example usage includes <literal>from me to any</literal>,
	    <literal>from any to me</literal>, <literal>from 0.0.0.0/0
	      to any</literal>, <literal>from any to
	      0.0.0.0/0</literal>, <literal>from 0.0.0.0 to
	      any</literal>. <literal>from any to 0.0.0.0</literal>,
	    and <literal>from me to 0.0.0.0</literal>.  IP addresses
	    are specified in dotted IP address format followed by the
	    mask in CIDR notation, or as a single host in dotted IP
	    address format.  This keyword is a mandatory requirement.
	    The <filename role="package">net-mgmt/ipcalc</filename>
	    port may be used to assist the mask calculation.</para>

	  <para><parameter>port number</parameter></para>

	  <para>For protocols which support port numbers, such as
	    <acronym>TCP</acronym> and <acronym>UDP</acronym>, it
	    is mandatory to include the port number of the service
	    that will be matched. Service names from
	    <filename>/etc/services</filename> may be used instead
	    of numeric port values.</para>

	  <para><parameter>in | out</parameter></para>

	  <para>Matches incoming or outgoing packets.  It is mandatory
	    that one or the other is included as part of the rule
	    matching criterion.</para>

	  <para><parameter>via IF</parameter></para>

	  <para>Matches packets going through the interface specified
	    by device name.  The <literal>via</literal> keyword causes
	    the interface to always be checked as part of the match
	    process.</para>

	  <para><parameter>setup</parameter></para>

	  <para>This mandatory keyword identifies the session start
	    request for <acronym>TCP</acronym> packets.</para>

	  <para><parameter>keep-state</parameter></para>

	  <para>This is a mandatory keyword.  Upon a match, the
	    firewall will create a dynamic rule, whose default
	    behavior is to match bidirectional traffic between source
	    and destination IP/port using the same protocol.</para>

	  <para><parameter>limit {src-addr | src-port | dst-addr |
	      dst-port}</parameter></para>

	  <para>The firewall will only allow
	    <replaceable>N</replaceable> connections with the same
	    set of parameters as specified in the rule.  One or more
	    of source and destination addresses and ports can be
	    specified.  <literal>limit</literal> and
	    <literal>keep-state</literal> can not be used on the same
	    rule as they provide the same stateful function.</para>
	</sect4>
      </sect3>

      <sect3>
	<title>Stateful Rule Option</title>

	<indexterm>
	  <primary>IPFW</primary>

	  <secondary>stateful filtering</secondary>
	</indexterm>

	<para>The <literal>check-state</literal> option is used to
	  identify where in the IPFW ruleset the packet is to be
	  tested against the dynamic rules facility.  On a match, the
	  packet exits the firewall to continue on its way and a new
	  rule is dynamically created for the next anticipated packet
	  being exchanged during this session.  On a no match, the
	  packet advances to the next rule in the ruleset for
	  testing.</para>

	<para>The dynamic rules facility is vulnerable to resource
	  depletion from a SYN-flood attack which would open a huge
	  number of dynamic rules.  To counter this type of attack
	  with  <acronym>IPFW</acronym>, use <literal>limit</literal>.
	  This keyword limits the number of simultaneous sessions by
	  checking that rule's source or destinations fields and using
	  the packet's IP address in a search of the open dynamic
	  rules, counting the number of times this rule and IP address
	  combination occurred.  If this count is greater than the
	  value specified by <literal>limit</literal>, the packet is
	  discarded.</para>
      </sect3>

      <sect3>
	<title>Logging Firewall Messages</title>

	<indexterm>
	  <primary>IPFW</primary>

	  <secondary>logging</secondary>
	</indexterm>

	<para>Even with the logging facility enabled, IPFW will not
	  generate any rule logging on its own.  The firewall
	  administrator decides which rules in the ruleset will be
	  logged, and adds the <literal>log</literal> keyword to those
	  rules.  Normally only deny rules are logged.  It is
	  customary to duplicate the <quote>ipfw default deny
	    everything</quote> rule with the <literal>log</literal>
	  keyword included as the last rule in the ruleset.  This
	  way, it is possible to see all the packets that did not
	  match any of the rules in the ruleset.</para>

	<para>Logging is a two edged sword.  If one is not careful,
	  an over abundance of log data or a DoS attack can fill the
	  disk with log files.  Log messages are not only written to
	  <application>syslogd</application>, but also are displayed
	  on the root console screen and soon become annoying.</para>

	<para>The <literal>IPFIREWALL_VERBOSE_LIMIT=5</literal>
	  kernel option limits the number of consecutive messages
	  sent to &man.syslogd.8;, concerning the packet matching of a
	  given rule.  When this option is enabled in the kernel, the
	  number of consecutive messages concerning a particular rule
	  is capped at the number specified.  There is nothing to be
	  gained from 200 identical log messages.  With this option
	  set to five,
	  five consecutive messages concerning a particular rule
	  would be logged to <application>syslogd</application> and
	  the remainder identical consecutive messages would be
	  counted and posted to <application>syslogd</application>
	  with a phrase like the following:</para>

	<programlisting>last message repeated 45 times</programlisting>

	<para>All logged packets messages are written by default to
	  <filename>/var/log/security</filename>, which is
	  defined in <filename>/etc/syslog.conf</filename>.</para>
      </sect3>

      <sect3 id="firewalls-ipfw-rules-script">
	<title>Building a Rule Script</title>

	<para>Most experienced IPFW users create a file containing
	  the rules and code them in a manner compatible with running
	  them as a script.  The major benefit of doing this is the
	  firewall rules can be refreshed in mass without the need
	  of rebooting the system to activate them.  This method is
	  convenient in testing new rules as the procedure can
	  be executed as many times as needed.  Being a script,
	  symbolic substitution can be used for frequently used
	  values to be substituted into multiple rules.</para>

	<para>This example script is compatible with the syntax used
	  by the &man.sh.1;,  &man.csh.1;, and &man.tcsh.1; shells.
	  Symbolic substitution fields are prefixed with a dollar sign
	  (&dollar;).  Symbolic fields do not have the &dollar;
	  prefix.  The value to populate the symbolic field must be
	  enclosed in double quotes ("").</para>

	<para>Start the rules file like this:</para>

	<programlisting>############### start of example ipfw rules script #############
#
ipfw -q -f flush       # Delete all rules
# Set defaults
oif="tun0"             # out interface
odns="192.0.2.11"      # ISP's DNS server IP address
cmd="ipfw -q add "     # build rule prefix
ks="keep-state"        # just too lazy to key this each time
&dollar;cmd 00500 check-state
&dollar;cmd 00502 deny all from any to any frag
&dollar;cmd 00501 deny tcp from any to any established
&dollar;cmd 00600 allow tcp from any to any 80 out via &dollar;oif setup &dollar;ks
&dollar;cmd 00610 allow tcp from any to &dollar;odns 53 out via &dollar;oif setup &dollar;ks
&dollar;cmd 00611 allow udp from any to &dollar;odns 53 out via &dollar;oif &dollar;ks
################### End of example ipfw rules script ############</programlisting>

	<para>The rules are not important as the focus of this example
	  is how the symbolic substitution fields are
	  populated.</para>

	<para>If the above example was in
	  <filename>/etc/ipfw.rules</filename>, the rules could be
	  reloaded by the following command:</para>

	<screen>&prompt.root; <userinput>sh /etc/ipfw.rules</userinput></screen>

	<para><filename>/etc/ipfw.rules</filename> can be located
	  anywhere and the file can have any name.</para>

	<para>The same thing could be accomplished by running these
	  commands by hand:</para>

	<screen>&prompt.root; <userinput>ipfw -q -f flush</userinput>
&prompt.root; <userinput>ipfw -q add check-state</userinput>
&prompt.root; <userinput>ipfw -q add deny all from any to any frag</userinput>
&prompt.root; <userinput>ipfw -q add deny tcp from any to any established</userinput>
&prompt.root; <userinput>ipfw -q add allow tcp from any to any 80 out via tun0 setup keep-state</userinput>
&prompt.root; <userinput>ipfw -q add allow tcp from any to 192.0.2.11 53 out via tun0 setup keep-state</userinput>
&prompt.root; <userinput>ipfw -q add 00611 allow udp from any to 192.0.2.11 53 out via tun0 keep-state</userinput></screen>
      </sect3>

      <sect3>
	<title>An Example Stateful Ruleset</title>

	<para>The following sample ruleset is a complete inclusive
	  type ruleset.  Comment out any
	  <literal>pass</literal> rules for services that are not
	  required.  To avoid logging undesired messages, add a
	  <literal>deny</literal> rule in the inbound section.
	  Change the <devicename>dc0</devicename> in every rule to the
	  device name of the interface that connects the system to the
	  Internet.</para>

	<para>There is a noticeable pattern in the usage of these
	  rules.</para>

	<itemizedlist>
	  <listitem>
	    <para>All statements that are a request to start a session
	      to the Internet use
	      <literal>keep-state</literal>.</para>
	  </listitem>

	  <listitem>
	    <para>All the authorized services that originate from
	      the Internet use <literal>limit</literal> to prevent
	      flooding.</para>
	  </listitem>

	  <listitem>
	    <para>All rules use <literal>in</literal> or
	      <literal>out</literal> to clarify direction.</para>
	  </listitem>

	  <listitem>
	    <para>All rules use <literal>via</literal>
	      <replaceable>interface-name</replaceable> to specify
	      the interface the packet is traveling over.</para>
	  </listitem>
	</itemizedlist>

	<para>The following rules go into
	  <filename>/etc/ipfw.rules</filename>:</para>

	<programlisting>################ Start of IPFW rules file ###############################
# Flush out the list before we begin.
ipfw -q -f flush

# Set rules command prefix
cmd="ipfw -q add"
pif="dc0"     # public interface name of NIC
              # facing the public Internet

#################################################################
# No restrictions on Inside LAN Interface for private network
# Not needed unless you have LAN.
# Change xl0 to your LAN NIC interface name
#################################################################
#&dollar;cmd 00005 allow all from any to any via xl0

#################################################################
# No restrictions on Loopback Interface
#################################################################
&dollar;cmd 00010 allow all from any to any via lo0

#################################################################
# Allow the packet through if it has previous been added to the
# the "dynamic" rules table by a allow keep-state statement.
#################################################################
&dollar;cmd 00015 check-state

#################################################################
# Interface facing Public Internet (Outbound Section)
# Interrogate session start requests originating from behind the
# firewall on the private network or from this gateway server
# destined for the public Internet.
#################################################################

# Allow out access to my ISP's Domain name server.
# x.x.x.x must be the IP address of your ISP.s DNS
# Dup these lines if your ISP has more than one DNS server
# Get the IP addresses from /etc/resolv.conf file
&dollar;cmd 00110 allow tcp from any to x.x.x.x 53 out via &dollar;pif setup keep-state
&dollar;cmd 00111 allow udp from any to x.x.x.x 53 out via &dollar;pif keep-state

# Allow out access to my ISP's DHCP server for cable/DSL configurations.
# This rule is not needed for .user ppp. connection to the public Internet.
# so you can delete this whole group.
# Use the following rule and check log for IP address.
# Then put IP address in commented out rule &amp; delete first rule
&dollar;cmd 00120 allow log udp from any to any 67 out via &dollar;pif keep-state
#&dollar;cmd 00120 allow udp from any to x.x.x.x 67 out via &dollar;pif keep-state

# Allow out non-secure standard www function
&dollar;cmd 00200 allow tcp from any to any 80 out via &dollar;pif setup keep-state

# Allow out secure www function https over TLS SSL
&dollar;cmd 00220 allow tcp from any to any 443 out via &dollar;pif setup keep-state

# Allow out send &amp; get email function
&dollar;cmd 00230 allow tcp from any to any 25 out via &dollar;pif setup keep-state
&dollar;cmd 00231 allow tcp from any to any 110 out via &dollar;pif setup keep-state

# Allow out FBSD (make install &amp; CVSUP) functions
# Basically give user root "GOD" privileges.
&dollar;cmd 00240 allow tcp from me to any out via &dollar;pif setup keep-state uid root

# Allow out ping
&dollar;cmd 00250 allow icmp from any to any out via &dollar;pif keep-state

# Allow out Time
&dollar;cmd 00260 allow tcp from any to any 37 out via &dollar;pif setup keep-state

# Allow out nntp news (i.e., news groups)
&dollar;cmd 00270 allow tcp from any to any 119 out via &dollar;pif setup keep-state

# Allow out secure FTP, Telnet, and SCP
# This function is using SSH (secure shell)
&dollar;cmd 00280 allow tcp from any to any 22 out via &dollar;pif setup keep-state

# Allow out whois
&dollar;cmd 00290 allow tcp from any to any 43 out via &dollar;pif setup keep-state

# deny and log everything else that.s trying to get out.
# This rule enforces the block all by default logic.
&dollar;cmd 00299 deny log all from any to any out via &dollar;pif

#################################################################
# Interface facing Public Internet (Inbound Section)
# Check packets originating from the public Internet
# destined for this gateway server or the private network.
#################################################################

# Deny all inbound traffic from non-routable reserved address spaces
&dollar;cmd 00300 deny all from 192.168.0.0/16 to any in via &dollar;pif  #RFC 1918 private IP
&dollar;cmd 00301 deny all from 172.16.0.0/12 to any in via &dollar;pif     #RFC 1918 private IP
&dollar;cmd 00302 deny all from 10.0.0.0/8 to any in via &dollar;pif          #RFC 1918 private IP
&dollar;cmd 00303 deny all from 127.0.0.0/8 to any in via &dollar;pif        #loopback
&dollar;cmd 00304 deny all from 0.0.0.0/8 to any in via &dollar;pif            #loopback
&dollar;cmd 00305 deny all from 169.254.0.0/16 to any in via &dollar;pif   #DHCP auto-config
&dollar;cmd 00306 deny all from 192.0.2.0/24 to any in via &dollar;pif       #reserved for docs
&dollar;cmd 00307 deny all from 204.152.64.0/23 to any in via &dollar;pif  #Sun cluster interconnect
&dollar;cmd 00308 deny all from 224.0.0.0/3 to any in via &dollar;pif         #Class D &amp; E multicast

# Deny public pings
&dollar;cmd 00310 deny icmp from any to any in via &dollar;pif

# Deny ident
&dollar;cmd 00315 deny tcp from any to any 113 in via &dollar;pif

# Deny all Netbios service. 137=name, 138=datagram, 139=session
# Netbios is MS/Windows sharing services.
# Block MS/Windows hosts2 name server requests 81
&dollar;cmd 00320 deny tcp from any to any 137 in via &dollar;pif
&dollar;cmd 00321 deny tcp from any to any 138 in via &dollar;pif
&dollar;cmd 00322 deny tcp from any to any 139 in via &dollar;pif
&dollar;cmd 00323 deny tcp from any to any 81 in via &dollar;pif

# Deny any late arriving packets
&dollar;cmd 00330 deny all from any to any frag in via &dollar;pif

# Deny ACK packets that did not match the dynamic rule table
&dollar;cmd 00332 deny tcp from any to any established in via &dollar;pif

# Allow traffic in from ISP's DHCP server. This rule must contain
# the IP address of your ISP.s DHCP server as it.s the only
# authorized source to send this packet type.
# Only necessary for cable or DSL configurations.
# This rule is not needed for .user ppp. type connection to
# the public Internet. This is the same IP address you captured
# and used in the outbound section.
#&dollar;cmd 00360 allow udp from any to x.x.x.x 67 in via &dollar;pif keep-state

# Allow in standard www function because I have apache server
&dollar;cmd 00400 allow tcp from any to me 80 in via &dollar;pif setup limit src-addr 2

# Allow in secure FTP, Telnet, and SCP from public Internet
&dollar;cmd 00410 allow tcp from any to me 22 in via &dollar;pif setup limit src-addr 2

# Allow in non-secure Telnet session from public Internet
# labeled non-secure because ID &amp; PW are passed over public
# Internet as clear text.
# Delete this sample group if you do not have telnet server enabled.
&dollar;cmd 00420 allow tcp from any to me 23 in via &dollar;pif setup limit src-addr 2

# Reject &amp; Log all incoming connections from the outside
&dollar;cmd 00499 deny log all from any to any in via &dollar;pif

# Everything else is denied by default
# deny and log all packets that fell through to see what they are
&dollar;cmd 00999 deny log all from any to any
################ End of IPFW rules file ###############################</programlisting>
      </sect3>

      <sect3>
	<title>An Example <acronym>NAT</acronym> and Stateful
	  Ruleset</title>

	<indexterm>
	  <primary>NAT</primary>

	  <secondary>and IPFW</secondary>
	</indexterm>

	<para>There are some additional configuration statements that
	  need to be enabled to activate the <acronym>NAT</acronym>
	  function of IPFW.  For a customized kernel, the kernel
	  configuration file needs
	  <literal>option IPDIVERT</literal> added to the other
	  <literal>IPFIREWALL</literal> options.</para>

	<para>In addition to the normal IPFW options in
	  <filename>/etc/rc.conf</filename>, the following are
	  needed:</para>

	<programlisting>natd_enable="YES"                   # Enable <acronym>NAT</acronym>D function
natd_interface="rl0"                # interface name of public Internet NIC
natd_flags="-dynamic -m"            # -m = preserve port numbers if possible</programlisting>

	<para>Utilizing stateful rules with a
	  <literal>divert natd</literal> rule complicates the ruleset
	  logic.  The positioning of the
	  <literal>check-state</literal>, and
	  <literal>divert natd</literal> rules in the ruleset is
	  critical and a new action type is used, called
	  <literal>skipto</literal>.  When using
	  <literal>skipto</literal>, it is mandatory that each rule is
	  numbered, so that the <literal>skipto</literal> rule knows
	  which rule to jump to.</para>

	<para>The following is an uncommented example of a ruleset
	  which explains the sequence of the packet flow.</para>

	<para>The processing flow starts with the first rule from the
	  top of the ruleset and progresses one rule at a time until
	  the end is reached or the packet matches and the packet is
	  released out of the firewall.  Take note of the location of
	  rule numbers 100 101, 450, 500, and 510.  These rules
	  control the translation of the outbound and inbound packets
	  so that their entries in the dynamic keep-state table always
	  register the private LAN IP address.  All the allow and deny
	  rules specify the direction of the packet and the interface.
	  All start outbound session requests will
	  <literal>skipto rule 500</literal> to undergo NAT.</para>

	<para>Consider a web browser which initializes a new HTTP
	  session over port 80.  When the first outbound packet enters
	  the firewall, it does not match rule 100 because it is
	  headed out rather than in.  It passes rule 101 because this
	  is the first packet, and it has not been posted to the
	  dynamic keep-state table yet.  The packet finally matches
	  rule 125 as it is outbound through the NIC facing the
	  Internet and has a source IP address as a private LAN IP
	  address.  On matching this rule, two actions take place.
	  <literal>keep-state</literal> adds this rule to the dynamic
	  keep-state rules table and the specified action is executed
	  and posted as part of the info in the dynamic table.  In
	  this case, the action is <literal>skipto rule 500</literal>.
	  Rule 500 <acronym>NAT</acronym>s the packet IP address and
	  sends it out to the Internet.  This packet makes its way to
	  the destination web server, where a response packet is
	  generated and sent back.  This new packet enters the top of
	  the ruleset.  It matches rule 100 and has it destination IP
	  address mapped back to the corresponding LAN IP address.  It
	  then is processed by the <literal>check-state</literal>
	  rule, is found in the table as an existing session, and is
	  released to the LAN.  It goes to the LAN system that sent it
	  and a new packet is sent requesting another segment of the
	  data from the remote server.  This time it matches the
	  <literal>check-state</literal> rule, its outbound entry is
	  found,  and the associated action,
	  <literal>skipto 500</literal>, is executed.  The packet
	  jumps to rule 500, gets <acronym>NAT</acronym>ed, and is
	  released to the Internet.</para>

	<para>On the inbound side, everything coming in that is part
	  of an existing session is automatically handled by the
	  <literal>check-state</literal> rule and the properly placed
	  <literal>divert natd</literal> rules.  The ruleset only has
	  to deny bad packets and allow only authorized services.
	  Consider a web server running on the firewall where web
	  requests from the Internet should have access to the local
	  web site.  An inbound start request packet will match rule
	  100 and its IP address will be mapped to the LAN IP address
	  of the firewall.  The packet is then matched against all the
	  nasty things that need to be checked and finally matches
	  rule 425 where two actions occur.  The packet rule is posted
	  to the dynamic keep-state table but this time, any new
	  session requests originating from that source IP address are
	  limited to 2.  This defends against DoS attacks against the
	  service running on the specified port number.  The action is
	  <literal>allow</literal>, so the packet is released to the
	  LAN. The packet generated as a response is recognized by the
	  <literal>check-state</literal> as belonging to an existing
	  session.  It is then sent to rule 500 for
	  <acronym>NAT</acronym>ing and released to the outbound
	  interface.</para>

	<para>Example Ruleset #1:</para>

	<programlisting>#!/bin/sh
cmd="ipfw -q add"
skip="skipto 500"
pif=rl0
ks="keep-state"
good_tcpo="22,25,37,43,53,80,443,110,119"

ipfw -q -f flush

&dollar;cmd 002 allow all from any to any via xl0  # exclude LAN traffic
&dollar;cmd 003 allow all from any to any via lo0  # exclude loopback traffic

&dollar;cmd 100 divert natd ip from any to any in via &dollar;pif
&dollar;cmd 101 check-state

# Authorized outbound packets
&dollar;cmd 120 &dollar;skip udp from any to xx.168.240.2 53 out via &dollar;pif &dollar;ks
&dollar;cmd 121 &dollar;skip udp from any to xx.168.240.5 53 out via &dollar;pif &dollar;ks
&dollar;cmd 125 &dollar;skip tcp from any to any &dollar;good_tcpo out via &dollar;pif setup &dollar;ks
&dollar;cmd 130 &dollar;skip icmp from any to any out via &dollar;pif &dollar;ks
&dollar;cmd 135 &dollar;skip udp from any to any 123 out via &dollar;pif &dollar;ks


# Deny all inbound traffic from non-routable reserved address spaces
&dollar;cmd 300 deny all from 192.168.0.0/16  to any in via &dollar;pif  #RFC 1918 private IP
&dollar;cmd 301 deny all from 172.16.0.0/12   to any in via &dollar;pif  #RFC 1918 private IP
&dollar;cmd 302 deny all from 10.0.0.0/8      to any in via &dollar;pif  #RFC 1918 private IP
&dollar;cmd 303 deny all from 127.0.0.0/8     to any in via &dollar;pif  #loopback
&dollar;cmd 304 deny all from 0.0.0.0/8       to any in via &dollar;pif  #loopback
&dollar;cmd 305 deny all from 169.254.0.0/16  to any in via &dollar;pif  #DHCP auto-config
&dollar;cmd 306 deny all from 192.0.2.0/24    to any in via &dollar;pif  #reserved for docs
&dollar;cmd 307 deny all from 204.152.64.0/23 to any in via &dollar;pif  #Sun cluster
&dollar;cmd 308 deny all from 224.0.0.0/3     to any in via &dollar;pif  #Class D &amp; E multicast

# Authorized inbound packets
&dollar;cmd 400 allow udp from xx.70.207.54 to any 68 in &dollar;ks
&dollar;cmd 420 allow tcp from any to me 80 in via &dollar;pif setup limit src-addr 1


&dollar;cmd 450 deny log ip from any to any

# This is skipto location for outbound stateful rules
&dollar;cmd 500 divert natd ip from any to any out via &dollar;pif
&dollar;cmd 510 allow ip from any to any

######################## end of rules  ##################</programlisting>

	<para>The next example is functionally equivalent, but uses
	  descriptive comments to help the inexperienced IPFW rule
	  writer to better understand what the rules are doing.</para>

	<para>Example Ruleset #2:</para>

	<programlisting>#!/bin/sh
################ Start of IPFW rules file ###############################
# Flush out the list before we begin.
ipfw -q -f flush

# Set rules command prefix
cmd="ipfw -q add"
skip="skipto 800"
pif="rl0"     # public interface name of NIC
              # facing the public Internet

#################################################################
# No restrictions on Inside LAN Interface for private network
# Change xl0 to your LAN NIC interface name
#################################################################
&dollar;cmd 005 allow all from any to any via xl0

#################################################################
# No restrictions on Loopback Interface
#################################################################
&dollar;cmd 010 allow all from any to any via lo0

#################################################################
# check if packet is inbound and nat address if it is
#################################################################
&dollar;cmd 014 divert natd ip from any to any in via &dollar;pif

#################################################################
# Allow the packet through if it has previous been added to the
# the "dynamic" rules table by a allow keep-state statement.
#################################################################
&dollar;cmd 015 check-state

#################################################################
# Interface facing Public Internet (Outbound Section)
# Check session start requests originating from behind the
# firewall on the private network or from this gateway server
# destined for the public Internet.
#################################################################

# Allow out access to my ISP's Domain name server.
# x.x.x.x must be the IP address of your ISP's DNS
# Dup these lines if your ISP has more than one DNS server
# Get the IP addresses from /etc/resolv.conf file
&dollar;cmd 020 &dollar;skip tcp from any to x.x.x.x 53 out via &dollar;pif setup keep-state


# Allow out access to my ISP's DHCP server for cable/DSL configurations.
&dollar;cmd 030 &dollar;skip udp from any to x.x.x.x 67 out via &dollar;pif keep-state

# Allow out non-secure standard www function
&dollar;cmd 040 &dollar;skip tcp from any to any 80 out via &dollar;pif setup keep-state

# Allow out secure www function https over TLS SSL
&dollar;cmd 050 &dollar;skip tcp from any to any 443 out via &dollar;pif setup keep-state

# Allow out send &amp; get email function
&dollar;cmd 060 &dollar;skip tcp from any to any 25 out via &dollar;pif setup keep-state
&dollar;cmd 061 &dollar;skip tcp from any to any 110 out via &dollar;pif setup keep-state

# Allow out FreeBSD (make install &amp; CVSUP) functions
# Basically give user root "GOD" privileges.
&dollar;cmd 070 &dollar;skip tcp from me to any out via &dollar;pif setup keep-state uid root

# Allow out ping
&dollar;cmd 080 &dollar;skip icmp from any to any out via &dollar;pif keep-state

# Allow out Time
&dollar;cmd 090 &dollar;skip tcp from any to any 37 out via &dollar;pif setup keep-state

# Allow out nntp news (i.e., news groups)
&dollar;cmd 100 &dollar;skip tcp from any to any 119 out via &dollar;pif setup keep-state

# Allow out secure FTP, Telnet, and SCP
# This function is using SSH (secure shell)
&dollar;cmd 110 &dollar;skip tcp from any to any 22 out via &dollar;pif setup keep-state

# Allow out whois
&dollar;cmd 120 &dollar;skip tcp from any to any 43 out via &dollar;pif setup keep-state

# Allow ntp time server
&dollar;cmd 130 &dollar;skip udp from any to any 123 out via &dollar;pif keep-state

#################################################################
# Interface facing Public Internet (Inbound Section)
# Check packets originating from the public Internet
# destined for this gateway server or the private network.
#################################################################

# Deny all inbound traffic from non-routable reserved address spaces
&dollar;cmd 300 deny all from 192.168.0.0/16  to any in via &dollar;pif  #RFC 1918 private IP
&dollar;cmd 301 deny all from 172.16.0.0/12   to any in via &dollar;pif  #RFC 1918 private IP
&dollar;cmd 302 deny all from 10.0.0.0/8      to any in via &dollar;pif  #RFC 1918 private IP
&dollar;cmd 303 deny all from 127.0.0.0/8     to any in via &dollar;pif  #loopback
&dollar;cmd 304 deny all from 0.0.0.0/8       to any in via &dollar;pif  #loopback
&dollar;cmd 305 deny all from 169.254.0.0/16  to any in via &dollar;pif  #DHCP auto-config
&dollar;cmd 306 deny all from 192.0.2.0/24    to any in via &dollar;pif  #reserved for docs
&dollar;cmd 307 deny all from 204.152.64.0/23 to any in via &dollar;pif  #Sun cluster
&dollar;cmd 308 deny all from 224.0.0.0/3     to any in via &dollar;pif  #Class D &amp; E multicast

# Deny ident
&dollar;cmd 315 deny tcp from any to any 113 in via &dollar;pif

# Deny all Netbios service. 137=name, 138=datagram, 139=session
# Netbios is MS/Windows sharing services.
# Block MS/Windows hosts2 name server requests 81
&dollar;cmd 320 deny tcp from any to any 137 in via &dollar;pif
&dollar;cmd 321 deny tcp from any to any 138 in via &dollar;pif
&dollar;cmd 322 deny tcp from any to any 139 in via &dollar;pif
&dollar;cmd 323 deny tcp from any to any 81  in via &dollar;pif

# Deny any late arriving packets
&dollar;cmd 330 deny all from any to any frag in via &dollar;pif

# Deny ACK packets that did not match the dynamic rule table
&dollar;cmd 332 deny tcp from any to any established in via &dollar;pif

# Allow traffic in from ISP's DHCP server. This rule must contain
# the IP address of your ISP's DHCP server as it is the only
# authorized source to send this packet type.
# Only necessary for cable or DSL configurations.
# This rule is not needed for 'user ppp' type connection to
# the public Internet. This is the same IP address you captured
# and used in the outbound section.
&dollar;cmd 360 allow udp from x.x.x.x to any 68 in via &dollar;pif keep-state

# Allow in standard www function because I have Apache server
&dollar;cmd 370 allow tcp from any to me 80 in via &dollar;pif setup limit src-addr 2

# Allow in secure FTP, Telnet, and SCP from public Internet
&dollar;cmd 380 allow tcp from any to me 22 in via &dollar;pif setup limit src-addr 2

# Allow in non-secure Telnet session from public Internet
# labeled non-secure because ID &amp; PW are passed over public
# Internet as clear text.
# Delete this sample group if you do not have telnet server enabled.
&dollar;cmd 390 allow tcp from any to me 23 in via &dollar;pif setup limit src-addr 2

# Reject &amp; Log all unauthorized incoming connections from the public Internet
&dollar;cmd 400 deny log all from any to any in via &dollar;pif

# Reject &amp; Log all unauthorized out going connections to the public Internet
&dollar;cmd 450 deny log all from any to any out via &dollar;pif

# This is skipto location for outbound stateful rules
&dollar;cmd 800 divert natd ip from any to any out via &dollar;pif
&dollar;cmd 801 allow ip from any to any

# Everything else is denied by default
# deny and log all packets that fell through to see what they are
&dollar;cmd 999 deny log all from any to any
################ End of IPFW rules file ###############################</programlisting>
      </sect3>
    </sect2>
  </sect1>
</chapter>