下一页 上一页 目录

4. IP 防火墙链

本节介绍构建满足您需求的包过滤器所需了解的所有真正知识。

4.1 数据包如何遍历过滤器

内核从三个规则列表开始;这些列表称为 防火墙链 或简称 。这三个链称为 inputoutputforward。当数据包进入(例如,通过以太网卡)时,内核使用 input 链来决定其命运。如果它在这一步中幸存下来,那么内核将决定接下来将数据包发送到哪里(这称为 路由)。如果它注定要发送到另一台机器,它会查阅 forward 链。最后,就在数据包要发出之前,内核会查阅 output 链。

链是 规则 的清单。每个规则都说“如果数据包头看起来像这样,那么这就是对数据包的处理方式”。如果规则与数据包不匹配,则咨询链中的下一个规则。最后,如果没有更多规则可咨询,那么内核将查看链 策略 以决定该怎么做。在注重安全的系统中,此策略通常告诉内核拒绝或拒绝数据包。

对于 ASCII 艺术爱好者来说,这显示了数据包进入机器的完整路径。

        ----------------------------------------------------------------
        |            ACCEPT/                              lo interface |
        v           REDIRECT                  _______                  |
--> C --> S --> ______ --> D --> ~~~~~~~~ -->|forward|----> _______ --> 
    h     a    |input |    e    {Routing }   |Chain  |     |output |ACCEPT
    e     n    |Chain |    m    {Decision}   |_______| --->|Chain  |
    c     i    |______|    a     ~~~~~~~~        |     | ->|_______|
    k     t       |        s       |             |     | |     |    
    s     y       |        q       |             v     | |     |    
    u     |       v        e       v            DENY/  | |     v    
    m     |     DENY/      r   Local Process   REJECT  | |   DENY/
    |     v    REJECT      a       |                   | |  REJECT
    |   DENY               d       --------------------- | 
    v                      e -----------------------------
   DENY                              
以下是每个阶段的详细描述

校验和

这是一个测试,用于检查数据包是否以某种方式损坏。如果已损坏,则会被拒绝。

健全性

实际上,在每个防火墙链之前都有一个健全性检查,但输入链的健全性检查是最重要的。一些格式错误的数据包可能会混淆规则检查代码,这些数据包在此处被拒绝(如果发生这种情况,则会向系统日志打印一条消息)。

输入链

这是将要测试数据包的第一个防火墙链。如果链的判决不是 DENYREJECT,则数据包继续前进。

解伪装

如果数据包是对先前伪装的数据包的回复,则会对其进行解伪装,并直接跳到 output 链。如果您不使用 IP 伪装,您可以从图中将其 mentally erase。

路由决策

路由代码会检查目标字段,以确定此数据包应发送到本地进程(请参阅下面的本地进程)还是转发到远程机器(请参阅下面的 forward 链)。

本地进程

在路由决策步骤之后,机器上运行的进程可以接收数据包,并且可以发送数据包(这些数据包将通过路由决策步骤,然后遍历输出链)。

lo 接口

如果来自本地进程的数据包注定要发送到本地进程,它们将通过接口设置为“lo”的输出链,然后通过接口也为“lo”的输入链返回。lo 接口通常称为环回接口。

本地

如果数据包不是由本地进程创建的,则检查 forward 链,否则数据包将转到 output 链。

转发链

此链用于遍历任何尝试通过此机器传递到另一台机器的数据包。

输出链

此链用于遍历所有即将发送出去的数据包。

使用 ipchains

首先,检查您是否拥有本文档引用的 ipchains 版本

$ ipchains --version
ipchains 1.3.9, 17-Mar-1999

请注意,我推荐 1.3.4(没有长选项,例如“--sport”),或 1.3.8 或更高版本;这些版本非常稳定。

ipchains 有一个相当详细的手册页 (man ipchains),如果您需要有关细节的更多详细信息,您可以查看编程接口 (man 4 ipfw),或 2.1.x 内核源代码中的文件 net/ipv4/ip_fw.c,该文件(显然)是权威的。

Scott Bronson 在源代码包中也提供了一个出色的快速参考卡,A4 和 US Letter PostScript(TM) 格式都有。

您可以使用 ipchains 执行几项不同的操作。首先是管理整个链的操作。您从三个内置链 inputoutputforward 开始,这些链您无法删除。

  1. 创建一个新链 (-N)。
  2. 删除一个空链 (-X)。
  3. 更改内置链的策略。 (-P)。
  4. 列出链中的规则 (-L)。
  5. 从链中刷新规则 (-F)。
  6. 将链中所有规则上的数据包和字节计数器归零 (-Z)。

有几种方法可以操作链内的规则

  1. 将新规则附加到链 (-A)。
  2. 在链中的某个位置插入新规则 (-I)。
  3. 替换链中某个位置的规则 (-R)。
  4. 删除链中某个位置的规则 (-D)。
  5. 删除链中第一个匹配的规则 (-D)。

有一些用于伪装的操作,它们在 ipchains 中,因为找不到一个好的放置位置

  1. 列出当前伪装的连接 (-M -L)。
  2. 设置伪装超时值 (-M -S)。 (但请参阅 我无法设置伪装超时!)。

最后一个(也可能是最有用的)功能允许您检查给定的数据包在遍历给定链时会发生什么。

计算机启动时您将看到的内容

在运行任何 ipchains 命令之前(注意:某些发行版在其初始化脚本中运行 ipchains),任何内置链(“input”、“forward”和“output”)中都不会有任何规则,并且每个链都将具有 ACCEPT 策略。这是您能得到的最开放的状态。

对单个规则的操作

这是 ipchains 的核心内容;操作规则。最常见的是,您可能会使用 append (-A) 和 delete (-D) 命令。其他命令(-I 用于插入,-R 用于替换)是这些概念的简单扩展。

每个规则都指定了数据包必须满足的一组条件,以及如果满足这些条件该怎么做(“目标”)。例如,您可能想要拒绝来自 IP 地址 127.0.0.1 的所有 ICMP 数据包。因此,在这种情况下,我们的条件是协议必须是 ICMP,并且源地址必须是 127.0.0.1。我们的目标是“DENY”。

127.0.0.1 是“环回”接口,即使您没有实际的网络连接,您也会拥有它。您可以使用“ping”程序生成此类数据包(它只是发送一个 ICMP 类型 8(回显请求),所有合作主机都应尽责地响应一个 ICMP 类型 0(回显回复)数据包)。这使其对于测试非常有用。

# ping -c 1 127.0.0.1
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.2 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.2/0.2/0.2 ms
# ipchains -A input -s 127.0.0.1 -p icmp -j DENY
# ping -c 1 127.0.0.1
PING 127.0.0.1 (127.0.0.1): 56 data bytes

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
# 

您可以在这里看到第一个 ping 成功了(“-c 1”告诉 ping 只发送一个数据包)。

然后,我们将一个规则附加 (-A) 到 “input” 链,该规则指定对于来自 127.0.0.1 (“-s 127.0.0.1”) 且协议为 ICMP (“-p ICMP”) 的数据包,我们应该跳转到 DENY (“-j DENY”)。

然后,我们使用第二个 ping 测试我们的规则。程序在放弃等待永远不会到来的响应之前会暂停一下。

我们可以通过两种方式删除该规则。首先,由于我们知道它是输入链中唯一的规则,我们可以使用编号删除,如下所示

        # ipchains -D input 1
        #
删除输入链中的规则编号 1。

第二种方法是镜像 -A 命令,但将 -A 替换为 -D。当您有一个复杂的规则链并且不想计数它们来找出您要删除的是规则 37 时,这非常有用。在这种情况下,我们将使用

        # ipchains -D input -s 127.0.0.1 -p icmp -j DENY
        #
-D 的语法必须与 -A(或 -I 或 -R)命令的选项完全相同。如果同一链中有多个相同的规则,则只会删除第一个。

过滤规范

我们已经看到了使用“-p”指定协议和使用“-s”指定源地址,但是我们还可以使用其他选项来指定数据包特征。以下是详尽的纲要。

指定源 IP 地址和目标 IP 地址

源 (-s) 和目标 (-d) IP 地址可以通过四种方式指定。最常见的方法是使用全名,例如“localhost”或“www.linuxhq.com”。第二种方法是指定 IP 地址,例如“127.0.0.1”。

第三种和第四种方法允许指定一组 IP 地址,例如“199.95.207.0/24”或“199.95.207.0/255.255.255.0”。这两者都指定从 199.95.207.0 到 199.95.207.255(包括)的任何 IP 地址;“/”之后的数字告诉您 IP 地址的哪些部分是重要的。“/32”或“/255.255.255.255”是默认值(匹配所有 IP 地址)。要指定任何 IP 地址,“/0”可以使用,如下所示

        # ipchains -A input -s 0/0 -j DENY
        #

这很少使用,因为上面的效果与根本不指定“-s”选项的效果相同。

指定反转

许多标志,包括“-s”和“-d”标志,它们的参数前面可以加上“!”(发音为“not”)来匹配与给定地址不相等的地址。例如。“-s ! localhost”匹配任何不是来自 localhost 的数据包。

不要忘记“!”周围的空格:它们确实是必需的。

指定协议

可以使用“-p”标志指定协议。协议可以是数字(如果您知道 IP 的数字协议值)或“TCP”、“UDP”或“ICMP”特殊情况的名称。大小写无关紧要,因此“tcp”和“TCP”一样有效。

协议名称可以以“!”为前缀来反转它,例如“-p ! TCP”。

指定 UDP 和 TCP 端口

对于指定 TCP 或 UDP 协议的特殊情况,可以有一个额外的参数指示 TCP 或 UDP 端口,或端口的(包含性)范围(但请参阅下面的 处理分片)。范围使用“:”字符表示,例如“6000:6010”,它涵盖了 11 个端口号,从 6000 到 6010(包括)。如果省略下限,则默认为 0。如果省略上限,则默认为 65535。因此,要指定来自 1024 以下端口的 TCP 连接,语法将为“-p TCP -s 0.0.0.0/0 :1023”。端口号可以用名称指定,例如“www”。

请注意,端口规范可以以“!”为前缀,这将反转它。因此,要指定除 WWW 数据包之外的每个 TCP 数据包,您将指定

-p TCP -d 0.0.0.0/0 ! www

重要的是要意识到规范

-p TCP -d ! 192.168.1.1 www

-p TCP -d 192.168.1.1 ! www

非常不同。第一个指定任何 TCP 数据包到任何机器上但 192.168.1.1 上的 WWW 端口。第二个指定到 192.168.1.1 上任何端口但 WWW 端口的任何 TCP 连接。

最后,这种情况意味着既不是 WWW 端口也不是 192.168.1.1

-p TCP -d ! 192.168.1.1 ! www

指定 ICMP 类型和代码

ICMP 也允许一个可选参数,但由于 ICMP 没有端口,(ICMP 有一个 类型 和一个 代码),它们具有不同的含义。

您可以将它们指定为 ICMP 名称(使用 ipchains -h icmp 列出名称)在“-s”选项之后,或指定为数字 ICMP 类型和代码,其中类型在“-s”选项之后,代码在“-d”选项之后。

ICMP 名称相当长:您只需要使用足够的字母来使名称与其他任何名称区分开来。

这是一个包含一些最常见的 ICMP 数据包的小表格

Number  Name                     Required by

0       echo-reply               ping
3       destination-unreachable  Any TCP/UDP traffic.
5       redirect                 routing if not running routing daemon
8       echo-request             ping
11      time-exceeded            traceroute

请注意,目前 ICMP 名称不能以“!”为前缀。

不要 不要 不要 阻止所有 ICMP 类型 3 消息!(请参阅下面的 ICMP 数据包)。

指定接口

“-i”选项指定要匹配的 接口 的名称。接口是数据包进入或发出的物理设备。您可以使用 ifconfig 命令列出“up”(即,目前正在工作)的接口。

传入数据包(即,遍历 input 链的数据包)的接口被认为是它们进入的接口。逻辑上,传出数据包(遍历 output 链的数据包)的接口是它们将要发出的接口。遍历 forward 链的数据包的接口也是它们将要发出的接口;在我看来这是一个相当随意的决定。

指定当前不存在的接口是完全合法的;在接口启动之前,该规则将不匹配任何内容。这对于拨号 PPP 链接(通常是接口 ppp0)等非常有用。

作为一种特殊情况,以“+”结尾的接口名称将匹配所有以该字符串开头的接口(无论它们当前是否存在)。例如,要指定匹配所有 PPP 接口的规则,将使用 -i ppp+ 选项。

接口名称可以以“!”为前缀,以匹配与指定接口不匹配的数据包。

仅指定 TCP SYN 数据包

有时,允许单向 TCP 连接很有用,但不允许另一方向的连接。例如,您可能想要允许连接到外部 WWW 服务器,但不允许来自该服务器的连接。

幼稚的方法是阻止来自服务器的 TCP 数据包。不幸的是,TCP 连接需要双向数据包才能工作。

解决方案是仅阻止用于请求连接的数据包。这些数据包称为 SYN 数据包(好的,从技术上讲,它们是设置了 SYN 标志,清除 FIN 和 ACK 标志的数据包,但我们称它们为 SYN 数据包)。通过仅禁止这些数据包,我们可以阻止尝试的连接。

“-y”标志用于此目的:它仅对指定 TCP 作为其协议的规则有效。例如,要指定来自 192.168.1.1 的 TCP 连接尝试

-p TCP -s 192.168.1.1 -y

再一次,可以通过在前面加上“!”来反转此标志,这意味着除连接启动之外的每个数据包。

处理分片

有时,数据包太大而无法一次性放入线路中。发生这种情况时,数据包会被分成 分片,并作为多个数据包发送。另一端重新组装分片以重建整个数据包。

分片的问题在于,上面列出的一些规范(特别是,源端口、目标端口、ICMP 类型、ICMP 代码或 TCP SYN 标志)要求内核查看数据包的开头,而数据包的开头仅包含在第一个分片中。

如果您的机器是到外部网络的唯一连接,那么您可以告诉 Linux 内核重新组装所有通过它的分片,方法是将内核编译时将 IP: always defragment 设置为“Y”。这巧妙地绕过了这个问题。

否则,了解分片如何被过滤规则处理非常重要。任何请求我们没有的信息的过滤规则都将匹配。这意味着第一个分片像任何其他数据包一样处理。第二个和后续分片则不会。因此,规则 -p TCP -s 192.168.1.1 www(指定源端口为“www”)永远不会匹配分片(第一个分片除外)。反向规则 -p TCP -s 192.168.1.1 ! www 也不会。

但是,您可以使用“-f”标志专门为第二个和后续分片指定规则。显然,在这样的分片规则中指定 TCP 或 UDP 端口、ICMP 类型、ICMP 代码或 TCP SYN 标志是非法的。

通过在“-f”前面加上“!”,也可以指定规则适用于第二个和后续分片。

通常,将第二个和后续分片放行被认为是安全的,因为过滤将影响第一个分片,从而阻止目标主机上的重新组装,但是,已知存在错误,允许仅通过发送分片来使机器崩溃。由您决定。

网络负责人请注意:格式错误的数据包(TCP、UDP 和 ICMP 数据包太短,防火墙代码无法读取端口或 ICMP 代码和类型)也被视为分片。只有从位置 8 开始的 TCP 分片才会被防火墙代码显式丢弃(如果发生这种情况,系统日志中应该会出现一条消息)。

例如,以下规则将丢弃任何发往 192.168.1.1 的分片

 
# ipchains -A output -f -d 192.168.1.1 -j DENY
#

过滤副作用

好的,现在我们知道了使用规则匹配数据包的所有方法。如果数据包与规则匹配,则会发生以下情况

  1. 该规则的字节计数器会增加数据包的大小(包括标头和所有内容)。
  2. 该规则的数据包计数器会递增。
  3. 如果规则请求,则会记录数据包。
  4. 如果规则请求,则会更改数据包的服务类型字段。
  5. 如果规则请求,则会标记数据包(不在 2.0 内核系列中)。
  6. 检查规则目标以决定接下来对数据包执行什么操作。

为了多样化,我将按重要性顺序解决这些问题。

指定目标

目标 告诉内核如何处理与规则匹配的数据包。ipchains 使用 “-j”(可以理解为 “jump-to”) 进行目标规范。目标名称必须少于 8 个字母,并且区分大小写:“RETURN” 和 “return” 完全不同。

最简单的情况是没有指定目标。这种类型的规则(通常称为 “accounting” 规则)对于简单地计数某种类型的数据包很有用。无论此规则是否匹配,内核都只检查链中的下一个规则。例如,要计数来自 192.168.1.1 的数据包数量,我们可以这样做

# ipchains -A input -s 192.168.1.1
#

(使用 “ipchains -L -v” 我们可以看到与每个规则关联的字节和数据包计数器)。

有六个特殊目标。前三个,ACCEPTREJECTDENY 相当简单。ACCEPT 允许数据包通过。DENY 丢弃数据包,就像从未收到过一样。REJECT 丢弃数据包,但(如果它不是 ICMP 数据包)生成 ICMP 回复给源,告知目标无法访问。

下一个,MASQ 告诉内核伪装数据包。要使此功能正常工作,您的内核需要编译时启用 IP 伪装。有关详细信息,请参阅 Masquerading-HOWTO 和附录 ipchains 和 ipfwadm 之间的差异。此目标仅对遍历 forward 链的数据包有效。

另一个主要的特殊目标是 REDIRECT,它告诉内核将数据包发送到本地端口,而不是它原本要前往的位置。这只能为指定 TCP 或 UDP 作为其协议的规则指定。可选地,可以在 “-j REDIRECT” 之后指定端口(名称或编号),这将导致数据包被重定向到该特定端口,即使它被寻址到另一个端口。此目标仅对遍历 input 链的数据包有效。

最后一个特殊目标是 RETURN,它与立即从链末尾掉下来相同。(请参阅下面的 设置策略)。

任何其他目标都指示用户定义的链(如 对整个链的操作 中所述)。数据包将开始遍历该链中的规则。如果该链没有决定数据包的命运,那么一旦在该链上的遍历完成,遍历将恢复到当前链中的下一个规则。

是时候来点更多的 ASCII 艺术了。考虑两个(愚蠢的)链:input(内置链)和 Test(用户定义的链)。

         `input'                         `Test'
        ----------------------------    ----------------------------
        | Rule1: -p ICMP -j REJECT |    | Rule1: -s 192.168.1.1    |
        |--------------------------|    |--------------------------|
        | Rule2: -p TCP -j Test    |    | Rule2: -d 192.168.1.1    |
        |--------------------------|    ----------------------------
        | Rule3: -p UDP -j DENY    |
        ----------------------------

考虑一个来自 192.168.1.1,发往 1.2.3.4 的 TCP 数据包。它进入 input 链,并针对 Rule1 进行测试 - 不匹配。Rule2 匹配,其目标是 Test,因此下一个检查的规则是 Test 的开头。Test 中的 Rule1 匹配,但未指定目标,因此检查下一个规则 Rule2。这不匹配,因此我们已到达链的末尾。我们返回到 input 链,在那里我们刚刚检查了 Rule2,所以我们现在检查 Rule3,它也不匹配。

因此,数据包路径是

                                v    __________________________
         `input'                |   /    `Test'                v
        ------------------------|--/    -----------------------|----
        | Rule1                 | /|    | Rule1                |   |
        |-----------------------|/-|    |----------------------|---|
        | Rule2                 /  |    | Rule2                |   |
        |--------------------------|    -----------------------v----
        | Rule3                 /--+___________________________/
        ------------------------|---
                                v

请参阅 如何组织您的防火墙规则 部分,了解如何有效地使用用户定义的链。

记录数据包

这是匹配规则可能产生的副作用;您可以使用 “-l” 标志记录匹配的数据包。您通常不希望对例行数据包执行此操作,但如果您想查找异常事件,这是一个有用的功能。

内核记录的此信息看起来像

Packet log: input DENY eth0 PROTO=17 192.168.2.1:53 192.168.1.1:1025
  L=34 S=0x00 I=18 F=0x0000 T=254

此日志消息旨在简洁明了,并包含仅对网络专家有用的技术信息,但它对我们其他人也可能有用。它分解如下

  1. “input” 是包含匹配数据包并导致日志消息的规则的链。
  2. “DENY” 是规则指示对数据包执行的操作。如果这是 “-”,则规则根本没有影响数据包(会计规则)。
  3. “eth0” 是接口名称。由于这是输入链,这意味着数据包从 “eth0” 进入。
  4. “PROTO=17” 表示数据包是协议 17。协议号列表在 “/etc/protocols” 中给出。最常见的是 1 (ICMP)、6 (TCP) 和 17 (UDP)。
  5. “192.168.2.1” 表示数据包的源 IP 地址是 192.168.2.1。
  6. “:53” 表示源端口是端口 53。查看 “/etc/services” 显示这是 “domain” 端口(即,这可能是 DNS 回复)。对于 UDP 和 TCP,此数字是源端口。对于 ICMP,它是 ICMP 类型。对于其他协议,它将是 65535。
  7. “192.168.1.1” 是目标 IP 地址。
  8. “:1025” 表示目标端口是 1025。对于 UDP 和 TCP,此数字是目标端口。对于 ICMP,它是 ICMP 代码。对于其他协议,它将是 65535。
  9. “L=34” 表示数据包总长度为 34 字节。
  10. “S=0x00” 表示服务类型字段(除以 4 以获得 ipchains 使用的服务类型)。
  11. “I=18” 是 IP ID。
  12. “F=0x0000” 是 16 位分片偏移量加上标志。以 “0x4” 或 “0x5” 开头的值表示设置了 “Don't Fragment” 位。“0x2” 或 “0x3” 表示设置了 “More Fragments” 位;预计在此之后会有更多分片。数字的其余部分是此分片的偏移量,除以 8。
  13. “T=254” 是数据包的生存时间 (TTL)。每次跃点都会从此值中减去 1,通常从 15 或 255 开始。
  14. “(#5)” 在更新的内核(可能在 2.2.9 之后)上,括号中可能有一个最终数字。这是导致数据包日志的规则编号。

在标准 Linux 系统上,此内核输出由 klogd(内核日志守护程序)捕获,klogd 将其交给 syslogd(系统日志守护程序)。“/etc/syslog.conf” 通过为每个 “facility”(在我们的例子中,facility 是 “kernel”)和 “level”(对于 ipchains,使用的 level 是 “info”)指定目标来控制 syslogd 的行为。

例如,我的 (Debian) /etc/syslog.conf 包含两行与 “kern.info” 匹配的行

kern.*                          -/var/log/kern.log
*.=info;*.=notice;*.=warn;\
        auth,authpriv.none;\
        cron,daemon.none;\
        mail,news.none          -/var/log/messages

这些行表示消息在 “/var/log/kern.log” 和 “/var/log/messages” 中重复。有关更多详细信息,请参阅 “man syslog.conf”。

操作服务类型

IP 标头中有四个很少使用的位,称为 服务类型 (TOS) 位。它们影响数据包的处理方式;这四个位是 “Minimum Delay”、“Maximum Throughput”、“Maximum Reliability” 和 “Minimum Cost”。只允许设置这些位中的一个。TOS-mangling 代码的作者 Rob van Nieuwkerk 这样说

特别是 “Minimum Delay” 对我来说很重要。我在我的上游 (Linux) 路由器中为 “interactive” 数据包打开它。我使用 33k6 调制解调器链接。Linux 在 3 个队列中对数据包进行优先级排序。这样,我在同时进行批量下载时也能获得可接受的交互性能。(如果串行驱动程序中没有那么大的队列,甚至可能会更好,但现在延迟保持在 1.5 秒以下)。

注意:显然,您无法控制传入的数据包;您只能控制离开您的盒子的数据包的优先级。要与另一端协商优先级,必须使用像 RSVP 这样的协议(我对它一无所知,所以不要问我)。

最常见的用途是将 telnet 和 ftp 控制连接设置为 “Minimum Delay”,将 FTP 数据设置为 “Maximum Throughput”。这将按如下方式完成

ipchains -A output -p tcp -d 0.0.0.0/0 telnet -t 0x01 0x10
ipchains -A output -p tcp -d 0.0.0.0/0 ftp -t 0x01 0x10
ipchains -A output -p tcp -s 0.0.0.0/0 ftp-data -t 0x01 0x08

“-t” 标志接受两个额外的参数,都是十六进制的。这些允许对 TOS 位进行复杂的调整:第一个掩码与数据包的当前 TOS 进行 AND 运算,然后第二个掩码与它进行 XOR 运算。如果这太令人困惑,只需使用下表

TOS Name                Value           Typical Uses

Minimum Delay           0x01 0x10       ftp, telnet
Maximum Throughput      0x01 0x08       ftp-data
Maximum Reliability     0x01 0x04       snmp
Minimum Cost            0x01 0x02       nntp

Andi Kleen 继续指出以下几点(为了后代略作编辑)

也许在讨论 TOS 位时添加对 ifconfig 的 txqueuelen 参数的引用会很有用。默认设备队列长度是为以太网卡调整的,在调制解调器上它太长,并且使 3 波段调度器(基于 TOS 排队)次优地工作。在调制解调器或单 B 通道 ISDN 链路上将其设置为 4-10 之间的值是一个好主意:在捆绑设备上需要更长的队列。这是一个 2.0 和 2.1 问题,但在 2.1 中它是一个 ifconfig 标志(使用最新的 nettools),而在 2.0 中它需要设备驱动程序中的源代码补丁才能更改。

因此,要最大程度地利用调制解调器 PPP 链接的 TOS 操作,请在您的 /etc/ppp/ip-up 脚本中执行 “ifconfig $1 txqueuelen”。要使用的数字取决于调制解调器速度和调制解调器中的缓冲量;以下是 Andi 再次纠正我

给定配置的最佳值需要实验。如果路由器上的队列太短,则数据包将被丢弃。当然,即使没有 TOS 重写,也能获得好处,只是 TOS 重写有助于将好处提供给不合作的程序(但所有标准 Linux 程序都是合作的)。

标记数据包

这允许与 Alexey Kuznetsov 的新服务质量实现以及更高版本 2.1 系列内核中的基于标记的转发进行复杂而强大的交互。更多消息即将发布。此选项在 2.0 内核系列中完全被忽略。

对整个链的操作

ipchains 的一个非常有用的功能是将相关规则分组到链中。您可以随意调用链,只要名称不与内置链(inputoutputforward)或目标(MASQREDIRECTACCEPTDENYREJECTRETURN)冲突即可。我建议完全避免使用大写标签,因为我可能会将这些用于未来的扩展。链名称最多可以包含 8 个字符。

创建新链

让我们创建一个新链。因为我是一个如此富有想象力的人,所以我将其命名为 test

# ipchains -N test
#

就这么简单。现在您可以像上面详细介绍的那样在其中放置规则。

删除链

删除链也很简单。

# ipchains -X test
# 

为什么是 “-X”?嗯,所有好的字母都被占用了。

删除链有一些限制:它们必须为空(请参阅下面的 刷新链),并且不能作为任何规则的目标。您无法删除任何三个内置链。

刷新链

有一种简单的方法可以清空链中的所有规则,即使用 “-F” 命令。

        # ipchains -F forward
        # 

如果您未指定链,则将刷新所有链。

列出链

您可以使用 “-L” 命令列出链中的所有规则。

# ipchains -L input
Chain input (refcnt = 1): (policy ACCEPT)
target     prot opt    source                destination           ports
ACCEPT     icmp -----  anywhere              anywhere              any
# ipchains -L test
Chain test (refcnt = 0):
target     prot opt    source                destination           ports
DENY       icmp -----  localnet/24           anywhere              any
#

test 列出的 “refcnt” 是以 test 作为其目标的规则数。在可以删除此链之前,此值必须为零(并且链必须为空)。

如果省略链名称,则会列出所有链,即使是空链。

有三个选项可以伴随 “-L”。“-n”(数字)选项非常有用,因为它可以防止 ipchains 尝试查找 IP 地址,如果您像大多数人一样使用 DNS,如果您的 DNS 未正确设置,或者您已过滤掉 DNS 请求,则会导致大量延迟。它还会导致端口以数字而不是名称打印出来。

“-v” 选项显示规则的所有详细信息,例如数据包和字节计数器、TOS 掩码、接口和数据包标记。否则,将省略这些值。例如

# ipchains -v -L input
Chain input (refcnt = 1): (policy ACCEPT)
 pkts bytes target     prot opt   tosa tosx  ifname    mark        source                destination           ports
   10   840 ACCEPT     icmp ----- 0xFF 0x00  lo                    anywhere              anywhere              any

请注意,数据包和字节计数器使用后缀 “K”、“M” 或 “G” 分别打印为 1000、1,000,000 和 1,000,000,000。同时使用 “-x”(展开数字)标志会打印完整的数字,无论它们有多大。

重置(归零)计数器

能够重置计数器很有用。可以使用 “-Z”(归零计数器)选项来完成此操作。例如

# ipchains -v -L input
Chain input (refcnt = 1): (policy ACCEPT)
 pkts bytes target     prot opt   tosa tosx  ifname    mark        source                destination           ports
   10   840 ACCEPT     icmp ----- 0xFF 0x00  lo                    anywhere              anywhere              any
# ipchains -Z input
# ipchains -v -L input
Chain input (refcnt = 1): (policy ACCEPT)
 pkts bytes target     prot opt   tosa tosx  ifname    mark        source                destination           ports
    0     0 ACCEPT     icmp ----- 0xFF 0x00  lo                    anywhere              anywhere              any
#

这种方法的问题在于,有时您需要在重置之前立即知道计数器值。在上面的示例中,在 “-L” 和 “-Z” 命令之间可能会有一些数据包通过。因此,您可以一起使用 “-L” 和 “-Z”,以在读取计数器时重置它们。不幸的是,如果您这样做,则无法对单个链进行操作:您必须一次列出和归零所有链。

# ipchains -L -v -Z
Chain input (policy ACCEPT):
 pkts bytes target     prot opt   tosa tosx  ifname    mark        source                destination           ports
   10   840 ACCEPT     icmp ----- 0xFF 0x00  lo                    anywhere              anywhere              any

Chain forward (refcnt = 1): (policy ACCEPT)
Chain output (refcnt = 1): (policy ACCEPT)
Chain test (refcnt = 0):
    0     0 DENY       icmp ----- 0xFF 0x00  ppp0                  localnet/24           anywhere              any
# ipchains -L -v
Chain input (policy ACCEPT):
 pkts bytes target     prot opt   tosa tosx  ifname    mark        source                destination           ports
   10   840 ACCEPT     icmp ----- 0xFF 0x00  lo                    anywhere              anywhere              any

Chain forward (refcnt = 1): (policy ACCEPT)
Chain output (refcnt = 1): (policy ACCEPT)
Chain test (refcnt = 0):
    0     0 DENY       icmp ----- 0xFF 0x00  ppp0                  localnet/24           anywhere              any
#

设置策略

当我们讨论数据包如何在 指定目标 中的链中漫游时,我们忽略了当数据包到达内置链的末尾时会发生什么。在这种情况下,链的 策略 决定了数据包的命运。只有内置链(inputoutputforward)具有策略,因为如果数据包从用户定义的链的末尾掉下来,遍历将在上一个链中恢复。

策略可以是前四个特殊目标中的任何一个:ACCEPTDENYREJECTMASQMASQ 仅对 “forward” 链有效。

同样重要的是要注意,内置链之一中的规则中的 RETURN 目标对于在数据包与规则匹配时显式定位链策略很有用。

伪装操作

您可以为 IP 伪装调整几个参数。它们与 ipchains 捆绑在一起,因为为它们编写单独的工具是不值得的(尽管这将会改变)。

IP 伪装命令是 “-M”,它可以与 “-L” 组合以列出当前伪装的连接,或与 “-S” 组合以设置伪装参数。

“-L” 命令可以伴随 “-n”(显示数字而不是主机名和端口名称)或 “-v”(显示伪装连接的序列号中的增量,以防您关心)。

“-S” 命令后应跟三个超时值,每个值以秒为单位:用于 TCP 会话、FIN 数据包后的 TCP 会话和 UDP 数据包。如果您不想更改这些值之一,只需给出一个值 “0”。

默认值在 “/usr/src/linux/include/net/ip_masq.h” 中列出,当前分别为 15 分钟、2 分钟和 5 分钟。

最常需要更改的值是第一个,用于 FTP (参见下文 FTP 梦魇)。

请注意 我无法设置伪装超时! 中列出的设置超时问题。

检查数据包

有时您想查看当某个数据包进入您的机器时会发生什么,例如为了调试您的防火墙链。ipchains 具有 `-C' 命令来允许这样做,它使用与内核用于诊断真实数据包完全相同的例程。

您可以通过在 `-C' 参数后跟链的名称来指定要测试数据包的链。虽然内核总是从 inputoutputforward 链开始遍历,但您被允许为了测试目的从任何链开始遍历。

数据包的详细信息使用与指定防火墙规则相同的语法指定。特别是,协议 (`-p')、源地址 (`-s')、目标地址 (`-d') 和接口 (`-i') 是强制性的。如果协议是 TCP 或 UDP,则必须指定单个源端口和单个目标端口,并且必须为 ICMP 协议指定 ICMP 类型和代码(除非指定 `-f' 标志以指示分片规则,在这种情况下,这些选项是非法的)。

如果协议是 TCP(并且未指定 `-f' 标志),则可以指定 `-y' 标志,以指示测试数据包应设置 SYN 位。

这是一个测试 TCP SYN 数据包的示例,该数据包来自 192.168.1.1 端口 60000 到 192.168.1.2 端口 www,通过 eth0 接口进入,进入 `input' 链。(这是一个经典的传入 WWW 连接启动)

# ipchains -C input -p tcp -y -i eth0 -s 192.168.1.1 60000 -d 192.168.1.2 www
packet accepted
# 

一次处理多条规则并观察发生的情况

有时,单行命令可能会导致多条规则生效。这通过两种方式完成。首先,如果您指定的主机名(使用 DNS)解析为多个 IP 地址,则 ipchains 的行为就像您键入了多个命令,每个命令都包含地址的每种组合。

因此,如果主机名 `www.foo.com' 解析为三个 IP 地址,而主机名 `www.bar.com' 解析为两个 IP 地址,则命令 `ipchains -A input -j reject -s www.bar.com -d www.foo.com' 将向 input 链追加六条规则。

ipchains 执行多个操作的另一种方法是使用双向标志 (`-b')。此标志使 ipchains 的行为就像您键入了两次命令一样,第二次命令将 `-s' 和 `-d' 参数反转。因此,为了避免转发到或来自 192.168.1.1,您可以执行以下操作

# ipchains -b -A forward -j reject -s 192.168.1.1
# 

就我个人而言,我不太喜欢 `-b' 选项;如果您想要方便,请参阅下文 使用 ipchains-save

-b 选项可以与 insert (`-I')、delete (`-D')(但不是采用规则编号的变体)、append (`-A') 和 check (`-C') 命令一起使用。

另一个有用的标志是 `-v'(verbose),它会准确打印出 ipchains 对您的命令执行的操作。如果您正在处理可能影响多条规则的命令,这将非常有用。例如,在这里我们检查 192.168.1.1 和 192.168.1.2 之间分片的行为。

# ipchains -v -b -C input -p tcp -f -s 192.168.1.1 -d 192.168.1.2 -i lo
  tcp opt   ---f- tos 0xFF 0x00  via lo    192.168.1.1  -> 192.168.1.2    * ->   *
packet accepted
  tcp opt   ---f- tos 0xFF 0x00  via lo    192.168.1.2  -> 192.168.1.1    * ->   *
packet accepted
# 

4.2 有用的示例

我有一个拨号 PPP 连接 (-i ppp0)。我每次拨号上网时都会抓取新闻 (-p TCP -s news.virtual.net.au nntp) 和邮件 (-p TCP -s mail.virtual.net.au pop-3)。我使用 Debian 的 FTP 方法定期更新我的机器 (-p TCP -y -s ftp.debian.org.au ftp-data)。我在进行这些操作时通过我的 ISP 代理上网 (-p TCP -d proxy.virtual.net.au 8080),但讨厌 Dilbert Archive 上的 doubleclick.net 广告 (-p TCP -y -d 199.95.207.0/24-p TCP -y -d 199.95.208.0/24)。

我不介意人们在我在线时尝试 ftp 连接到我的机器 (-p TCP -d $LOCALIP ftp),但我不希望任何外部人员冒充我的内部网络的 IP 地址 (-s 192.168.1.0/24)。这通常称为 IP 欺骗,并且在 2.1.x 及更高版本的内核中有一种更好的方法来保护自己免受其害:请参阅 如何设置 IP 欺骗保护?

此设置相当简单,因为目前我的内部网络上没有其他盒子。

我不希望任何本地进程(即 Netscape、lynx 等)连接到 doubleclick.net

# ipchains -A output -d 199.95.207.0/24 -j REJECT
# ipchains -A output -d 199.95.208.0/24 -j REJECT
# 

现在我想设置各种传出数据包的优先级(对传入数据包执行此操作没有多大意义)。由于我有相当多的这些规则,因此将它们全部放在一个名为 ppp-out 的链中是有意义的。

# ipchains -N ppp-out
# ipchains -A output -i ppp0 -j ppp-out
# 

Web 流量和 telnet 的最小延迟。

# ipchains -A ppp-out -p TCP -d proxy.virtual.net.au 8080 -t 0x01 0x10
# ipchains -A ppp-out -p TCP -d 0.0.0.0/0 telnet -t 0x01 0x10
# 

ftp 数据、nntp、pop-3 的低成本

# ipchains -A ppp-out -p TCP -d 0.0.0.0/0 ftp-data -t 0x01 0x02
# ipchains -A ppp-out -p TCP -d 0.0.0.0/0 nntp -t 0x01 0x02
# ipchains -A ppp-out -p TCP -d 0.0.0.0/0 pop-3 -t 0x01 0x02
# 

ppp0 接口传入的数据包有一些限制:让我们创建一个名为 `ppp-in' 的链

# ipchains -N ppp-in
# ipchains -A input -i ppp0 -j ppp-in
# 

现在,没有传入 ppp0 的数据包应该声明 192.168.1.* 的源地址,所以我们记录并拒绝它们

# ipchains -A ppp-in -s 192.168.1.0/24 -l -j DENY
#

我允许 UDP 数据包进入以进行 DNS(我运行一个缓存名称服务器,它将所有请求转发到 203.29.16.1,所以我只期望来自他们的 DNS 回复)、传入的 ftp 以及仅返回 ftp-data(它应该仅发送到高于 1023 的端口,而不是 6000 左右的 X11 端口)。

# ipchains -A ppp-in -p UDP -s 203.29.16.1 -d $LOCALIP dns -j ACCEPT
# ipchains -A ppp-in -p TCP -s 0.0.0.0/0 ftp-data -d $LOCALIP 1024:5999 -j ACCEPT
# ipchains -A ppp-in -p TCP -s 0.0.0.0/0 ftp-data -d $LOCALIP 6010: -j ACCEPT
# ipchains -A ppp-in -p TCP -d $LOCALIP ftp -j ACCEPT
#

我允许 TCP 回复数据包返回

# ipchains -A ppp-in -p TCP ! -y -j ACCEPT
#

最后,本地到本地的数据包是可以的

# ipchains -A input -i lo -j ACCEPT
# 

现在,我在 input 链上的默认策略是 DENY,所以其他所有内容都会被丢弃

# ipchains -P input DENY
# 

注意:我不会按此顺序设置我的链,因为数据包可能会在我设置时通过。最安全的方法通常是首先将策略设置为 DENY,然后插入规则。当然,如果您的规则需要 DNS 查找来解析主机名,您可能会遇到麻烦。

使用 ipchains-save

以您想要的方式设置防火墙链,然后尝试记住您使用的命令以便下次可以执行它们,这很麻烦。

因此,ipchains-save 是一个脚本,它读取您当前的链设置并将其保存到文件中。目前,关于 ipchains-restore 的作用,我将让您保持悬念。

ipchains-save 可以保存单个链,也可以保存所有链(如果未指定链名称)。当前唯一允许的选项是 `-v',它会在保存规则时打印规则(到 stderr)。inputoutputforward 链的策略也会被保存。

# ipchains-save > my_firewall
Saving `input'.
Saving `output'.
Saving `forward'.
Saving `ppp-in'.
Saving `ppp-out'.
# 

使用 ipchains-restore

ipchains-restore 恢复使用 ipchains-save 保存的链。它可以接受两个选项:`-v',它描述添加的每个规则,以及 `-f',如果用户定义的链存在,则强制刷新用户定义的链,如下所述。

如果在输入中找到用户定义的链,ipchains-restore 会检查该链是否已存在。如果存在,则会提示您是否应刷新(清除所有规则)链,或者是否应跳过恢复此链。如果您在命令行上指定了 `-f',则不会提示您;链将被刷新。

例如

# ipchains-restore < my_firewall
Restoring `input'.
Restoring `output'.
Restoring `forward'.
Restoring `ppp-in'.
Chain `ppp-in' already exists. Skip or flush? [S/f]? s
Skipping `ppp-in'.
Restoring `ppp-out'.
Chain `ppp-out' already exists. Skip or flush? [S/f]? f
Flushing `ppp-out'.
# 


下一页 上一页 目录