<!--
     The FreeBSD Documentation Project
     The FreeBSD Simplified Chinese Project

     Original Revision: 1.57
     $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>防火墙</title>

  <indexterm><primary>防火墙</primary></indexterm>

  <indexterm>
    <primary>安全</primary>

    <secondary>防火墙</secondary>
  </indexterm>

  <sect1 id="firewalls-intro">
    <title>入门</title>

    <para>防火墙的存在， 使得过滤出入系统的数据流成为可能。
      防火墙可以使用一组或多组 <quote>规则 (rules)</quote>，
      来检查出入您的网络连接的数据包， 并决定允许或阻止它们通过。
      这些规则通常可以检查数据包的某个或某些特征，
      这些特征包括， 但不必限于协议类型、 来源或目的主机地址，
      以及来源或目的端口。</para>

    <para>防火墙可以大幅度地改善主机或网络的安全。
      它可以用来完成下面的任务：</para>

    <itemizedlist>
      <listitem>
	<para>保护和隔离应用程序、 服务程序， 以及您内部网络上的机器，
	  不受那些来自公共的 Internet 网络上您所不希望的数据流量的干扰。</para>
      </listitem>

      <listitem>
	<para>限制或禁止从内部网访问公共的 Internet 上的服务。</para>
      </listitem>

      <listitem>
	<para>支持网络地址转换
	  (<acronym>NAT</acronym>)，
	  它使得您的内部网络能够使用私有的 <acronym>IP</acronym> 地址，
	  并分享一条通往公共的 Internet 的连接 (使用一个
	  <acronym>IP</acronym> 地址， 或者一组公网地址)。</para>
      </listitem>
    </itemizedlist>

    <para>读完这章， 您将了解：</para>

    <itemizedlist>
      <listitem>
	<para>如何正确地定义包过滤规则。</para>
      </listitem>

      <listitem>
	<para>&os; 中内建的集中防火墙之间的差异。</para>
      </listitem>

      <listitem>
	<para>如何使用和配置 OpenBSD 的
	  <application>PF</application> 防火墙。</para>
      </listitem>

      <listitem>
	<para>如何使用和配置
	  <application>IPFILTER</application>。</para>
      </listitem>

      <listitem>
	<para>如何使用和配置
	  <application>IPFW</application>。</para>
      </listitem>
    </itemizedlist>

    <para>阅读这章之前， 您需要：</para>

    <itemizedlist>
      <listitem>
	<para>理解基本的 &os; 和 Internet 概念。</para>
      </listitem>
    </itemizedlist>
  </sect1>

  <sect1 id="firewalls-concepts">
    <title>防火墙的概念</title>

    <indexterm>
      <primary>防火墙/primary>

      <secondary>规则集</secondary>
    </indexterm>

    <para>建立防火墙规则集的基本方法有两种：
      <quote>包容式的</quote> 或 <quote>排斥式的</quote>。
      排斥式的防火墙， 允许除了禁止的那些数据之外的所有网络流量通过。
      包容式的防火墙正好相反。 后者只允许符合规则的流量通过，
      而其他所有的流量都被阻止。</para>

    <para>包容式防火墙一般说来要比排斥式防火墙安全，
      因为他们显著地降低了由于允许不希望的网络流量通过所带来的风险。</para>

    <para>如果使用了 <quote>带状态功能的防火墙 (stateful
	firewall)</quote>， 则安全机制可以进一步地细化。
      带状态功能的防火墙能够记录通过防火墙的连接，
      进而只允许与现有连接匹配的连接， 或创建新的连接。
      带状态功能的防火墙的缺点， 则是在很短时间内有大量的连接请求时，
      它们可能会受到拒绝服务
      (<acronym>DoS</acronym>) 攻击。
      绝大多数防火墙都提供了同时启用两种防火墙的能力，
      以便为站点提供更好的保护。</para>
  </sect1>

  <sect1 id="firewalls-apps">
    <title>防火墙软件包</title>

    <para>&os; 的基本系统内建了三种不同的防火墙软件包。
      它们是 <emphasis>IPFILTER</emphasis>
      (也被称作 <acronym>IPF</acronym>)、
      <emphasis>IPFIREWALL</emphasis> (也被称作 <acronym>IPFW</acronym>)，
      以及 <emphasis>OpenBSD 的 PacketFilter</emphasis> (也被称为
      <acronym>PF</acronym>)。 &os; 也提供了两个内建的、
      用于流量整形 (基本上是控制带宽占用) 的软件包：
      &man.altq.4; 和 &man.dummynet.4;。 Dummynet 在过去一直和
      <acronym>IPFW</acronym> 紧密集成， 而
      <acronym>ALTQ</acronym> 则需要配合
      <acronym>IPF</acronym>/<acronym>PF</acronym> 使用。 IPF、
      IPFW， 以及 PF 都是用规则来控制是否允许数据包出入您的系统，
      虽然它们采取了不同的实现方法和规则语法。</para>

    <para>&os; 包含多个内建的防火墙软件包的原因在于，
      不同的人会有不同的需求和偏好。 任何一个防火墙软件包都很难说是最好的。</para>

    <para>作者倾向于使用 IPFILTER， 因为它提供的状态式规则，
      在 <acronym>NAT</acronym> 的环境中要简单许多，
      而且它内建了 ftp 代理， 这简化了使用外部 FTP 服务时所需的配置。</para>

    <para>由于所有的防火墙都基于检查所选定的包控制字段来实现功能，
      撰写防火墙规则集时， 就必须了解
      <acronym>TCP</acronym>/IP 是如何工作的，
      以及包的控制字段在正常会话交互中的作用。
      您可以在这个网站找到一份很好的解释文档：
      <ulink
	url="http://www.ipprimer.com/overview.cfm"></ulink>.</para>
  </sect1>

  <sect1 id="firewalls-pf">
    <title>OpenBSD Packet Filter (PF) 和
      <acronym>ALTQ</acronym></title>

    <indexterm>
      <primary>防火墙</primary>

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

    <para>2003 年 7 月， OpenBSD 的防火墙，
      也就是常说的 <acronym>PF</acronym> 被成功地移植到了 &os; 上，
      并可以通过 &os; Ports Collection 来安装了；
      第一个将 <acronym>PF</acronym> 集成到基本系统中的版本是
      2004 年 11 月发行的 &os;&nbsp;5.3。
      <acronym>PF</acronym> 是一个完整的提供了大量功能的防火墙软件，
      并提供了可选的 <acronym>ALTQ</acronym> (交错队列， Alternate
      Queuing) 功能。 <acronym>ALTQ</acronym> 提供了服务品质
      (<acronym>QoS</acronym>) 带宽整形功能，
      这个功能能够以基于过滤规则的方式来保障不同服务的带宽。
      OpenBSD Project 在维护 PF 用户指南方面已经做了非常卓越的工作，
      因此我们不打算在这本使用手册中进行更进一步的阐述，
      以避免不必要的重复劳动。</para>

    <para>PF 在各种 &os; 发行版上的可用情况如下所示：</para>

    <informaltable frame="none" pgwide="1">
      <tgroup cols="2">
	<thead>
	  <row>
	    <entry>&os; 版本</entry>

	    <entry>PF 可用情况</entry>
	  </row>
	</thead>

	<tbody>
	  <row>
	    <entry>4.X 之前的版本</entry>

	    <entry>PF 在 &os; 4.X 之前的版本分支上不可用。</entry>
	  </row>

	  <row>
	    <entry>所有 4.X 版本分支上的版本</entry>

	    <entry>PF 作为 KAME 的一部分提供。</entry>
	  </row>

	  <row>
	    <entry>5.X 在 5.3-RELEASE 之前的版本</entry>

	    <entry><filename role="package">security/pf</filename>
	      port 可以用来在这些 &os; 版本上安装 PF。
	      这些版本主要是供开发人员， 以及预览早期的 5.X
	      版本上使用的。 强烈建议升级到
	      5.3-RELEASE 或更新的 &os; 版本。</entry>
	  </row>

	  <row>
	    <entry>5.3-RELEASE 以及之后的版本</entry>

	    <entry>PF 已经成为了基本系统的一部分。
	      请 <emphasis>不要</emphasis> 尝试使用 <filename
		role="package">security/pf</filename> port
	      在这些 &os; 版本上安装它， 因为这样做是没有作用的。
	      请使用基本系统中的 &man.pf.4; 支持。</entry>
	  </row>
	</tbody>
      </tgroup>
    </informaltable>

    <para>更多的详细信息， 可以在 &os; 版本的 PF 网站上找到： <ulink
	url="http://pf4freebsd.love2party.net/"></ulink>。</para>

    <para>OpenBSD PF 用户指南可以在这里找到：<ulink
	url="http://www.openbsd.org/faq/pf/"></ulink>。</para>

    <warning>
      <para>在 &os; 5.X 上的 PF 相当于 OpenBSD 3.5 版本。 以
	port 形式出现在 &os; Ports Collection 的版本相当于 OpenBSD
	的 3.4 版。 在阅读用户指南时， 请注意这样的区别。</para>
    </warning>

    <sect2>
      <title>启用 PF</title>

      <para>PF 作为 &os; 5.3 和更高版本基本系统安装的一部分，
	作为一个可以动态加载的模块出现。 如果在 rc.conf 中配置了
	<literal>pf_enable="YES"</literal> 则系统会自动加载对应的内核模块。
	可加载内核模块在构建时启用了 &man.pflog.4;。</para>

      <note>
	<para>这个模块假定 <literal>options
	    INET</literal> 和 <literal>device bpf</literal> 是存在的。 除非编译时指定了
	  <literal>NOINET6</literal> (例如在 &man.make.conf.5; 中)
	  则还需要 <literal>options
	    INET6</literal>。</para>
      </note>
    </sect2>

    <sect2>
      <title>内核选项</title>

      <indexterm>
	<primary>内核选项</primary>

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

      <indexterm>
	<primary>内核选项</primary>

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

      <indexterm>
	<primary>内核选项</primary>

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

      <para>将下面这些选项加入到 &os; 内核的编译配置文件中并不是启用
	PF 的强制性要求。 这里列出它们主要是为了介绍一些背景信息。
	将 PF 编译到内核中之后， 就不再需要使用可加载内核模块了。</para>

      <para>如何在内核编译配置中加入对于 PF 选项的例子可以在内核源代码中的
	<filename>/usr/src/sys/conf/NOTES</filename> 这个文件中找到。
	这里列举如下：</para>

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

      <para><literal>device pf</literal> 用于启用
	<quote>Packet Filter</quote> 防火墙的支持。</para>

      <para><literal>device pflog</literal> 启用可选的
	&man.pflog.4; 伪网络设备， 用以通过 &man.bpf.4;
	描述符来记录流量。 &man.pflogd.8; 服务可以用来存储信息，
	并把它们以日志形式记录到磁盘上。</para>

      <para><literal>device pfsync</literal> 启用可选的
	&man.pfsync.4; 伪网络设备， 用以监视
	<quote>状态变更</quote>。 由于这不是那个可加载内核模块的一部分，
	因此如果需要使用它， 就必须自行编译定制的内核了。</para>

      <para>这些设置只有在您使用它们构建和安装新内核之后才会生效。</para>
    </sect2>

    <sect2>
      <title>可用的 rc.conf 选项</title>

      <para>您需要在 <filename>/etc/rc.conf
	</filename> 中添加如下配置， 以便在启动时激活 PF：</para>

      <programlisting>pf_enable="YES"                 # 启用 PF (如果需要的话， 自动加载内核模块)
pf_rules="/etc/pf.conf"         # pf 使用的规则定义文件
pf_flags=""                     # 启动时传递给 pfctl 的其他选项
pflog_enable="YES"              # 启动 pflogd(8)
pflog_logfile="/var/log/pflog"  # pflogd 用于记录日志的文件名
pflog_flags=""                  # 启动时传递给 pflogd 的其他选项</programlisting>

      <para>如果您的防火墙后面有一个 LAN，
	而且需要通过它来转发 LAN 上的包， 或进行 NAT，
	还必须同时启用下述选项：</para>

      <programlisting>gateway_enable="YES"            # 启用作为 LAN 网关的功能</programlisting>
    </sect2>

    <sect2>
      <title>启用 <acronym>ALTQ</acronym></title>

      <para><acronym>ALTQ</acronym> 只有在作为编译选项加入到 &os; 内核时，
	才能使用。 <acronym>ALTQ</acronym> 目前还不是所有的可用网卡驱动都能够支持的。
	请参见 &man.altq.4; 联机手册了解您正使用的 &os; 版本中的驱动支持情况。
	下面这些选项将启用 <acronym>ALTQ</acronym> 以及一些附加的功能。</para>

      <programlisting>options         ALTQ
options         ALTQ_CBQ        # Class Bases 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> 将启用
	<acronym>ALTQ</acronym> 框架的支持。</para>

      <para><literal>options ALTQ_CBQ</literal>
	用于启用基于分类的队列 (<acronym>CBQ</acronym>) 支持。 <acronym>CBQ</acronym>
	允许您将连接分成不同的类别， 或者说， 队列，
	以便在规则中为它们指定不同的优先级。</para>

      <para><literal>options ALTQ_RED</literal>
	将启用随机预检测 (<acronym>RED</acronym>)。
	<acronym>RED</acronym> 是一种用于防止网络拥塞的技术。
	<acronym>RED</acronym> 度量队列的长度，
	并将其与队列的最大和最小长度阈值进行比较。
	如果队列过长， 则新的包将被丢弃。
	如名所示， <acronym>RED</acronym> 从不同的连接中随机地丢弃数据包。</para>

      <para><literal>options ALTQ_RIO</literal> 将启用出入的随机预检测。</para>

      <para><literal>options ALTQ_HFSC</literal>
	启用层次式公平服务平滑包调度器。
	要了解关于 <acronym>HFSC</acronym> 进一步的信息， 请参见 <ulink
	  url="http://www-2.cs.cmu.edu/~hzhang/HFSC/main.html"></ulink>。</para>

      <para><literal>options ALTQ_PRIQ</literal> 启用优先队列
	(<acronym>PRIQ</acronym>)。 <acronym>PRIQ</acronym>
	首先允许高优先级队列中的包通过。</para>

      <para><literal>options ALTQ_NOPCC</literal> 启用
	<acronym>ALTQ</acronym> 的
	<acronym>SMP</acronym> 支持。
	如果是 <acronym>SMP</acronym> 系统，
	则必须使用它。</para>
    </sect2>
  </sect1>

  <sect1 id="firewalls-ipf">
    <title>IPFILTER (IPF) 防火墙</title>

    <indexterm>
      <primary>防火墙</primary>

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

    <note>
      <para>这一节的内容正在撰写中。 其内容可能不总是十分准确。</para>
    </note>

    <para>IPFILTER 的作者是 Darren Reed。 IPFILTER 是独立于操作系统的：
      它是一个开放源代码的应用， 并且已经被移植到了 &os;、 NetBSD、
      OpenBSD、 SunOS、 HP/UX， 以及 Solaris 操作系统上。
      IPFILTER 的支持和维护都相当活跃， 并且有规律地发布更新版本。</para>

    <para>IPFILTER 基于内核模式的防火墙和
      <acronym>NAT</acronym> 机制，
      这些机制可以通过用户模式运行的接口程序中进行控制。
      防火墙规则可以使用 &man.ipf.8; 工具来动态地设置和删除。
      <acronym>NAT</acronym> 规则可以通过
      &man.ipnat.1; 工具来维护。 &man.ipfstat.8; 工具则可以用来显示
      IPFILTER 内核部分的统计数据。 最后， 使用
      &man.ipmon.8; 程序可以把 IPFILTER 的动作记录到系统日志文件中。</para>

    <para>IPF 最初是使用一组
      <quote>以最后匹配的规则为准</quote> 的策略来实现的，
      这种方式只能支持无状态的规则。 随着时代的进步，
      IPF 被逐渐增强， 并加入了 <quote>quick</quote> 选项，
      以及支持状态的 <quote>keep
      state</quote> 选项， 这使得规则处理逻辑变得更富有现代气息。
      IPF 的官方文档介绍了传统的规则编写方法和文件处理逻辑。
      新增的功能只是作为一些附加的选项出现， 如果能完全理解这些功能，
      则对于建立更安全的防火墙就很有好处。</para>

    <para>这一节中主要是针对 <quote>quick</quote> 选项，
      以及支持状态的 <quote>keep state</quote> 选项的介绍。
      这是包容式防火墙规则集最基本的编写要素。</para>

    <!-- XXX: something like this already in
	 <xref linkend="firewalls-concepts">
	 AND: the para below is repeated 3 times in this chapter-->

    <para>包容式防火墙只允许与规则匹配的包通过。 这样，
      您就既能够控制来自防火墙后面的机器请求 Internet 公网上的那些服务，
      同时也可以控制来自 Internet 的请求能够访问内部网上的哪些服务。
      所有其它的访问请求都会被阻止， 并记录下来。
      包容式防火墙一般而言要远比排斥式的要安全，
      而且也只需要定义允许哪些访问通过。</para>

    <para>要获得关于传统规则处理方式的详细信息，
      请参考： <ulink
	url="http://www.obfuscation.org/ipf/ipf-howto.html#TOC_1"></ulink>
      以及 <ulink
	url="http://coombs.anu.edu.au/~avalon/ip-filter.html"></ulink>。</para>

    <para>IPF FAQ 可以在 <ulink
	url="http://www.phildev.net/ipf/index.html"></ulink> 找到。</para>

    <sect2>
      <title>启用 IPF</title>

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>启用</secondary>
      </indexterm>

      <para>IPF 作为 &os; 基本安装的一部分，
	以一个独立的内核模块的形式提供。
	如果在 rc.conf 中配置了 <literal>
	ipfilter_enable="YES"</literal>，
	系统就会自动地动态加载 IPF 内核模块。
	这个内核模块在创建时启用了日志支持， 并加入了 <literal>default
	pass all</literal> 选项。 如果只是需要把默认的规则设置为
	<literal>block all</literal> 的话， 并不需要把 IPF
	编译到内核中。 可以简单地通过把这条规则加入自己的规则集来达到同样的目的。</para>
    </sect2>

    <sect2>
      <title>内核选项</title>

      <indexterm>
	<primary>内核选项</primary>

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

      <indexterm>
	<primary>内核选项</primary>

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

      <indexterm>
	<primary>内核选项</primary>

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

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>内核选项</secondary>
      </indexterm>

      <para>下面这些 &os; 内核编译选项并不是启用 IPF 所必需的。
	这里只是作为背景知识来加以阐述。 如果将 IPF
	编入了内核， 则对应的内核模块将不被使用。</para>

      <para>关于 IPF 选项语句的内核编译配置的例子， 可以在内核源代码中的
	<filename>/usr/src/sys/conf/NOTES</filename> 找到
	(对于 &os;&nbsp;4.X 来说， 则是
	<filename>/usr/src/sys/<replaceable>arch</replaceable>/conf/LINT</filename>)
	并这里列举如下：</para>

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

      <para><literal>options IPFILTER</literal> 用于启用
	<quote>IPFILTER</quote> 防火墙的支持。</para>

      <para><literal>options IPFILTER_LOG</literal> 用于启用 IPF 的日志支持，
	所有匹配了包含 <literal>log</literal> 的规则的包，
	都会被记录到 <devicename>ipl</devicename> 这个包记录伪&mdash;设备中。</para>

      <para><literal>options IPFILTER_DEFAULT_BLOCK</literal>
	将改变防火墙的默认动作， 进而， 所有不匹配防火墙的 <literal>pass</literal>
	规则的包都会被阻止。</para>

      <para>这些选项只有在您重新编译并安装内核之后才会生效。</para>
    </sect2>

    <sect2>
      <title>可用的 rc.conf 选项</title>

      <para>要在启动时激活 IPF， 您需要在 <filename>/etc/rc.conf</filename>
	中增加下面的设置：</para>

      <programlisting>ipfilter_enable="YES"             # 启动 ipf 防火墙
ipfilter_rules="/etc/ipf.rules"   # 将被加载的规则定义， 这是一个文本文件
ipmon_enable="YES"                # 启动 IP 监视日志
ipmon_flags="-Ds"                 # D = 作为服务程序启动
                                  # s = 使用 syslog 记录
                                  # v = 记录 tcp 窗口大小、 ack 和顺序号(seq)
                                  # n = 将 IP 和端口映射为名字</programlisting>
      <para>如果您的 LAN 在防火墙后面， 并且使用了保留的私有 IP 地址范围，
	那就需要增加下面的一些选项来启用 <acronym>NAT</acronym> 功能：</para>

      <programlisting>gateway_enable="YES"              # 启用作为 LAN 网关的功能
ipnat_enable="YES"                # 启动 ipnat 功能
ipnat_rules="/etc/ipnat.rules"    # 用于 ipnat 的规则定义文件</programlisting>

    <sect2>
      <title>IPF</title>

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

      <para>ipf 命令可以用来加载您自己的规则文件。 一般情况下，
	您可以建立一个包括您自定义的规则的文件，
	并使用这个命令来替换掉正在运行的防火墙中的内部规则：</para>

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

      <para><option>-Fa</option> 表示清除所有的内部规则表。</para>

      <para><option>-f</option> 用于指定将要被读取的规则定义文件。</para>

      <para>这个功能使得您能够修改自定义的规则文件， 通过运行上面的 IPF 命令，
	可以将正在运行的防火墙刷新为使用全新的规则集， 而不需要重新启动系统。
	这对于测试新的规则来说就很方便， 因为您可以任意执行上面的命令。</para>

      <para>请参考 &man.ipf.8; 联机手册以了解这个命令提供的其它选项。</para>

      <para>&man.ipf.8; 命令假定规则文件是一个标准的文本文件。
	它不能处理使用符号代换的脚本。</para>

      <para>也确实有办法利用脚本的非常强大的符号替换能力来构建 IPF 规则。
	要了解进一步的细节， 请参考
	<xref linkend="firewalls-ipf-rules-script">。</para>
     </sect2>

    <sect2>
      <title>IPFSTAT</title>

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

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>统计</secondary>
      </indexterm>

      <para>默认情况下， &man.ipfstat.8; 会获取并显示所有的累积统计，
	这些统计是防火墙启动以来用户定义的规则匹配的出入流量，
	您可以通过使用
	<command>ipf -Z</command> 命令来将这些计数器清零。</para>

      <para>请参见 &man.ipfstat.8; 联机手册以了解进一步的细节。</para>

      <para>默认的 &man.ipfstat.8; 命令输出类似于下面的样子：</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>如果使用了 <option>-i</option> (进入流量)
	或者 <option>-o</option> (输出流量)，
	它就只获取并显示内核中所安装的对应过滤器规则的统计数据。</para>

      <para><command>ipfstat -in</command> 以规则号的形式显示进入的内部规则表。</para>

      <para><command>ipfstat -on</command> 以规则号的形式显示流出的内部规则表。</para>

      <para>输出和下面的类似：</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> 显示内部规则表中的进入流量，
	每一个匹配规则前面会同时显示匹配的次数。</para>

      <para><command>ipfstat -oh</command> 显示内部规则表中的流出流量，
	每一个匹配规则前面会同时显示匹配的次数。</para>

      <para>输出和下面的类似：</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><command>ipfstat</command> 命令的一个重要的功能可以通过指定
	<option>-t</option> 参数来使用， 它会以类似 &man.top.1;
	的显示 &os; 正运行的进程表的方式来显示统计数据。
	当您的防火墙正在受到攻击的时候， 这个功能让您得以识别、
	试验， 并查看攻击的数据包。 这个选项提还提供了实时选择希望监视的目的或源 IP、
	端口或协议的能力。 请参见 &man.ipfstat.8; 联机手册以了解详细信息。</para>
    </sect2>

    <sect2>
      <title>IPMON</title>

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

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>记录日志</secondary>
      </indexterm>

      <para>为了使 <command>ipmon</command> 能够正确工作，
	必须打开 IPFILTER_LOG 这个内核选项。 这个命令提供了两种不同的使用模式。
	内建模式是默认的模式， 如果您不指定
	<option>-D</option> 参数， 就会采用这种模式。</para>

      <para>服务模式是持续地通过系统日志来记录的工作模式， 这样，
	您就可以通过查看日志来了解过去曾经发生过的事情。
	这种模式是 &os; 和 IPFILTER 配合工作的模式。
	由于在 &os; 中提供了一个内建的系统日志自动轮转功能，
	因此， 使用 syslogd 比默认的将日志信息记录到一个普通文件要好。
	在默认的 <filename>rc.conf</filename> 文件中， 您会看到一个
	ipmon_flags 语句， 指定了 <option>-Ds</option> 标志：</para>

      <programlisting>ipmon_flags="-Ds"                 # D = 作为服务程序启动
                  # s = 使用 syslog 记录
                  # v = 记录 tcp 窗口大小、 ack 和顺序号(seq)
                  # n = 将 IP 和端口映射为名字</programlisting>

      <para>记录日志的好处是很明显的。 它提供了在事后重新审查相关信息，
	例如哪些包被丢弃， 以及这些包的来源地址等等。
	这将为查找攻击者提供非常有用的第一手资料。</para>

      <para>即使启用了日志机制， IPF 仍然不会对其规则进行任何日志记录工作。
	防火墙管理员可以决定规则集中的哪些应记录日志，
	并在这些规则上加入 log 关键字。 一般来说， 只应记录拒绝性的规则。</para>

      <para>作为惯例， 通常会有一条默认的、拒绝所有网络流量的规则，
	并指定 log 关键字， 作为您的规则集的最后一条。
	这样， 您就能够看到所有没有匹配任何规则的数据包。</para>
    </sect2>

    <sect2>
      <title>IPMON 的日志</title>

      <para><application>Syslogd</application> 使用其自有的方法，
	来对日志数据进行分类。 它使用称为 <quote>facility</quote>
	的组， 以及 <quote>level</quote> 来区分它们。 以 <option>-Ds</option>
	模式运行的 IPMON 会使用 <literal>local0</literal> 作为
	<quote>facility</quote> 名。 所有由 IPMON 记录的数据都会记录到
	<literal>local0</literal>。 如果需要的话， 可以使用下面的
	levels 来区分数据：</para>

      <screen>LOG_INFO - 使用 "log" 关键字指定的通过或阻止动作
LOG_NOTICE - 同时记录通过的那些数据包
LOG_WARNING - 同时记录阻止的数据包
LOG_ERR - 进一步记录含不完整的包头的数据包</screen>
      <!-- XXX: "can be considered short" == "with incomplete header" -->

      <para>要设置 IPFILTER 来将所有的数据记录到
	<filename>/var/log/ipfilter.log</filename>， 需要首先建立这个文件。
	下面的命令可以完成这个工作：</para>

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

      <para>syslog 功能可以通过在 <filename>/etc/syslog.conf</filename> 文件中的语句来定义。
	<filename>syslog.conf</filename> 提供了相当多的用以控制 syslog
	如何处理类似 IPF 这样的用用程序所产生的系统消息的方法。</para>

      <para>将下面的语句加入 <filename>/etc/syslog.conf</filename>：</para>

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

      <para>这里， <literal>local0.*</literal>
	表示把所有的相关日志信息写到指定的位置。</para>

      <para>要让 <filename>/etc/syslog.conf
	</filename> 中的修改立即生效， 您可以重新启动计算机， 或者通过执行
	<command>/etc/rc.d/syslogd reload</command>
	来让它重新读取 <filename>/etc/syslog.conf</filename>
	(在 &os;&nbsp;4.X 中， 则是 <command>killall -HUP syslogd</command>)。</para>

      <para>不要忘了修改 <filename>/etc/newsyslog.conf
	</filename> 来让您刚创建的日志进行轮转。</para>

     </sect2>

     <sect2>
       <title>记录消息的格式</title>

       <para>由 <command>ipmon</command> 生成的消息由空格分隔的数据字段组成。
         所有的消息都包含的字段是：</para>

      <orderedlist>
	<listitem>
	  <para>接到数据包的日期。</para>
	</listitem>

	<listitem>
	  <para>接到数据包的时间。 其格式为
	    HH:MM:SS.F， 分别是小时、 分钟、 秒，
	    以及分秒 (这个数字可能有许多位)。</para>
	</listitem>

	<listitem>
	  <para>处理数据包的网络接口名字，
	    例如 <devicename>dc0</devicename>。</para>
	</listitem>

	<listitem>
	  <para>组和规则的编号， 例如 <literal>@0:17</literal>。</para>
	</listitem>
      </orderedlist>

      <para>可以通过 <command>ipfstat -in</command> 来查看这些信息。</para>

      <orderedlist>
	<listitem>
	  <para>动作： p 表示通过， b 表示阻止， S 表示包头不全，
	    n 表示没有匹配任何规则， L 表示 log 规则。
	    显示这些标志的顺序是： S, p, b, n, L。
	    大写的 P 或 B 表示记录包的原因是某个全局的日志配置，
	    而不是某个特定的规则。</para>
	</listitem>

	<listitem>
	  <para>地址。 这实际上包括三部分：
	    源地址和端口 (以逗号分开)， 一个 ->
	    符号， 以及目的地址和端口。
	    209.53.17.22,80 -> 198.73.220.17,1722.</para>
	</listitem>

	<listitem>
	  <para><literal>PR</literal>， 后跟协议名称或编号，
	    例如， PR tcp。</para>
	</listitem>

	<listitem>
	  <para><literal>len</literal>， 后跟包头的长度， 以及包的总长度，
	    例如 len 20 40。</para>
	</listitem>
      </orderedlist>

      <para>对于 <acronym>TCP</acronym> 包， 则还会包括一个附加的字段，
	由一个连字号开始， 之后是表示所设置的标志的一个字母。
	请参见 &man.ipmon.8; 联机手册， 以了解这些字母所对应的标志。</para>

      <para>对于 ICMP 包， 则在最后会有两个字段。
	前一个总是 <quote>ICMP</quote>， 而后一个则是 ICMP
	消息和子消息的类型， 中间以斜线分靠， 例如 ICMP 3/3
	表示端口不可达消息。</para>
    </sect2>

    <sect2 id="firewalls-ipf-rules-script">
      <title>构建采用符号替换的规则脚本</title>

      <para>一些有经验的 IPF 会创建包含规则的文件，
	并把它编写成能够与符号替换脚本兼容的方式。 这样做最大的好处是，
	它能够让您只修改符号名字所代表的值，
	而在脚本执行时直接替换掉所有的名符。
	作为脚本， 您可以使用符号替换来把那些经常使用的值直接用于多个规则。
	下面我们将给出一个例子。</para>

      <para>这个脚本所使用的语法与 sh、 csh，
	以及 tcsh 脚本。</para>

      <para>符号替换的前缀字段是美元符号： <literal>&dollar;</literal>。</para>

      <para>符号字段不使用 &dollar; 前缀。</para>

      <para>希望替换符号字段的值，
	必须使用双引号 (<literal>"</literal>) 括起来。</para>

       <para>您的规则文件的开头类似这样：</para>

      <programlisting>############# IPF 规则脚本的开头 ########################
oif="dc0"            # 外网接口的名字
odns="192.0.2.11"    # ISP 的 DNS 服务器 IP 地址
myip="192.0.2.7"     # 来自 ISP 的静态 IP 地址
ks="keep state"
fks="flags S keep state"

# 可以使用这个脚本来建立 /etc/ipf.rules 文件，
# 也可以 "直接地" 运行它。
#
# 请删除两个注释号之一。
#
# 1) 保留下面一行， 则创建 /etc/ipf.rules：
#cat &gt; /etc/ipf.rules &lt;&lt; EOF
#
# 2) 保留下面一行， 则 "直接地" 运行脚本：
/sbin/ipf -Fa -f - &lt;&lt; EOF

# 允许发出到我的 ISP 的域名服务器的访问
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

# 允许发出未加密的 www 访问请求
pass out quick on &dollar;oif proto tcp from &dollar;myip to any port = 80 &dollar;fks

# 允许发出使用 TLS SSL 加密的 https www 访问请求
pass out quick on &dollar;oif proto tcp from &dollar;myip to any port = 443 &dollar;fks
EOF
################## IPF 规则脚本的结束 ########################</programlisting>

      <para>这就是所需的全部内容。 这个规则本身并不重要，
	它们主要是用于体现如何使用符号代换字段，
	以及如何完成值的替换。
	如果上面的例子的名字是 <filename>/etc/ipf.rules.script</filename>，
	就可以通过输入下面的命令来重新加载规则：</para>

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

      <para>在规则文件中嵌入符号有一个问题： IPF 无法识别符号替换，
	因此它不能直接地读取这样的脚本。</para>

      <para>这个脚本可以使用下面两种方法之一来使用：</para>

      <itemizedlist>
	<listitem>
	  <para>去掉 <literal>cat</literal> 之前的注释，
	    并注释掉
	    <literal>/sbin/ipf</literal> 开头的那一行。 像其他配置一样， 将
	    <literal>ipfilter_enable="YES"</literal> 放到
	    <filename>/etc/rc.conf</filename> 文件中，
	    并在此后立刻执行脚本， 以创建或更新
	    <filename>/etc/ipf.rules</filename>。</para>
	</listitem>

	<listitem>
	  <para>通过把 <literal>ipfilter_enable="NO"</literal>
	    (这是默认值) 加到
	    <filename>/etc/rc.conf</filename> 中，
	    来禁止系统启动脚本开启 IPFILTER。</para>

	  <para>在
	    <filename>/usr/local/etc/rc.d/</filename>
	    启动目录中增加一个类似下面的脚本。
	    应该给它起一个显而易见的名字， 例如
	    <filename>ipf.loadrules.sh</filename>。
	    请注意， <filename>.sh</filename>
	    扩展名是必需的。</para>

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

	  <para>脚本文件必须设置为属于 <username>root</username>，
	    并且属主可读、 可写、 可执行。</para>

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

      <para>这样， 在系统启动时， 就会自动加载您的 IPF
	规则了。</para>
    </sect2>

    <sect2>
      <title>IPF 规则集</title>

      <!-- XXX: looks incorrect (and duplicated 2 times in this chapter):
	    1. Packet can be processed two times depend of firewall
	       firewall configuration, but "return trip back" is
	       another packet.
	    2. "Each TCP/IP service ... is predefined by its protocol ..."
	       - this shold be about packet and it's parameters
	       (source/destination address and port). -->

      <para>规则集是指一组编写好的依据包的值决策允许通过或阻止 ipf 规则。
	包的双向交换组成了一个会话交互。 防火墙规则集对同一个包会进行两次处理，
	第一次是它从公网的 Internet 主机到达的时候， 第二次是它离开并返回初始的
	Internet 公网主机的时候。 每一个 TCP/IP 服务 (例如 telnet, www,
	邮件等等) 是由协议预先定义的源或目的 IP 地址， 以及源或目的端口。
	这是最基本的一些可以为防火墙规则所利用的， 判别是否允许服务通过的标准。</para>

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>规则处理顺序</secondary>
       </indexterm>

      <para>IPF 最初被写成使用一组称作
	<quote>以最后匹配的规则为准</quote> 的处理逻辑， 且只能处理无状态的规则。
	随着时代的发展， IPF 进行了改进， 并提供了 <quote>quick</quote>
	选项， 以及一个有状态的 <quote>keep state</quote> 选项。
	后者使处理逻辑迅速地跟上了时代的步伐。</para>

      <para>这一节中提供的一些指导，
	是基于使用包含 <quote>quick</quote> 选项和有状态的
	<quote>keep state</quote> 选项来进行阐述的。
	这些是编写包容式防火墙规则集的基本要素。</para>

    <!-- XXX: something like this already in
	 <xref linkend="firewalls-concepts">
	 AND: the para below is repeated 3 times in this chapter-->
      <para>包容式防火墙只允许与规则匹配的服务通过。 这样，
	您就既能够控制来自防火墙后面的机器请求 Internet 公网上的那些服务，
	同时也可以控制来自 Internet 的请求能够访问内部网上的哪些服务。
	所有其它的访问请求都会被阻止， 并记录下来。
	包容式防火墙一般而言要远比排斥式的要安全，
	而且也只需要定义允许哪些访问通过。</para>

      <warning>
	<para>当对防火墙规则进行操作时， 应该
	    <emphasis>谨慎行事</emphasis>。 某些配置可能会
	    <emphasis>将您反锁在</emphasis> 服务器外面。
	    安全起见， 您可以考虑在第一次进行防火墙配置时在本地控制台上，
	    而不是远程， 例如通过
	    <application>ssh</application> 来进行。</para>
      </warning>
    </sect2>

    <sect2>
      <title>规则语法</title>

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>规则语法</secondary>
      </indexterm>

      <para>这里给出的规则语法已经简化到只处理那些新式的带状态规则，
	并且都是 <quote>第一个匹配的规则获胜</quote> 逻辑的。
	要了解完整的传统规则语法描述， 请参见 &man.ipf.8; 联机手册。</para>

      <para>以 <literal>#</literal> 字符开头的内容会被认为是注释。
	这些注释可以出现在一行规则的末尾， 或者独占一行。 空行会被忽略。</para>

      <para>规则由关键字组成。 这些关键字必须以一定的顺序，
	从左到右出现在一行上。 接下来的文字中关键字将使用粗体表示。
	某些关键字可能提供了子选项， 这些子选项本身可能也是关键字，
	而且可能会提供更多的子选项。 下面的文字中，
	每种语法都使用粗体的小节标题呈现， 并介绍了其上下文。</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
	网络接口的名字</para>

      <para><replaceable>SELECTION</replaceable> = proto 协议名称 |
	源/目的 IP | port = 端口号 | flags 标志值</para>

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

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

      <para><replaceable>OBJECT</replaceable> = IP地址 | any</para>

      <para><replaceable>PORT_NUM</replaceable> = port 端口号</para>

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

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

      <sect3>
	<title>ACTION (动作)</title>

	<para>动作对表示匹配规则的包应采取什么动作。
	  每一个规则 <emphasis>必须</emphasis> 包含一个动作。
	  可以使用下面两种动作之一：</para>

	<para><literal>block</literal> 表示如果规则与包匹配，
	  则丢弃包。</para>

	<para><literal>pass</literal> 表示如果规则与包匹配，
	  则允许包通过防火墙。</para>
      </sect3>

      <sect3>
	<title>IN-OUT</title>
	<para>每个过滤器规则都必须明确地指定是流入还是流出的规则。
	  下一个关键字必须要么是 in， 要么是 out，
	  否则将无法通过语法检查。</para>

	<para><literal>in</literal> 表示规则应被应用于刚刚从 Internet
	  公网上收到的数据包。</para>

	<para><literal>out</literal> 表示规则应被应用于即将发出到
	  Internet 的数据包。</para>
      </sect3>

      <sect3>
	<title>OPTIONS</title>
	<note>
	  <para>这些选项必须按下面指定的顺序出现。</para>
	</note>

	<para><literal>log</literal> 表示包头应被写入到

	<!-- XXX - xref here -->

	  <devicename>ipl</devicename> 日志 (如前面 LOGGING 小节所介绍的那样)，
	  如果它与规则匹配的话。</para>

	<para><literal>quick</literal> 表示如果给出的参数与包匹配，
	  则以这个规则为准， 这使得能够 "短路" 掉后面的规则。
	  这个选项对于使用新式的处理逻辑是必需的。</para>

	<para><literal>on</literal> 表示将网络接口的名称作为筛选参数的一部分。
	  接口的名字会在 &man.ifconfig.8; 的输出中显示。 使用这个选项，
	  则规则只会应用到某一个网络接口上的出入数据包上。
	  要配置新式的处理逻辑， 必须使用这个选项。</para>

	<para>当记录包时， 包的头会被写入到 IPL 包日志伪设备中。
	  紧跟 log 关键字， 可以使用下面几个修饰符
	  (按照下列顺序)：</para>

	<para><literal>body</literal> 表示应同时记录包的前 128 字节的内容。</para>

	<para><literal>first</literal> 如果 <literal>log</literal> 关键字和
	  <quote>keep state</quote> 选项同时使用，
	  则这个选项只在第一个包上触发， 这样就不用记录每一个
	  <quote>keep state</quote> 包信息了。</para>
      </sect3>

      <sect3>
	<title>SELECTION</title>
	<para>这一节所介绍的关键字可以用于所检察的包的属性。
	  有一个关键字主题， 以及一组子选项关键字，
	  您必须从他们中选择一个。 以下是一些通用的属性，
	  它们必须按下面的顺序使用：</para>
      </sect3>

      <sect3>
	<title>PROTO</title>
	<para><literal>proto</literal> 是一个主题关键字，
	  它必须与某个相关的子选项关键字配合使用。
	  这个值的作用是匹配某个特定的协议。
	  要使用新式的规则处理逻辑， 就必须使用这个选项。</para>

	<para><literal>tcp/udp | udp | tcp | icmp</literal> 或其他在
	  <filename>/etc/protocols</filename> 中定义的协议。
	  特殊的协议关键字 <literal>tcp/udp</literal> 可以用于匹配
	  <acronym>TCP</acronym> 或 <acronym>UDP</acronym> 包，
	  引入这个关键字的作用是是避免大量的重复规则的麻烦。</para>
      </sect3>

      <sect3>
	<title>SRC_ADDR/DST_ADDR</title>
	<para>使用 <literal>all</literal> 关键词， 基本上相当于 <quote>from
	  any to any</quote> 在没有配合其他关键字的情形。</para>

	<para><literal>from src to dst</literal>： from 和 to
	  关键字主要是用来匹配 IP 地址。 所有的规则都必须同时给出源和目的两个参数。
	  <literal>any</literal> 是一个可以用于匹配任意 IP 地址的特殊关键字。
	  例如， 您可以使用 <quote>from any to any</quote> 或 <quote>from
	  0.0.0.0/0 to any</quote> 或 <quote>from any to 0.0.0.0/0</quote> 或 <quote>from
	  0.0.0.0 to any</quote> 以及 <quote>from any to 0.0.0.0</quote>。</para>

	<!-- XXX: Needs rewording -->
	<para>IP 地址可以按句点分隔的 IP 地址/掩码长度 的方式来指定，
	  也可以只指定一个句点分隔的 IP 地址。</para>

	<para>如果无法使用子网掩码来表示 IP 的话， 表达地址就会很麻烦。
	  请参见下面的网页了解如何撰写掩码长度：
	  <ulink url="http://jodies.de/ipcalc"></ulink>。</para>
      </sect3>

      <sect3>
	<title>PORT</title>
	<para>如果为源或目的指定了匹配端口， 规则就只能应用于 <acronym>TCP</acronym> 和
	  <acronym>UDP</acronym> 包了。 当编写端口比较规则时，
	  可以指定 <filename>/etc/services</filename> 中所定义的名字，
	  也可以直接用端口号来指定。 如果端口号出现在源对象一侧，
	  则被认为是源端口号； 反之， 则被认为是目的端口号。
	  要使用新式的规则处理逻辑，
	  就必须与 <literal>to</literal> 对象配合使用这个选项。
	  使用的例子： <quote>from any to any port = 80</quote>。</para>

	<!-- XXX: Needs rewriting -->
	<para>端口的比较是以数字的形式进行的， 可以使用比较算符来指定，
	  也可以指定一个范围。</para>

	<para>port "=" | "!=" | "&lt;" | "&gt;" | "&lt;=" | "&gt;=" |
	  "eq" | "ne" | "lt" | "gt" | "le" | "ge".</para>

	<para>要指定端口范围， 可以使用 "&lt;&gt;" | "&gt;&lt;"。</para>

	<warning>
	  <para>在源和目的匹配参数之后， 需要使用下面两个参数，
	    才能够使用新式的规则处理逻辑。</para>
	</warning>
      </sect3>

      <sect3>
	<title><acronym>TCP</acronym>_FLAG</title>
	<para>标志只对 <acronym>TCP</acronym> 过滤使用。
	  这些字母用来表达 <acronym>TCP</acronym> 包头的标志。</para>

	<para>新式的规则处理逻辑使用 <literal>flags
	  S</literal> 参数来识别 tcp 会话开始的请求。</para>
      </sect3>

      <sect3>
	<title>STATEFUL</title>
	<para><literal>keep state</literal> 表示如果有一个包与规则匹配，
	  则其筛选参数应激活有状态的过滤机制。</para>

	<note>
	  <para>如果使用新式的处理逻辑， 则这个选项是必需的。</para>
	</note>
      </sect3>
    </sect2>


    <sect2>
      <title>有状态过滤</title>

      <indexterm>
	<primary>IPFILTER</primary>

	<secondary>有状态过滤</secondary>
      </indexterm>
      <!-- XXX: duplicated -->
      <para>有状态过滤将网络流量当作一种双向的包交换来处理。
	如果激活它， keep-state 会动态地为每一个相关的包在双向会话交互过程中产生内部规则。
	它能够确认发起者和包的目的地之间的会话是有效的双向包交换过程的一部分。
	如果包与这些规则不符， 则将自动地拒绝。</para>

      <para>状态保持也使得 ICMP 包能够与 <acronym>TCP</acronym>
	或 UDP 会话相关。 因此， 如果您在浏览网站时收到允许的状态保持规则匹配的
	ICMP 类型 3 代码 4 响应， 则这些响应会被自动地允许进入。
	所有 IPF 能够处理的包， 都可以作为某种活跃会话的一部分，
	即使它是另一种协议的， 也会被允许进入。</para>

      <para>所发生的事情是：</para>

      <para>将要通过联入公网的网络接口发出的包， 首先会经过动态状态表的检查。
	如果包与会话中预期的下一个包匹配， 则防火墙就会允许包通过，
	而会话的交互流信息也会在动态状态表中进行更新， 而其他的包，
	则将使用发出规则集来检查。</para>

      <para>发到联入 Internet 公网的包， 也会首先经过动态规则表的检查。
	如果与会话中预期的下一个包匹配， 则防火墙就允许它通过，
	并更新动态状态表。 其他包仍会使用进入规则集进行检查。</para>

      <para>当会话结束时， 对应的项会在动态状态表中删除。</para>

      <para>有状态过滤使得您能够集中于阻止/允许新的会话。
	一旦新会话被允许通过， 则所有后续的包就都被自动地允许通过，
	而伪造的包则被自动地拒绝。 如果新的会话被阻止，
	则后续的包也都不会被允许通过。 有状态过滤从技术角度而言，
	在阻止目前攻击者常用的洪水式攻击来说， 具有更好的抗御能力。</para>
    </sect2>

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

      <title>包容式规则集的例子</title>

      <para>下面的规则集是如何编写非常安全的包容式防火墙规则集的一个范例。
	包容式防火墙只让允许的服务通过， 而所有其他的访问都会被默认地拒绝。
	所有的防火墙都有至少两个接口对应的默认规则， 从而使防火墙能够正常工作。</para>

      <para>所有的类 &unix; 系统， 包括 &os; 都使用
	<devicename>lo0</devicename> 和 IP 地址
	<hostid role="ipaddr">127.0.0.1</hostid> 用于操作系统中内部的通讯。
	防火墙规则必须允许这些包无阻碍地通过。</para>

      <para>接入 Internet 公网的网络接口， 是放置规则并允许将访问请求发到
	Internet 以及接收响应的地方。
	这有可能是用户模式的 PPP <devicename>tun0</devicename> 接口，
	如果您的网卡同 DSL 或电缆调制解调器相联的话。</para>

      <para>如果有至少一个网卡与防火墙后的内网 LAN 相联，
	这些网络接口就应该有一个规则来允许来自这些 LAN 接口的包无阻碍地通过。</para>

      <para>一般说来， 规则应被组织为三个主要的小节：
	所有允许自由通过的接口规则， 发到公网接口的规则，
	以及进入公网接口的规则。</para>

      <para>每一个公网接口规则中， 经常会匹配到的规则应该放置在尽可能靠前的位置。
	而最后一个规则应该是阻止包通过， 并记录它们。</para>

      <para>下面防火墙规则集中， Outbound 部分是一些使用
	'pass' 的规则， 这些规则指定了允许访问的公网
	Internet 服务， 并且指定了 'quick'、 'on'、
	'proto'、 'port'， 以及 'keep state' 这些选项。 'proto
	tcp' 规则还指定了 'flag' 这个选项， 这样会话的第一个包将出发状态机制。</para>

      <para>接下来的 Inbound 一节， 则首先阻止所有不希望的数据包。
	这样做有两个原因， 其一是被阻止的包可能会被后面的规则允许，
	从而并不妨碍获得授权的服务正常工作；
	其二是这避免了那些不常见的包由于匹配到最后一条规则而触发日志，
	规则集中的最后一条规则是阻止并记录所有的包，
	通过这样的记录， 就比较容易找到攻击系统的人，
	并为采取法律措施收集证据。</para>

      <para>需要注意的另一件事情是， 如果收到了不希望的数据包，
	则这些包会被丢弃， 而不是给出什么响应。 这样做的好处是，
	攻击者无法了解包是否已经被您的系统收到。
	攻击者所能了解到的信息越少，
	攻陷您的系统所需要花费的时间也就越长。
	我们在这里记录的连入的 'nmap OS 指纹' 探测企图，

	<!-- XXX: what? -->

	一般来说正是攻击者所做的第一件事。</para>

      <para>如果您看到了 'log first' 规则的日志，
	就应该用 <command>ipfstat -hio</command> 命令来看看那个规则被匹配的次数，
	以便了解系统是否正在或曾被攻击。</para>

      <para>如果记录的包的端口号并不是您所知道的，
	可以在 <filename>/etc/services</filename> 或 <ulink
	  url="http://www.securitystats.com/tools/portsearch.php"></ulink>
	了解端口号通常的用途。</para>

      <para>参考下面的网页， 了解木马使用的端口：<ulink
	  url="http://www.simovits.com/trojans/trojans.html"></ulink>。</para>

      <para>下面是我在自己的系统中使用的完整的， 非常安全的
	'包容式' 防火墙规则集。 直接使用这个规则集不会给您造成问题，
	您所要做的只是注释掉那些您不需要的服务。</para>

      <para>如果在日志中发现了希望阻止的记录， 只需在
	inbound 小节中增加一条阻止规则集可。</para>

      <para>您必须将每一个规则中的 <devicename>dc0</devicename>
	替换为您系统上接入 Internet 的网络接口名称， 例如，
	用户环境下的 PPP 应该是 <devicename>tun0</devicename>。</para>

      <para>在
	<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)
# Interrogate session start requests originating from behind the
# firewall on the private network
# or from this gateway server destine 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 & 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 & 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 & LAN users non-secure FTP ( both passive & 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 secure FTP, Telnet, and SCP
# 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 non-secure Telnet
pass out quick on dc0 proto tcp from any to any port = 23 flags S keep state

# Allow out FBSD CVSUP function
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 for LAN PC 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 enforces the block all by default logic.
block out log first quick on dc0 all

#################################################################
# Interface facing Public Internet (Inbound Section)
# Interrogate packets originating from the public Internet
# destine 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 & 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'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.
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 stops a .denial of service. attack targeted
# at filling up your log file space.
# This rule enforces the block all by default logic.
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 伪装</primary>

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

      <indexterm>
	<primary>网络地址转换</primary>

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

      <para><acronym>NAT</acronym> 是 网络地址转换(Network Address
	Translation) 的缩写。 对于那些熟悉 &linux; 的人来说，
	这个概念叫做 IP 伪装 (Masquerading)； <acronym>NAT</acronym> 和 IP
	伪装是完全一样的概念。 由
	IPF 的 <acronym>NAT</acronym> 提供的一项功能是，
	将防火墙后的本地局域网 (LAN) 共享一个 ISP 提供的 IP
	地址来接入 Internet 公网。</para>

      <para>有些人可能会问， 为什么需要这么做。 一般而言， ISP
	会为非商业用户提供动态的 IP 地址。 动态地址意味着每次登录到
	ISP 都有可能得到不同的 IP 地址， 无论是采用电话拨号登录，
	或使用 cable 以及 DSL 调制解调器的方式。 这个 IP 是您与
	Internet 公网交互时使用的身份。</para>

      <para>现在考虑家中有五台 PC 需要访问
	Internet 的情形。 您可能需要向 ISP 为每一台 PC
	所使用的独立的 Internet 账号付费， 并且拥有五根电话线。</para>

      <para>有了 <acronym>NAT</acronym>， 您就只需要一个 ISP 账号，
	然后将另外四台 PC 的网卡通过交换机连接起来，
	并通过运行 &os; 系统的那台机器作为网关连接出去。
	<acronym>NAT</acronym> 会自动地将每一台 PC 在内网的 LAN IP 地址，
	在离开防火墙时转换为公网的 IP 地址。 此外， 当数据包返回时，
	也将进行逆向的转换。</para>

      <para><acronym>NAT</acronym> 通常是在没有向 ISP 请求许可，
	或事先知会的情况下进行的， 因而如果被发现，
	有时可能会成为 ISP 撤销您的账号的一个借口。
	商业用户一般来说会购买昂贵得多的 Internet 线路，
	通常会获得一组长期有效的静态 IP 地址块。 ISP
	一般会希望并同意商业用户在他们的内网中使用
	<acronym>NAT</acronym>。</para>

      <para>在 IP 地址空间中， 有一些特殊的范围是保留供经过
	<acronym>NAT</acronym> 的内网 LAN IP 地址使用的。 根据
	RFC 1918， 您可以使用下面这些 IP 范围用于内网，
	它们不会在 Internet 公网上路由：</para>

      <informaltable frame="none" pgwide="1">
	<tgroup cols="2">
	  <colspec colwidth="1*">

	  <colspec colwidth="1*">

	  <colspec colwidth="1*">

	  <tbody>
	    <row>
	      <entry>起始 IP <hostid role="ipaddr">10.0.0.0</hostid></entry>

	      <entry>-</entry>

	      <entry>结束 IP <hostid role="ipaddr">10.255.255.255</hostid></entry>
	    </row>

	    <row>
	      <entry>起始 IP <hostid role="ipaddr">172.16.0.0</hostid></entry>

	      <entry>-</entry>

	      <entry>结束 IP <hostid role="ipaddr">172.31.255.255</hostid></entry>
	    </row>

	    <row>
	      <entry>起始 IP <hostid role="ipaddr">192.168.0.0</hostid></entry>

	      <entry>-</entry>

	      <entry>结束 IP <hostid role="ipaddr">192.168.255.255</hostid></entry>
	    </row>
	  </tbody>
	</tgroup>
      </informaltable>
    </sect2>

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

      <indexterm>
	<primary>NAT</primary>

	<secondary>以及 IPFILTER</secondary>
      </indexterm>

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

      <para><acronym>NAT</acronym> 规则是通过
	<command>ipnat</command> 命令加载的。 默认情况下，
	<acronym>NAT</acronym> 规则会保存在
	<filename>/etc/ipnat.rules</filename> 文件中。 请参见 &man.ipnat.1;
	了解更多的详情。</para>

      <para>如果在 <acronym>NAT</acronym> 已经启动之后想要修改
	<acronym>NAT</acronym> 规则， 可以修改保存 NAT 规则的那个文件，
	然后在执行 ipnat 命令时加上 <option>-CF</option> 参数，
	以删除在用的 <acronym>NAT</acronym> 内部规则表，
	以及所有地址翻译表中已有的项。</para>

      <para>要重新加载 <acronym>NAT</acronym> 规则， 可以使用类似下面的命令：</para>

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

      <para>如果想要看看您系统上
	<acronym>NAT</acronym> 的统计信息， 可以用下面的命令：</para>

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

      <para>要列出当前的 <acronym>NAT</acronym> 表的映射关系，
	使用下面的命令：</para>

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

      <para>要显示详细的信息并显示与规则处理和当前的规则/表项：</para>

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

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

      <para><acronym>NAT</acronym> 规则非常的灵活，
	能够适应商业用户和家庭用户的各种不同的需求。</para>

      <para>这里所介绍的规则语法已经被简化，
	以适应非商用环境中的一般情况。 完整的规则语法描述，
	请参考 &man.ipnat.5; 联机手册中的介绍。</para>

      <para><acronym>NAT</acronym> 规则的写法与下面的例子类似：</para>

      <programlisting>map <replaceable>IF</replaceable> <replaceable>LAN_IP_RANGE</replaceable> -> <replaceable>PUBLIC_ADDRESS</replaceable></programlisting>

      <para>关键词 <literal>map</literal> 出现在规则的最前面。</para>

      <para>将 <replaceable>IF</replaceable> 替换为对外的网络接口名。</para>

      <para><replaceable>LAN_IP_RANGE</replaceable> 是内网中的客户机使用的地址范围。
	通常情况下， 这应该是类似 <hostid
	  role="ipaddr">192.168.1.0/24</hostid> 的地址。</para>

      <para><replaceable>PUBLIC_ADDRESS</replaceable>
	既可以是外网的 IP 地址， 也可以是
	<literal>0/32</literal> 这个特殊的关键字，
	它表示分配到 <replaceable>IF</replaceable> 上的所有地址。</para>
    </sect2>

    <sect2>
      <title><acronym>NAT</acronym> 的工作原理</title>

      <para>当包从 LAN 到达防火墙， 而目的地址是公网地址时，
	它首先会通过 outbound 过滤规则。 接下来，
	<acronym>NAT</acronym> 会得到包， 并按自顶向下的顺序处理规则，
	而第一个匹配的规则将生效。
	<acronym>NAT</acronym> 接下来会根据包对应的接口名字和源
	IP 地址检查所有的规则。 如果包和某个 <acronym>NAT</acronym>
	规则匹配， 则会检查包的
	[源 IP 地址， 例如， 内网的 IP 地址] 是否在
	<acronym>NAT</acronym> 规则中箭头左侧指定的 IP 地址范围匹配。
	如果匹配， 则包的原地址将被根据用 <literal>0/32</literal>
	关键字指定的 IP 地址重写。
	<acronym>NAT</acronym> 将向它的内部
	<acronym>NAT</acronym> 表发送此地址， 这样， 当包从 Internet
	公网中返回时， 就能够把地址映射回原先的内网 IP 地址，
	并在随后使用过滤器规则来处理。</para>
    </sect2>

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

      <para>要启用 IP<acronym>NAT</acronym>， 只需在
	<filename>/etc/rc.conf</filename> 中加入下面一些语句。</para>

      <para>使机器能够在不同的网络接口之间进行包的转发，
	需要：</para>

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

      <para>每次开机时自动启动 IP<acronym>NAT</acronym>：</para>

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

      <para>指定 IP<acronym>NAT</acronym> 规则集文件：</para>

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

    <sect2>
      <title>大型 LAN 中的 <acronym>NAT</acronym></title>

      <para>对于在一个 LAN 中有大量 PC， 以及包含多个 LAN 的情形，
	把所有的内网 IP 地址都映射到同一个公网 IP 上会导致资源不够的问题，
	因为同一个端口可能在许多做了
	<acronym>NAT</acronym> 的 LAN PC 上被多次使用， 并导致碰撞。
	有两种方法来缓解这个难题。</para>

      <sect3>
	<title>指定使用哪些端口</title>

	<!-- What does it mean ? Is there something missing ?-->
	<!-- XXXBLAH <- Apparently you can't start a sect
	     with a <programlisting> tag ?-->

	<para>普通的 NAT 规则类似于：</para>

	<programlisting>map dc0 192.168.1.0/24 -> 0/32</programlisting>

	<para>上面的规则中， 包的源端口在包通过 IP<acronym>NAT</acronym>
	  时时不会发生变化的。 通过使用 portmap 关键字， 您可以要求
	  IP<acronym>NAT</acronym> 只使用一定范围内的端口地址。
	  比如说， 下面的规则将让
	  IP<acronym>NAT</acronym> 把源端口改为指定范围内的端口：</para>

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

	<para>使用
	  <literal>auto</literal> 关键字可以让配置变得更简单一些， 它会要求
	  IP<acronym>NAT</acronym> 自动地检测可用的端口并使用：</para>

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

      <sect3>
	<title>使用公网地址池</title>

	<para>对于很大的 LAN 来说， 总会有一个临界值，
	  此时 LAN 的地址会多到没办法使用一个公网地址表达的程度。
	  这时， 类似下面的规则需要进行修改：</para>

	<programlisting>map dc0 192.168.1.0/24 -> 204.134.75.1</programlisting>

	<para>目前的这个规则， 将所有的链接都通过 <hostid
	    role="ipaddr">204.134.75.1</hostid> 来映射。
	  可以把它改为一个范围：</para>

	<programlisting>map dc0 192.168.1.0/24 -> 204.134.75.1-10</programlisting>

	<para>或者使用 CIDR 记法指定的一组地址：</para>

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

    <sect2>
      <title>端口重定向</title>

      <para>非常流行的一种做法是， 将 web 服务器、
	邮件服务器、 数据库服务器以及 DNS 分别放到 LAN 上的不同的 PC 上。
	这种情况下， 来自这些服务器的网络流量仍然应该被 <acronym>NAT</acronym>，
	但必须有办法把进入的流量发到对应的局域网的
	PC 上。 IP<acronym>NAT</acronym> 提供了 <acronym>NAT</acronym>
	重定向机制来解决这个问题。 考虑下面的情况，
	您的 web 服务器的 LAN 地址是 <hostid
	  role="ipaddr">10.0.10.25</hostid>， 而您的唯一的公网 IP
	地址是 <hostid role="ipaddr">20.20.20.5</hostid>， 则可以编写这样的规则：</para>

	<programlisting>map dc0 20.20.20.5/32 port 80 -> 10.0.10.25 port 80</programlisting>

	<para>或者：</para>

	<programlisting>map dc0 0/32 port 80 -> 10.0.10.25 port 80</programlisting>

	<para>另外， 也可以让 LAN 地址 <hostid
	    role="ipaddr">10.0.10.33</hostid> 上运行的 LAN DNS 服务器来处理公网上的
	  DNS 请求：</para>

	<programlisting>map dc0 20.20.20.5/32 port 53 -> 10.0.10.33 port 53 udp</programlisting>
    </sect2>

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

      <para>FTP 是一个在 Internet 如今天这样为人所熟知之前就已经出现的恐龙，
	那时， 研究机构和大学是通过租用的线路连到一起的， 而 FTP
	则被用于在科研人员之间共享大文件。 那时，
	数据的安全性并不是需要考虑的事情。 若干年之后， FTP
	协议则被埋进了正在形成中的 Internet 骨干，
	而它使用明文来交换用户名和口令的缺点，
	并没有随着新出现的一些安全需求而得到改变。 FTP 提供了两种不同的风格，
	即主动模式和被动模式。 两者的区别在于数据通道的建立方式。
	被动模式相对而言要更加安全， 因为数据通道是由发起 ftp
	会话的一方建立的。 关于 FTP 以及它所提供的不同模式，
	在 <ulink
	  url="http://www.slacksite.com/other/ftp.html"></ulink>
	进行了很好的阐述。</para>

      <sect3>
	<title>IP<acronym>NAT</acronym> 规则</title>

	<para>IP<acronym>NAT</acronym> 提供了一个内建的 FTP 代理选项，
	  它可以在 <acronym>NAT</acronym> map 规则中指定。
	  它能够监视所有外发的 FTP 主动或被动模式的会话开始请求，
	  并动态地创建临时性的过滤器规则， 只打开用于数据通道的端口号。
	  这样， 就消除了 FTP 一般会给防火墙带来的，
	  需要大范围地打开高端口所可能带来的安全隐患。</para>

	<para>下面的规则可以处理来自内网的 FTP 访问：</para>

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

	<para>这个规则能够处理来自网关的 FTP 访问：</para>

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

	<para>这个则处理所有来自内网的非 FTP 网络流量：</para>

	<programlisting>map dc0 10.0.10.0/29 -> 0/32</programlisting>

	<para>FTP map 规则应该在普通的 map 规则之前出现。
	  所有的包会从最上面的第一个规则开始进行检查。
	  匹配的顺序是网卡名称， 内网源 IP 地址， 以及它是否是 FTP 包。
	  如果所有这些规则都匹配成功， 则 FTP
	  代理将建立一个临时的过滤规则， 以便让 FTP 会话的数据包能够正常出入，
	  同时对这些包进行 <acronym>NAT</acronym>。
	  所有的 LAN 数据包， 如果没有匹配第一条规则，
	  则会继续尝试匹配下面的规则， 并最终被
	  <acronym>NAT</acronym>。</para>
      </sect3>

      <sect3>
	<title>IP<acronym>NAT</acronym> FTP 过滤规则</title>

	<para>如果使用了
	  <acronym>NAT</acronym> FTP 代理， 则只需要为 FTP 创建一个规则。</para>

	<para>如果没有使用 FTP 代理， 则需要下面三个规则：</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 > 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>

      <sect3>
	<title>FTP <acronym>NAT</acronym> 代理的 bug</title>

	<para>在 &os; 4.9 中提供了 IPFILTER 的 3.4.31 版。
	  在整个 FTP 会话直到将要结束之前， FTP 代理都会像文档中介绍的那样。
	  但一旦有一方要求关闭会话， 则来自远程 FTP 服务器 21
	  端口的一个包会被阻止， 并在日志中记下这一情况。 <acronym>NAT</acronym>
	  FTP/代理显然过早地删除了临时规则， 而不是等待远程 FTP 服务器确认它。
	  有人就这个问题在 IPF 的邮件列表中发表了问题报告。</para>

	<para>解决这个问题的方法是， 要么增加一条过滤规则来摆脱不希望的日志消息，
	  或者什么都不做， 并忽略日志中的 FTP 连入错误信息。
	  绝大多数人并不经常使用外部的
	  FTP 服务。</para>

	<programlisting>block in quick on rl0 proto tcp from any to any port = 21</programlisting>
      </sect3>
    </sect2>
  </sect1>

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

    <indexterm>
      <primary>防火墙</primary>

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

    <note>
      <para>这一节的内容正在撰写中。 其内容可能不总是十分准确。</para>
    </note>

    <para>IPFIREWALL (IPFW) 是一个由 &os; 发起的防火墙应用软件，
      它由 &os; 的志愿者成员编写和维护。 它使用了传统的无状态规则，
      以及传统的规则编写技巧， 以期达到所谓的简单状态逻辑。</para>

    <para>标准的 &os; 安装中， IPFW 所给出的规则集例子 (可以在
      <filename>/etc/rc.firewall</filename> 中找到) 非常简单，
      以至于没有办法不加修改地加以使用。 那个例子中没有使用状态过滤，
      而这对于许多配置都非常有好处， 因此这一节并不以系统自带的例子作为基础。</para>

    <para>IPFW 的无状态规则语法， 是由一种提供复杂的选择能力的技术支持的，
      这种技术远远超出了一般的防火墙安装人员的知识水平。
      IPFW 是为满足专业用户，
      以及掌握先进技术的电脑爱好者们对于高级的包选择需求而设计的。
      要完全释放 IPFW 的规则所拥有的强大能力，
      需要对不同的协议的细节有深入的了解，
      并根据它们独特的包头信息来编写规则。
      这一级别的详细阐述超出了这本手册的范围。</para>

    <para>IPFW 由其部分组成， 其主要组件是内核的防火墙过滤规则处理器，
      及其集成的数据包记帐工具、 日志工具、  用以触发
      <acronym>NAT</acronym> 工具的 'divert' (转发) 规则、
      高级特殊用途工具、 dummynet 流量整形机制，
      'fwd rule' 转发工具， 桥接工具， 以及 ipstealth 工具。</para>

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

      <indexterm>
	<primary>IPFW</primary>

	<secondary>启用</secondary>
      </indexterm>

      <para>IPFW 是基本的 &os; 安装的一部分， 以单独的可加载内核模块的形式提供。
	如果在 <filename>rc.conf</filename> 中加入
	<literal>firewall_enable="YES"</literal> 语句， 就会自动地加载对应的内核模块。
	除非您打算使用由它提供的
	<acronym>NAT</acronym> 功能， 一般情况下并不需要把 IPFW 编进
	&os; 的内核。</para>

      <para>如果将
	<literal>firewall_enable="YES"</literal> 加入到
	<filename>rc.conf</filename> 中并重新启动系统，
	则下列信息将在启动过程中， 以高亮的白色显示出来：</para>

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

      <para>可加载内核模块在编译时加入了记录日志的能力。 要启用日志功能，
	并配置详细日志记录的限制， 需要在
	<filename>/etc/sysctl.conf</filename> 中加入一些配置。
	这些设置将在重新启动之后生效：</para>

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

    <sect2 id="firewalls-ipfw-kernel">
      <title>内核选项</title>

      <indexterm>
	<primary>内核选项</primary>

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

      <indexterm>
	<primary>内核选项</primary>

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

      <indexterm>
	<primary>内核选项</primary>

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

      <indexterm>
	<primary>IPFW</primary>

	<secondary>内核选项</secondary>
      </indexterm>

      <para>把下列选项在编译 &os; 内核时就加入， 并不是启用 IPFW 所必需的，
	除非您需要使用 <acronym>NAT</acronym> 功能。
	这里只是将这些选项作为背景知识来介绍。</para>

      <programlisting>options    IPFIREWALL</programlisting>

      <para>这个选项将 IPFW 作为内核的一部分来启用。</para>

      <programlisting>options    IPFIREWALL_VERBOSE</programlisting>

      <para>这个选项将启用记录通过 IPFW 的匹配了包含 'log' 关键字规则的每一个包的功能。</para>

      <programlisting>options    IPFIREWALL_VERBOSE_LIMIT=5</programlisting>

      <para>以每项的方式， 限制通过 &man.syslogd.8; 记录的包的个数。
	如果在比较恶劣的环境下记录防火墙的活动可能会需要这个选项。
	它能够避免潜在的针对 syslog 的洪水式拒绝服务攻击。</para>

      <indexterm>
	<primary>内核选项</primary>

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

      <programlisting>options    IPFIREWALL_DEFAULT_TO_ACCEPT</programlisting>

      <para>这个选项默认地允许所有的包通过防火墙，
	如果您是第一次配置防火墙， 使用这个选项将是一个不错的主意。</para>

      <programlisting>options    IPV6FIREWALL
options    IPV6FIREWALL_VERBOSE
options    IPV6FIREWALL_VERBOSE_LIMIT
options    IPV6FIREWALL_DEFAULT_TO_ACCEPT</programlisting>

      <para>这些选项与 IPv4 的对应选项功能一样， 它们是针对 IPv6
	的。 如果不使用 IPv6， 则不带任何规则的 IPV6FIREWALL
	将阻止所有的 IPv6 包。</para>

      <indexterm>
	<primary>内核选项</primary>

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

      <programlisting>options    IPDIVERT</programlisting>

      <para>这一选项启用 <acronym>NAT</acronym>
	功能。</para>

      <note>
	<para>如果内核选项中没有加入 IPFIREWALL_DEFAULT_TO_ACCEPT，
	  或将您的防火墙设置配置为允许所有的进入包，
	  则所有发到本机或发出的包都会被阻止。</para>
      </note>
    </sect2>

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

      <para>如果没有将 IPFW 编入内核， 则您需要在
	<filename>/etc/rc.conf</filename> 中加入下列语句来加载它：</para>

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

      <para>用以激活您的防火墙规则的配置脚本：</para>

      <programlisting>firewall_script="/etc/ipfw.rules"</programlisting>

      <para>启用日志：</para>

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

      <warning>
	<para>设置
	  <varname>firewall_logging</varname> 的唯一作用是，
	  系统将把 <varname>net.inet.ip.fw.verbose</varname> sysctl
	  变量置为 <literal>1</literal> (参见 <xref
	    linkend="firewalls-ipfw-enable">)。 并没有能够设置日志限制的
	  <filename>rc.conf</filename> 变量， 不过这种限制可以通过设置某些 sysctl
	  变量来完成， 可以手工进行操作， 也可以写到
	  <filename>/etc/sysctl.conf</filename> 文件中：</para>

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

    <sect2 id="firewalls-ipfw-cmd">
      <title>IPFW 命令</title>

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

      <para>ipfw 命令是在防火墙运行时，
	用于在其内部规则表中手工逐条添加或删除防火墙规则的标准工具。
	这一方法的问题在于， 一旦您的关闭计算机或停机，
	则所有增加或删除或修改的规则也就丢掉了。 
	把所有的规则都写到一个文件中， 并在启动时使用这个文件来加载规则，
	或一次大批量地替换防火墙规则， 那么推荐使用这里介绍的方法。</para>

      <para>ipfw 的另一个非常实用的功能是将所有正在运行的防火墙规则显示出来。
	IPFW 的记账机制会为每一个规则动态地创建计数器，
	用以记录与它们匹配的包的数量。 在测试规则的过程中，
	列出规则及其计数器是了解它们是否工作正常的重要手段。</para>

      <para>按顺序列出所有的规则：</para>

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

      <para>列出所有的规则， 同时给出最后一次匹配的时间戳：</para>

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

      <para>列出所有的记账信息、 匹配规则的包的数量， 以及规则本身。
	第一列是规则的编号， 随后是发出包匹配的数量， 进入包的匹配数量，
	最后是规则本身。</para>

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

      <para>列出所有的动态规则和静态规则：</para>

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

      <para>同时显示已过期的动态规则：</para>

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

      <para>将计数器清零：</para>

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

      <para>只把规则号为
	<replaceable>NUM</replaceable> 的计数器清零：</para>

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

    <sect2 id="firewalls-ipfw-rules">
      <title>IPFW 规则集</title>

      <!-- XXX: looks incorrect (and duplicated 2 times in this chapter):
	    1. Packet can be processed two times depend of firewall
	       firewall configuration, but "return trip back" is
	       another packet.
	    2. "Each TCP/IP service ... is predefined by its protocol ..."
	       - this shold be about packet and it's parameters
	       (source/destination address and port). -->

      <para>规则集是一组根据包中选择的数值使用 allow 或 deny 写出的 ipfw 规则。
	在两个主机之前的双向包交换组成了一次会话交互。 防火墙规则集，
	会对同一个包处理两次： 第一次是包从公网上到达防火墙时，
	而第二次则是包返回 Internet 公网上的主机时。
	每一个 TCP/IP 服务 (例如 telnet, www, mail,
	等等)， 都有事先定义好的协议， 以及一个端口号。
	这可以作为建立允许或阻止规则时的基本选择依据。</para>

      <indexterm>
	<primary>IPFW</primary>

	<secondary>规则处理顺序</secondary>
      </indexterm>

      <!-- Needs rewording to include note below -->

      <para>当有数据包进入防火墙时， 会从规则集里的第一个规则开始进行比较，
	并自顶向下地进行匹配。 当包与某个选择规则参数相匹配时，
	将会执行规则所定义的动作， 并停止规则集搜索。 这种策略，
	通常也被称作 <quote>最先匹配者获胜</quote> 的搜索方法。
	如果没有任何与包相匹配的规则， 那么它就会根据强制的 ipfw
	默认规则， 也就是 65535 号规则截获。 一般情况下这个规则是阻止包，
	而且不给出任何回应。</para>

      <note>
	<para>如果规则定义的动作是 <literal>count</literal>、
	  <literal>skipto</literal> 或 <literal>tee</literal>
	  规则的话， 搜索会继续。</para>
      </note>

      <para>这里所介绍的规则， 都是使用了那些包含状态功能的，
	也就是 'keep state'、 'limit'、 'in'/'out'、
	或者 'via' 选项的规则。 这是编写包容式防火墙规则集所需的基本框架。</para>

    <!-- XXX: something like this already in
	 <xref linkend="firewalls-concepts">
	 AND: the para below is repeated 3 times in this chapter. -->

      <para>包容式防火墙只允许与规则匹配的包通过。 这样，
	您就既能够控制来自防火墙后面的机器请求 Internet 公网上的那些服务，
	同时也可以控制来自 Internet 的请求能够访问内部网上的哪些服务。
	所有其它的访问请求都会被阻止， 并记录下来。
	包容式防火墙一般而言要远比排斥式的要安全，
	而且也只需要定义允许哪些访问通过。</para>

      <warning>
	<para>在操作防火墙规则时应谨慎行事， 如果操作不当，
	  有可能将自己反锁在外面。</para>
      </warning>

      <sect3 id="firewalls-ipfw-rules-syntax">
	<title>规则语法</title>

	<indexterm>
	  <primary>IPFW</primary>

	  <secondary>规则语法</secondary>
	</indexterm>

	<para>这里所介绍的规则语法已经经过了简化，
	  只包括了建立标准的包容式防火墙规则集所必需的那些。
	  要了解完整的规则语法说明，
	  请参见 &man.ipfw.8; 联机手册。</para>

	<para>规则是由关键字组成的： 这些关键字必须以特定的顺序从左到右书写。
	  下面的介绍中， 关键字使用粗体表示。 某些关键字还包括了子选项，
	  这些自选向本身可能也是关键字， 有些还可以包含更多的子选项。</para>

	<para><literal>#</literal> 用于表示开始一段注释。
	  它可以出现在一个规则的后面， 也可以独占一行。
	  空行会被忽略。</para>

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

	<sect4>
	  <title>CMD</title>

	  <para>每一个新的规则都应以
	    <parameter>add</parameter> 作为前缀， 它表示将规则加入内部表。</para>
	</sect4>

	<sect4>
	  <title>RULE_NUMBER</title>

	  <para>每一个规则都必须包含一个规则编号。</para>
	</sect4>

	<sect4>
	  <title>ACTION</title>

	  <para>每一个规则可以与下列的动作之一相关联，
	    所指定的动作将在进入的数据包与规则所指定的选择标准相匹配时执行。</para>

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

	  <para>这些关键字都表示允许匹配规则的包通过防火墙，
	    并停止继续搜索规则。</para>

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

	  <para>根据动态规则表检查数据包。 如果匹配，
	    则执行规则所指定的动作， 亦即生成动态规则；
	    否则， 转移到下一个规则。 check-state 规则没有选择标准。
	    如果规则集中没有 check-state 规则，
	    则会在第一个 keep-state 或 limit 规则处，
	    对动态规则表实施检查。</para>

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

	  <para>这两个关键字都表示丢弃匹配规则的包。
	    同时， 停止继续搜索规则。</para>
	</sect4>

	<sect4>
	  <title>LOGGING</title>

	  <para><parameter>log</parameter> or
	    <parameter>logamount</parameter></para>

	  <para>当数据包与带 log 关键字的规则匹配时，
	    将通过名为 SECURITY 的 facility 来把消息记录到 syslogd。
	    只有在记录的次数没有超过 logamount 参数所指定的次数时，
	    才会记录日志。 如果没有指定 logamount， 则会以 sysctl 变量
	    net.inet.ip.fw.verbose_limit 所指定的限制为准。
	    如果将这两种限制值之一指定为零， 则表示不作限制。
	    万一达到了限制数，
	    可以通过将规则的日志计数或包计数清零来重新启用日志，
	    请参见 ipfw reset log 命令来了解细节。</para>

	  <note>
	    <para>日志是在所有其他匹配条件都验证成功之后，
	      在针对包实施最终动作 (accept, deny) 之前进行的。
	      您可以自行决定哪些规则应启用日志。</para>
	    </note>
	</sect4>

	<sect4>
	  <title>SELECTION</title>

	  <para>这一节所介绍的关键字主要用来描述检查包的哪些属性，
	    用以判断包是否与规则相匹配。
	    下面是一些通用的用于匹配包特征的属性，
	    它们必须按顺序使用：</para>

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

	  <para>也可以指定在
	    <filename>/etc/protocols</filename> 中所定义的协议。
	    这个值定义的是匹配的协议， 在规则中必须指定它。</para>

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

	  <para>from 和 to 关键字用于匹配 IP 地址。
	    规则中必须同时指定源和目的两个参数。
	    如果需要匹配任意 IP 地址，
	    可以使用特殊关键字 <literal>any</literal>。
	    还有一个特殊关键字， 即 <literal>me</literal>，
	    用于匹配您的 &os; 系统上所有网络接口上所配置的 IP 地址，
	    它可以用于表达网络上的其他计算机到防火墙 (也就是本机)，
	    例如 'from me to any' 或 'from any to me' 或 'from 0.0.0.0/0 to any' 或
	    'from any to 0.0.0.0/0' 或 'from 0.0.0.0 to any' 或 'from
	    any to 0.0.0.0' 以及 'from me to 0.0.0.0'。 IP
	    地址可以通过 带点的 IP 地址/掩码长度，
	    或者一个带点的 IP 地址的形式来指定。 这是编写规则时所必需的。
	    如果不清楚如何写掩码长度， 请参见 <ulink
	      url="http://jodies.de/ipcalc"></ulink>。</para>

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

	  <para>这个参数主要用于那些支持端口号的协议 (例如
	    <acronym>TCP</acronym> 和 UDP)。 如果要通过端口号匹配某个协议，
	    就必须指定这个参数。 此外， 也可以通过服务的名字 (根据
	    <filename>/etc/services</filename>) 来指定服务，
	    这样会比使用数字指定端口号直观一些。</para>

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

	  <para>相应地， 匹配进入和发出的包。
	    这里的 in 和 out 都是关键字， 在编写匹配规则时，
	    必需作为其他条件的一部分来使用。</para>

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

	  <para>根据指定的网络接口的名称精确地匹配进出的包。
	    这里的 <literal>via</literal>
	    关键字将使得接口名称成为匹配过程的一部分。</para>

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

	  <para>要匹配 <acronym>TCP</acronym> 会话的发起请求，
	    就必须使用它。</para>

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

	  <para>这是一个必须使用的关键字。 在发生匹配时，
	    防火墙将创建一个动态规则， 其默认行为是，
	    匹配使用同一协议的、从源到目的 IP/端口 的双向网络流量。</para>

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

	  <para>防火墙只允许匹配规则时， 与指定的参数相同的
	    <replaceable>N</replaceable> 个连接。
	    可以指定至少一个源或目的地址及端口。
	    'limit' 和 'keep-state' 不能在同一规则中同时使用。
	    'limit' 提供了与 'keep-state' 相同的功能，
	    并增加了一些独有的能力。</para>
	</sect4>
      </sect3>

      <sect3>
	<title>状态规则选项</title>

	<indexterm>
	  <primary>IPFW</primary>

	  <secondary>带状态过滤</secondary>
	</indexterm>

	<!-- XXX: duplicated -->

	<para>有状态过滤将网络流量当作一种双向的包交换来处理。
	  它提供了一种额外的检查能力，
	  用以检测会话中的包是否来自最初的发送者，
	  并在遵循双向包交换的规则进行会话。
	  如果包与这些规则不符， 则将自动地拒绝它们。</para>

	<para>'check-state' 用来识别在 IPFW
	  规则集中的包是否符合动态规则机制的规则。
	  如果匹配， 则允许包通过，
	  此时防火墙将创建一个新的动态规则来匹配双向交换中的下一个包。
	  如果不匹配， 则将继续尝试规则集中的下一个规则。</para>

	<para>动态规则机制在 SYN-flood 攻击下是脆弱的，
	  因为这种情况会产生大量的动态规则， 从而耗尽资源。
	  为了抵抗这种攻击， 从 &os; 4.5 版开始， 加入了一个叫做 limit
	  的新选项。
	  这个选项可以用来限制符合规则的会话允许的并发连接数。
	  如果动态规则表中的规则数超过限制， 则包将被丢弃。</para>
      </sect3>

      <sect3>
	<title>记录防火墙消息</title>

	<indexterm>
	  <primary>IPFW</primary>

	  <secondary>记录日志</secondary>
	</indexterm>

	<para>记录日志的好处是显而易见的： 它提供了在事后检查所发生的状况的方法，
	  例如哪些包被丢弃了， 这些包的来源和目的地，
	  从而为您提供找到攻击者所需的证据。</para>

	<para>即使启用了日志机制， IPFW 也不会自行生成任何规则的日志。
	  防火墙管理员需要指定规则集中的哪些规则应该记录日志，
	  并在这些规则上增加 log 动作。 一般来说， 只有
	  deny 规则应记录日志， 例如对于进入的
	  <acronym>ICMP</acronym> ping 的 deny 规则。 另外，
	  复制默认的 ipfw 终极 deny 规则， 并加入 log
	  动作来作为您的规则集的最后一条规则也是很常见的用法。
	  这样， 您就能看到没有匹配任何一条规则的那些数据包。</para>

	<para>日志是一把双刃剑， 如果不谨慎地加以利用，
	  则可能会陷入过多的日志数据中， 并导致磁盘被日志塞满。
	  将磁盘填满是 DoS 攻击最为老套的手法之一。
	  由于日志除了会写入磁盘之外， 还会输出到 root 的控制台屏幕上，
	  因此有过多的日志信息是很让人恼火的事情。</para>

	<para><literal>IPFIREWALL_VERBOSE_LIMIT=5</literal>
	  内核选项将限制同一个规则发到系统日志程序 syslogd 的连续消息的数量。
	  当内核启用了这个选项时，
	  某一特定规则所产生的连续消息的数量将封顶为这个数字。
	  一般来说， 没有办法从连续 200 条一模一样的日志信息中获取更多有用的信息。
	  举例来说， 如果同一个规则产生了 5 次消息并被记录到
	  syslogd， 余下的相同的消息将被计数，
	  并像下面这样发给 syslogd：</para>

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

	<para>所有记录的数据包包消息， 默认情况下会最终写到
	  <filename>/var/log/security</filename> 文件中，
	  后者在 <filename>/etc/syslog.conf</filename> 文件里进行了定义。</para>
      </sect3>

      <sect3 id="firewalls-ipfw-rules-script">
	<title>编写规则脚本</title>

	<para>绝大多数有经验的 IPFW 用户会创建一个包含规则的文件，
	  并且， 按能够以脚本形式运行的方式来书写。 这样做最大的一个好处是，
	  可以大批量地刷新防火墙规则， 而无须重新启动系统就能够激活它们。
	  这种方法在测试新规则时会非常方便，
	  因为同一过程在需要时可以多次执行。
	  作为脚本， 您可以使用符号替换来撰写那些经常需要使用的值，
	  并用同一个符号在多个规则中反复地表达它。
	  下面将给出一个例子。</para>

	<para>这个脚本使用的语法同 'sh'、
	  'csh' 以及 'tcsh' 脚本兼容。 符号替换字段使用美元符号 &dollar;
	  作为前缀。 符号字段本身并不使用 &dollar; 前缀。
	  符号替换字段的值必须使用 "双引号" 括起来。</para>

	<para>可以使用类似下面的规则文件：</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>这就是所要做的全部事情了。 例子中的规则并不重要，
	  它们主要是用来表示如何使用符号替换。</para>

	<para>如果把上面的例子保存到
	  <filename>/etc/ipfw.rules</filename> 文件中，
	  您就可以通过输入下面的命令来加载它。</para>

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

	<para><filename>/etc/ipfw.rules</filename> 这个文件可以放到任何位置，
	  也可以命名为随便什么别的名字。</para>

	<para>也可以手工执行下面的命令来达到类似的目的：</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>带状态规则集</title>

	<para>以下的这组非-<acronym>NAT</acronym> 规则集，
	  是如何编写非常安全的 '包容式' 防火墙的一个例子。
	  包容式防火墙只允许匹配了 pass 规则的包通过，
	  而默认阻止所有的其他数据包。 必须有至少两个网络接口，
	  并且在其上配置了规则才能使防火墙正常工作。</para>

	<para>所有类 &unix; 操作系统， 也包括 &os;，
	  都设计为允许使用网络接口 <devicename>lo0</devicename> 和 IP
	  地址 <hostid role="ipaddr">127.0.0.1</hostid> 来完成操作系统内部的通讯。
	  防火墙必须包含一组规则， 使这些数据包能够无障碍地收发。</para>

	<para>接入 Internet 公网的那个网络接口上，
	  应该配置授权和访问控制， 来限制对外的访问， 以及来自 Internet
	  公网的访问。 这个接口很可能是您的 ppp 接口， 例如
	  <devicename>tun0</devicename>， 或者您接在 DSL 或电缆 modem
	  上的网卡。</para>

	<para>如果有至少一个网卡接入了防火墙后的内网 LAN，
	  则必须为这些接口配置规则，
	  以便让这些接口之间的包能够顺畅地通过。</para>

	<para>所有的规则应被组织为三个部分， 所有应无阻碍地通过的规则，
	  公网的发出规则， 以及公网的接收规则。</para>

	<para>公网接口相关的规则的顺序，
	  应该是最经常用到的放在尽可能靠前的位置，
	  而最后一个规则， 则应该是阻止那个接口在那一方向上的包。</para>

	<para>发出部分的规则只包含一些 'allow' 规则，
	  允许选定的那些唯一区分协议的端口号所指定的协议通过，
	  以允许访问 Internet 公网上的这些服务。
	  所有的规则中都指定了 proto, port,
	  in/out, via 以及 keep state 这些选项。 'proto tcp'
	  规则同时指定 'setup' 选项， 来区分开始协议会话的包，
	  以触发将包放入
	  keep state 规则表中的动作。</para>

	<para>接收部分则首先阻止所有不希望的包， 在这里有两个目的。
	  首先是， 这些包被禁止掉之后， 就不会由于匹配了后面的某个规则而被允许。
	  其次， 明确地禁止这些包， 就不会再在日志中记录它们而形成干扰。
	  防火墙的最后一条规则是阻止并记录所有包， 这样，
	  您就可以留下用于起诉攻击您的系统的人的有用记录。</para>

	<para>另一件需要注意的事情是， 不希望的数据包一般来说不会有任何响应，
	  这些数据包会被丢弃并消失。 这样，
	  攻击者也就无法了解他的数据包是否到达了您的系统。
	  而攻击者了解的信息越少， 它们攻陷系统所需的时间也就越长。
	  当记录的包使用的端口号不是您所熟悉的那些时， 可以看一看
	  <filename>/etc/services/</filename> 或到 <ulink
	    url="http://www.securitystats.com/tools/portsearch.php"></ulink>
	  并查找一下端口号， 以了解其用途。 另外，
	  您也可以在这个网页上了解常见木马所使用的端口： <ulink
	    url="http://www.simovits.com/trojans/trojans.html"></ulink>。</para>
      </sect3>

      <sect3>
	<title>包容式规则集的例子</title>

	<para>下面是一个非-<acronym>NAT</acronym> 的规则集，
	  它是一个完整的包容式规则集。 使用它作为您的规则集不会有什么问题。
	  只需把那些不需要的服务对应的 pass 规则注释掉就可以了。
	  如果您在日志中看到消息， 而且不想再看到它们，
	  只需在接收部分增加一个一个 deny 规则。 您可能需要把 'dc0'
	  改为接入公网的接口的名字。 对于使用用户态 ppp 的用户而言， 应该是
	  'tun0'。</para>

	<para>您可以看出这些规则中的模式。</para>

	<itemizedlist>
	  <listitem>
	    <para>所有请求 Internet 公网上服务的会话开始包，
	      都使用了 keep-state。</para>
	  </listitem>

	  <listitem>
	    <para>所有来自 Internet 的授权服务请求，
	      都采用了 limit 选项来防止洪水式攻击。</para>
	  </listitem>

	  <listitem>
	    <para>所有的规则都使用了 in 或者 out 来说明方向。</para>
	  </listitem>

	  <listitem>
	    <para>所有的规则都使用了 via 接口名来指定应该匹配通过哪一个接口的包。</para>
	  </listitem>
	</itemizedlist>

	<para>这些规则都应放到
	  <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
# destine 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 & 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 & 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 & 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)
# Interrogate packets originating from the public Internet
# destine 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 & 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 & 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 & 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>一个 <acronym>NAT</acronym> 和带状态规则集的例子</title>

	<indexterm>
	  <primary>NAT</primary>

	  <secondary>以及 IPFW</secondary>
	</indexterm>

	<para>要使用 IPFW 的 <acronym>NAT</acronym> 功能， 还需要进行一些额外的配置。
	  除了其他 IPFIREWALL 语句之外，
	  还需要在内核编译配置中加上 'option divert' 语句。</para>

	<para>在
	  <filename>/etc/rc.conf</filename> 中， 除了普通的 IPFW 配置之外，
	  还需要加入：</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>将带状态规则与转发 natd 规则 (网络地址转换)
	  会使规则集的编写变得非常复杂。 check-state 的位置，
	  以及 'divert natd' 规则将变得非常关键。 这样一来，
	  就不再有简单的顺序处理逻辑流程了。 提供了一种新的动作类型，
	  称为 'skipto'。 要使用 skipto 命令，
	  就必须给每一个规则进行编号，
	  以确定 skipto 规则号是您希望跳转到的位置。</para>

	<para>下面给出了一些未加注释的例子来说明如何编写这样的规则，
	  用以帮助您理解包处理规则集的处理顺序。</para>

	<para>处理流程从规则文件最上边的第一个规则开始处理，
	  并自顶向下地尝试每一个规则， 直到找到匹配的规则，
	  且数据包从防火墙中放出为止。 请注意规则号 100 101，
	  450， 500， 以及 510 的位置非常重要。
	  这些规则控制发出和接收的包的地址转换过程，
	  这样它们在 keep-state 动态表中的对应项中就能够与内网的
	  LAN IP 地址关联。 另一个需要注意的是， 所有的 allow
	  和 deny 规则都指定了包的方向 (也就是 outbound 或 inbound)
	  以及网络接口。 最后， 请注意所有发出的会话请求都会请求
	  skipto rule 500 以完成网络地址转换。</para>

	<para>下面以 LAN 用户使用 web 浏览器访问一个 web 页面为例。
	  Web 页面使用 80 来完成通讯。 当包进入防火墙时，
	  规则 100 并不匹配， 因为它是发出而不是收到的包。
	  它能够通过规则 101， 因为这是第一个包，
	  因而它还没有进入动态状态保持表。
	  包最终到达规则 125， 并匹配该规则。
	  最终， 它会通过接入 Internet 公网的网卡发出。
	  这之前， 包的源地址仍然是内网 IP 地址。
	  一旦匹配这个规则， 就会触发两个动作。
	  keep-state 选项会把这个规则发到 keep-state 动态规则表中，
	  并执行所指定的动作。 动作是发到规则表中的信息的一部分。
	  在这个例子中， 这个动作是 "skipto rule
	  500"。 规则 500 <acronym>NAT</acronym> 包的 IP 地址，
	  并将其发出。 请务必牢记， 这一步非常重要。
	  接下来， 数据包将到达目的地， 之后返回并从规则集的第一条规则开始处理。
	  这一次， 它将与规则 100 匹配， 其目的 IP 地址将被映射回对应的内网
	  LAN IP 地址。 其后， 它会被
	  check-state 规则处理， 进而在暨存会话表中找到对应项，
	  并发到 LAN。 数据包接下来发到了内网 LAN PC 上，
	  而后者则会发送从远程服务器请求下一段数据的新数据包。
	  这个包会再次由 check-state 规则检查， 并找到发出的表项，
	  并执行其关联的动作， 即 'skipto 500'。 包跳转到规则 500 并被
	  <acronym>NAT</acronym> 后发出。</para>

	<para>在接收一侧， 已经存在的会话的数据包， 会被 check-state
	  规则自动地处理， 并放到转发 natd 规则。 我们需要解决的问题是，
	  阻止所有的坏数据包， 而只允许授权的服务。
	  例如在防火墙上运行了 Apache 服务， 而我们希望人们在访问 Internet
	  公网的同时， 也能够访问本地的 web 站点。
	  新的接入开始请求包将匹配规则 100， 而 IP
	  地址则为防火墙所在的服务器而映射到了 LAN
	  IP。 此后， 包会匹配所有我们希望检查的那些令人生厌的东西，
	  并最终匹配规则 425。 一旦发生匹配， 会发生两件事。
	  数据包会被发到 keep-state 动态表， 但此时，
	  所有来自那个源 IP 的会话请求的数量会被限制为 2。
	  这一做法能够挫败针对指定端口上服务的 DoS 攻击。
	  动作同时指定了包应被发到 LAN 上。 包返回时，
	  check-state 规则会识别出包属于某一已经存在的会话交互，
	  并直接把它发到规则 500 做
	  <acronym>NAT</acronym>， 并发到发出接口。</para>

	<para>示范规则集 #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 & 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>下面的这个规则集基本上和上面一样，
	  但使用了易于读懂的编写方式， 并给出了相当多的注解，
	  以帮助经验较少的 IPFW 规则编写者更好地理解这些规则到底在做什么。</para>

	<para>示范规则集 #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)
# Interrogate session start requests originating from behind the
# firewall on the private network or from this gateway server
# destine 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 & 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 & 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)
# Interrogate packets originating from the public Internet
# destine 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 & 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'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 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 & 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 & Log all unauthorized incoming connections from the public Internet
&dollar;cmd 400 deny log all from any to any in via &dollar;pif

# Reject & 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>

<!--
     Local Variables:
     mode: sgml
     sgml-declaration: "../chapter.decl"
     sgml-indent-data: t
     sgml-omittag: nil
     sgml-always-quote-attributes: t
     sgml-parent-document: ("../book.sgml" "part" "chapter")
     End:
-->
