欢迎您来到中国万网客户服务中心!
客服导航
如果您在使用我们的产品中遇到问题,建议您首先在“常见问题”中查询解决方法;
如果没有找到该问题的解决方法,您可以在“问题搜索”中进行搜索;
如果搜索后没有找到满意答案,您可以“在线提问”,我们会在1个工作日内给您答复。
您现在的位置: 中国万网客服中心 >> 常见问题 >> 软件常见问题 >> 虚拟主机教程 >> 文章正文

ipfw 手册

作者:客服中心    文章来源:中国万网    点击数:    更新时间:2007-6-25

ipfw  FreeBSD 内建的防火墙指令,我们可以用它来管理进出的网络交通。如果防火墙服务器是扮演着路由器 (gateway 例如上一篇中的 NAT 服务器的角色,则进出的封包会被 ipfw 处理二次,而如果防火墙扮演的是桥接器 (bridge) 的角色,则封包只会被处理一次。这个观念关系着我们以下所要介绍的语法,有的语法并不适用于桥接器。

另外,我们在设定防火墙时有二种模式,一种模式是预设拒绝所有联机,再一条一条加入允许的联机;另一种是预设接受所有联机,加入几条拒绝的规则。如果是非常强调安全性,应该是使用预设拒绝所有联机,再一条一条加入我们允许的规则。

我们会将 firewall 的设定写在 /etc/rc.firewall 中,每一条设定都是以先入为主 (first match wins) 的方式来呈现,也就是先符合的规则 (rules) 为优先。所有进出的封包都会被这些规则过滤,因此我们会尽量减少规则的数量,以加速处理的速度。

 kernel 中,关于防火墙的设定有下列几条:

防火墙

options IPFIREWALL

支援 NAT

options IPDIVERT

下面这一行是预设允许所有封包通过,如果没有这一行,


就必须在 /etc/rc.firewall 中设定封包的规则。

这条规则内定编号是 65535,也就是所有规则的最后一条

如果没有加这一条规则,内定就是拒绝所有封包,

只允许规则中允许的封包通过。

options IPFIREWALL_DEFAULT_TO_ACCEPT

这一行是让您可以在 ipfw 中设定要记录哪些封包,

如果没有这一行,就算设定了要留下记录也不会有作用。

options IPFIREWALL_VERBOSE

这一行是限制每一条规则所要记录的封包数量,

因为同样的规则可能有许多记录,加上这一条可以使

同样的记录重复数减少,以避免记录文件爆增。

options IPFIREWALL_VERBOSE_LIMIT=10

下面这一行是用来支援封包转向,

当您要使用 fwd 动作时必须要有这一项设定。

options IPFIREWALL_FORWARD

如果要使用 pipe 来限制频宽,必须加入下列选项以支持 dummynet

options DUMMYNET

ipfw 
也支持状态维持 (keep-state) 的功能,就是可以让符合设定的规则以动态的方式来分配增加规则 (地址或连接端口来让封包通过。也就是说防火墙可以记住一个外流的封包所使用的地址及连接端口,并在接下来的几分钟内允许外界响应。这种动态分配的规则有时间的限制,一段时间内会检查联机状态,并清除记录。

所有的规则都有计数器记录封包的数量、位数、记录的数量及时间等。而这些记录可以用 ipfw 指令来显示或清除。

在说明 ipfw 规则的语法之前,我们先来看这个指令的用法。ipfw 可以使用参数:

指令 说明 

ipfw add [rule] 
新增一条规则。规则 (rule) 的语法请参考下一节的说明。
 

ipfw delete [number] 
删除一条编号为 number 的规则。
 

ipfw -f flush 
清除所有的规则。
 

ipfw zero 
将计数统计归零。
 

ipfw list 
列出现在所有规则,可以配合下列参数使用。
 

-a 
使用 list 时,可以列出封包统计的数目。
 

-f 
不要提出确认的询问。
 

-q 
当新增 (add)、归零(zero)、或清除 (flush) 时,不要列出任何回应。当使用远程登入,以 script ( sh /etc/rc.firewall) 来修改防火墙规则时,内定会列出您修改的规则。但是当下了 flush 之后,会立即关掉所有联机,这时候响应的讯息无法传达终端机,而规则也将不被继续执行。此时唯一的方法就是回到该计算机前重新执行了。在修改防火墙规则时,最好在计算机前修改,以免因为一个小错误而使网络联机中断。
 

-t 
当使用 list 时,列出最后一个符合的时间。
 

-N 
在输出时尝试解析 IP 地址及服务的名称。
 

-s [field] 
当列出规则时,依哪一个计数器 (封包的数量、位数、记录的数量及时间来排序。
 

12.3.1 ipfw 
规则


我们在过滤封包时,可以依据下列的几个封包所包含的信息来处理该封包:

接收或传送的接口,可以使用接口名称或地址。 

方向,流入或流出。
 

来源或目的地的 IP 地址,也可以加上子网掩码。
 

通讯协议,TCP,UDP,ICMP 等。
 

TCP flags
 

IP fragment flag
 

IP options
 

ICMP 
的类型。
 

和封包相关的 socket User/group ID
 

使用 IP 地址或 TCP/UDP 的端口号来做为规则可能蛮危险的,因为这二种都有可能被以假的信息所蒙骗 (spoof)。但是这二种却也是最常被使用的方法。


下列为 ipfw rules 的语法:

[number] action [log] proto from src to dist [interface_spec] [option]

使用 [ ] 包起来的表示可有可无,我们一一为大家说明它们的意义:

number


number 
是一个数字,用来定义规则的顺序,因为规则是以先入为主的方式处理,如果您将规则设定放在一个档案中 (  /etc/rc.firewall ),规则会依每一行排列的顺序自动分配编号。您也可以在规则中加上编号,这样就不需要按顺序排列了。如果是在命令列中下 ipfw 指令来新增规则的话,也要指定编号,这样才能让规则依我们的喜好排列,否则就会以指令的先后顺序来排。这个编号不要重复,否则结果可能不是您想要的样子。

action


action 
表示我们这条规则所要做的事,可以用的 action 有下列几个:

命令 意义 

allow 
允许的规则,符合则通过。也可以使用 pass,permit, accept 等别名。
 

deny 
拒绝通过的规则。
 

reject 
拒绝通过的规则,符合规则的封包将被丢弃并传回一个 host unreachable  ICMP
 

count 
更新所有符合规则的计数器。
 

check-state 
检查封包是否符合动态规则,如果符合则停止比对。若没有 check-state 这条规则,动态规则将被第一个 keep-state 的规则所检查。
 

divert port 
将符合 divert sock 的封包转向到指定的 port
 

fwd ipaddr[,port] 
将符合规则封包的去向转向到 ipaddripaddr 可以是 IP 地址或是 hostname。如果设定的 ipaddr 不是直接可以到达的地址,则会依本机即有的 routing table 来将封包送出。如果该地址是本地地址 (local address),则保留本地地址并将封包送原本指定的 IP 地址。这项设定通常用来和 transparent proxy 搭配使用。例如:
 

# ipfw add 50000 fwd 127.0.0.1,3128 tcp from \

192.168.1.0/24 to any 80

如果没有设定 port ,则会依来源封包的 port 将封包送到指定的 IP。使用这项规则时,必须在 kernel 中设定选项 IPFIREWALL_FORWARD


pipe pipe_nr 
传递封包给 dummynet(4) "pipe",用以限制频宽。使用本语法必须先在核心中加入 option DUMMYNET。请 man ipfw  man dummynet

基本语法是先将要设定频宽的规则加入: 

ipfw add pipe pipe_nr ....

再设定该规则的频宽:


ipfw pipe pipe_nr config bw B delay D queue Q plr P

这里的 pipe_nr 指的是 pipe 规则编号,从 1~65535是指频宽,可以表示为 bit/sKbit/sMbit/sBytes/sKBytes/s、或 MBytes/s是延迟多少 milliseconds (1/1000) queue size 的大小 (单位为 packages  Bytes)是要随机丢弃的封包数量。

例如我们要限制内部网域的计算机对外上传的最大频宽是 20 KBytes

ipfw add pipe 1 ip from 192.168.0.1/24 to any in

ipfw pipe 1 config bw 20KBytes/s

log


如果该规则有加上 log 这个关键词,则会将符合规则的封包记录在 /var/log/security 中。前提是在核心中有设定 IPFIREWALL_VERBOSE 的选项。有时因为同样的封包太多,会使记录文件保有大量相同的记录,因此我们会在核心中再设定 IPFIREWALL_VERBOSE_LIMIT 这个选项,来限制要记录多少相同的封包。

proto


proto 
表示 protocol,即网络协议的名称,如果使用 ip  all 表示所有协议。可以使用的选项有 ip,all,tcp,udp,icmp 等。

src 
 dist

src 
是封包来源;dist 是封包目的地。在这二个项目可以用的关键词有 any, me, 或是以 <address/mask>[ports] 的方式明确指定地址及端口号。

若使用关键词 any 表示使这条规则符合所有 ip 地址。若使用关键词 me 则代表所有在本系统接口的 IP 地址。而使用明确指定地址的方式有下列三种:

IP 
地址,指定一个 IP,如 168.20.33.45 

IP/bits
,如 1.2.3.4/24,表示所有从 1.2.3.0  1.2.3.255  IP 都符合规则。
 

IP:mask
,由 IP 加上子网掩码,如 1.2.3.4:255.255.240.0 表示从 1.2.0.0  1.2.15.255 都符合。
 

而在 me, any  指定的 ip 之后还可以再加上连接埠编号 (ports),指定 port 的方法可以是直接写出 port ,如 23;或给定一个范围,如 23-80;或是指定数个 ports,如 23,21,80 以逗点隔开。或者是写出在 /etc/services 中所定义的名称,如 ftp,在 services 中定义是 21,因此写 ftp 则代表 port 21


interface-spec


interface-spec 
表示我们所要指定的网络接口及流入或流出的网络封包。我们可以使用下列几个关键词的结合:

关键词 意义 

in 
只符合流入的封包。
 

out 
只符合流出的封包。
 

via ifX 
封包一定要经过接口 ifXif 为接口的代号,为编号,如 vr0
 

via if* 
表示封包一定要经过接口 ifXif 为接口的代号,而 * 则是任何编号,如 vr* 代表 vr0,vr1,...
 

via any 
表示经过任何界面的封包。
 

via ipno 
表示经过 IP  ipno 界面的封包。
 

via 
会使接口永远都会被检查,如果用另一个关键词 recv ,则表示只检查接收的封包,而 xmit 则是送出的封包。这二个选项有时也很有用,例如要限制进出的接口不同时:


ipfw add 100 deny ip from any to any out recv vr0 xmit ed1

recv 
接口可以检查流入或流出的封包,而 xmit 接口只能检查流出的封包。所以在上面这里一定要用 out 而不能用 in,只要有使用 xmit 就一定要使用 out。另外,如果 via  recv  xmit 一起使用是没有效的。

有的封包可能没有接收或传送的接口:例如原本就由本机所送出的封包没有接收接口,而目的是本机的封包也没有传送界面。

options


我们再列出一些常用的 option 选项 ,更多选项请 man ipfw

选项名称 意义 

keep-state 
当符合规则时,ipfw 会建立一个动态规则,内定是让符合规则的来源及目的地使用相同的协议时就让封包通过。这个规则有一定的生存期限 (lift time,由 sysctl 中的变量所控制),每当有新的封包符合规则时,便用重设生存期限。
 

bridged 
只符合 bridged 的封包。
 

established 
只适用于 TCP 封包,当封包中有 RST  ACK bits 时就符合。
 

uid xxx 
当使用者 uid  xxx 则符合该规则。例如,我们如果要限制 Anonymous FTP 的下载速度最大为 64KB/s,则可以使用:


ipfw pipe 1 config bw 512Kbit/s

ipfw add pipe 1 tcp from me to any uid 21

上列规则第一行是先建一个编号为 1  pipe,限制频宽为 512 Kbit/s (也就是 64 KByte/s),接着第二条是当使用者 uid  21 时,从本机 (me) 下载的 tcp 封包都使用编号 1  pipe。因为 Anonymous FTP 的使用者是 ftp,它的预设 uid  21,所以这条规则会被套用在 Anonymous FTP user 上。

setup 
只适用于 TCP 封包,当封包中有 SYN bits 时就符合。 

以上的说明只是 man ipfw 中的一小部份。如果您想要对 ipfw 更了解,例如如何使用 ipfw 来限制频宽等,建议您 man ipfw


不知道您看了这么多的规则是否觉得眼花撩乱,如果不了解 TCP/IP 的原理,彻底了解 ipfw 的设定还真不容易。没关系,我们下面将举几个简单、常用的设定,这些范例应该够平常使用了。

12.3.2 
范例

我将原本的 /etc/rc.firewall 备份成 rc.firewal.old,并将它改成下列内容,请注意,这里只是范例,只供参考:

设定我的 IP

myip="1.2.3.4"

设定对外的网络卡代号


outif="vr0"

设定对内的网络上代号

inif="vr1"

#
清除所有的规则

/sbin/ipfw -f flush

# Throw away RFC 1918 networks

$ add deny ip from 10.0.0.0/8 to any in via $

$ add deny ip from 172.16.0.0/12 to any in via $

$ add deny ip from 192.168.0.0/16 to any in via $

只允许内部网络对 192.168.0.1 使用 telnet 服务

/sbin/ipfw add 200 allow tcp from 192.168.0.1/24 to 192.168.0.1 telnet

拒绝其它人连到 port 23,并记录尝试联机的机器

/sbin/ipfw add 300 deny log tcp from any to me 23

拒绝任何 ICMP 封包

/sbin/ipfw add 400 deny icmp from any to any

下面这台机器是坏人,不让它进来,并记录下来

/sbin/ipfw add 1100 deny log all from 211.21.104.102 to any

# NAT 
的设定

/sbin/ipfw add divert natd all from any to any via vr0

限制内部网域对外下载最大频宽为 20KBytes/s,上传最大频宽为 5KBytes/s

ipfw pipe 20 config bw 20KBytes/s

ipfw add pipe 20 ip from any to 192.168.0.1/24 out

ipfw pipe 30 config bw 5KBytes/s

ipfw add pipe 30 ip from 192.168.0.1/24 to any in

允许本机对任何地方联机


/sbin/ipfw add check-state

/sbin/ipfw add 2000 allow udp from $ to any keep-state

/sbin/ipfw add 2100 pass ip from $  to any

允许外界使用邮件服务

/sbin/ipfw add 3000 pass tcp from any to $ 25 in via $

不允许内部的 IP 从外部连进来

/sbin/ipfw add 1200 add deny ip from $/24 to any in via $

其它都拒绝,如果没有在 kernel 中设定

# IPFIREWALL_DEFAULT_TO_ACCEPT 
则内定就有下列这一条

/sbin/ipfw 65535 add deny all from any to any

存盘后就可以使用 sh rc.firewall 来执行新的规则了。如果您将规则放在 /etc/rc.firewall 中,则开机时会自动执行。

12.3.3 
一些小建议

在建立一个封包过滤的防火墙时,应该尽可能阻挡一些不必要的服务。避免开放 port 1024 以下的 TCP 服务,例如只通过 SMTP 封包 (port 25) 给邮件服务器;拒绝所有 UDP 联机 (只有少部份服务如 NFS 会用到);一些只有内部才会使用的服务,如数据库等也不必对外开放。

另外,同样的防火墙限制可以使用不同的语法来展现,应该要试着让规则数量越少越好,以加快处理速度。

在更新 firewall 规则时,如果规则没有写好,而您又是以远程登入的方式修改规则,很可能会因此无法继续登入。因此建议更新规则时最好在 console 前执行,若迫不得已一定要使用远程登入,建议您执行 /usr/share/examples/ipfw/change_rules.sh 这支程序来编辑规则:

# cd /usr/share/examples/ipfw

# sh change_rules.sh

接着会出现文书编辑软件并最动加载 /etc/rc.firewall 让您编辑,结束离开后,会询问是否要执行更新。如果执行新的规则后造成断线,它会自动加载旧的规则,让我们可以再次联机。

12.4 
封包过滤桥接器

如果您有三台机器全部都有 public IP,而您想使用其中一台做为防火墙,在不改变另外二台机器的设定下,我们可以使具封包过滤的桥接器来架设防火墙。只要将这台桥接器放在另外二台和对外网络之间即可。

另外,当我们的内部网络有不同 class 的主机时,例如内部有 140.115.2.3  140.115.5.6 这二台计算机时,就无法使用传统的防火墙。如果要在这二台机器连到因特网中途中使用防火墙,我们必须使用新的方式,也可以使用这里介绍的桥接器。

我们可以使用 FreeBSD 为桥接器,利用它来做封包过滤的动作,而丝毫不影响内部的主机原本的设定。为了达到这个功能,我们必需要有二张支持 promiscuous mode 的网络卡,现在的网络卡大部份都有支持。二张网络卡当中,一张需要设定 IP,另一张不需要。至于您要将 IP 设定在哪一张卡都可以,建议是设在对外的网络卡上。

首先,我们必须在核心中加入关于桥接器的设定:

支援桥接器

options BRIDGE

防火墙设定

options IPFIREWALL

options IPFIREWALL_VERBOSE

我们这里不将防火墙预设为接收所有封包

#options IPFIREWALL_DEFAULT_TO_ACCEPT

如果您要让桥接器具有流量控制的功能,则可以加上之前提到的选项「options DUMMYNET」。重新编译核心后,在重开机前,我们先设定一下 /etc/rc.conf

firewall_enable="YES"

firewall_type="open"

还有一件事要做,当在以太网络上跑 IP 协议时,事实上使用二种以太网络协议,一个是 IP,另一个是 ARPARP 协定是当机器要找出给定 IP 地址所对应的以太网络地址时使用的。ARP 并不是 IP 层的一部份,只是给 IP 应用在以太网络上运作。标准的防火墙规则中并未加入对于 ARP 的支持,幸运的是,高手们的在 ipfirewall 程序代码中加入了对封包过滤桥接器的支持。如果我们在 IP 地址 0.0.0.0 上建立一个特别的 UDP 规则,UDP 端口的号码将被使用来搭配被桥接封包的以太网络协议号码,如此一来,我们的桥接器就可以被设定成传递或拒绝非 IP 的协议。请在 /etc/rc.firewall 中接近文件顶端处理 lo0 的那三行之下(就是有写 Only in rare cases do you want to change these rules 的地方)加入下面一行:

$ add allow udp from 0.0.0.0 2054 to 0.0.0.0

现在我们就可以重新开机了。重开机之后,先执行下列指令来启动桥接器:

如果您使用的是 FreeBSD 4.x

# sysctl -w net.link.ether.bridge_ipfw=1

# sysctl -w net.link.ether.bridge=1

如果您使用的是 FreeBSD 5.x

# sysctl -w net.link.ether.bridge.ipfw=1

# sysctl -w net.link.ether.bridge.enable=1

现在我们可以将机器放在内外二个网域之间了。因为我们之前在 /etc/rc.conf 中,设定防火墙完全打开,不阻挡任何封包,所以放在二个网域之间时,运作应该没有问题。我们之前只设了一张网络上的 IP,而在执行了上述的指令之后,第二张网络卡便开始运作。

下一步就是将我们启动桥接器的指令放在 /etc/rc.local 中,让系统在开机时自动执行。或者,我们可以在 /etc/sysctl.conf 中加入下面二行:

如果您使用的是 FreeBSD 4.x

net.link.ether.bridge_ipfw=1

net.link.ether.bridge=1

如果您使用的是 FreeBSD 5.2 以后的版本


net.link.ether.bridge.enable=1

net.link.ether.bridge.ipfw=1

接下来我们就可以依自己的需求在 /etc/rc.firewall 文件的最后面加上我们自己想要的防火墙规则了。以下是一个简单的设定规则,假设桥接器的 IP  140.115.75.137,内部有二台主机,一台提供网页服务,一台是 BBS

us_ip=140.115.75.137

basrv_ip=140.115.3.4

bbs_ip=140.115.5.6

oif=fxp0

iif=fxp1

ipfw="/sbin/ipfw"

# Things that we've kept state on before get to go through in a hurry.

$ 1000 add check-state

# Throw away RFC 1918 networks

$ 1100 add deny ip from 10.0.0.0/8 to any in via $

$ 1200 add deny log ip from 172.16.0.0/12 to any in via $

$ 1300 add deny log ip from 192.68.0.0/16 to any in via $

允许桥接器本身所有想做的联机 (keep state if UDP)

$ 1400 add pass udp from $ to any keep-state

$ 1500 add pass ip from $ to any

允许内部网络任何想做的联机
 (keep state if UDP)

$ 1600 add pass udp from any to any in via $ keep-state

$ 1700 add pass ip from any to any in via $

允许任何的 ICMP 联机


$ 1800 add pass icmp from any to any

不允许使用 port 888 联机

$ 2000 add deny log tcp from any to $ 888

# TCP section

任何地方都可以建立 TCP 联机

$ 3000 add pass tcp from any to any via $

# Pass the "quarantine" range.

$ 3100 add pass tcp from any to any 49152-65535 in via $

# Pass ident probes. It's better than waiting for them to timeout

$ 3200 add pass tcp from any to any 113 in via $

# Pass SSH.

$ 3300 add pass tcp from any to any 22 in via $

# Pass DNS. 
当内部网络有名称服务器时才需要

#$ add pass tcp from any to any 53 in via $

只传递 SMTP 给邮件服务器

$ 3400 add pass tcp from any to $ 25 in via $

$ 3500 add pass tcp from any to $ 25 in via $

# UDP section

# Pass the "quarantine" range.

$ 4000 add pass udp from any to any 49152-65535 in via $

# Pass DNS. 
当内部网络有名称服务器时才需要

#$ 4100 add pass udp from any to any 53 in via $

其它的都拒绝
$ 60000 add deny ip from any to any  


返回上一页