分类: BSD
2009-05-12 10:53:49
ipfw – IP防火墙和流量整形的控制程序
ipfw [-cq] add rule
ipfw [-acdefnNStT] [set N] {list | show} [rule | first-last ...]
ipfw [-f | -q] [set N] flush
ipfw [-q] [set N] {delete | zero | resetlog} [number ...]
ipfw enable
{firewall | altq | one_pass | debug | verbose | dyn_keepalive}
ipfw disable
{firewall | altq | one_pass | debug | verbose | dyn_keepalive}
ipfw set [disable number ...] [enable number ...]
ipfw set move [rule] number to number
ipfw set swap number number
ipfw set show
ipfw table number add addr[/masklen] [value]
ipfw table number delete addr[/masklen]
ipfw table number flush
ipfw table number list
ipfw {pipe | queue} number config config-options
ipfw [-s [field]] {pipe | queue} {delete | list | show} [number ...]
ipfw nat number config config-options
ipfw [-cfnNqS] [-p preproc [preproc-flags]] pathname
ipfw是FreeBSD下的一个用户界面,它用来控制ipfw防火墙和流量整形。
ipfw的配置,也叫做规则集,它是由一系列的规则组成的,这些规则的编号是从1到65535。通过ipfw的数据包可以来自协议栈的许多各个的地方 (这主要依赖于数据包的来源和目的,某些数据包很可多次经过防火墙)。数据包在通过防火墙的时候,要和防火墙规则集逐条对比。如果发现匹配的规则,则执行 相应规则的动作。
在特定的规则动作和系统设置下,数据包在匹配完一个规则后,可能在某些规则之后重新进入防火墙。
一个ipfw规则集总是包含一个默认的规则(编号为65535),这个默认规则匹配所有的数据包,不能修改或删除。根据内核配置的不同,这个默认规则或者是拒绝所以数据包通过,或者是允许所有数据包通过。
如果规则集中存在一个或多个包含有keep-state(状态保持)或limit(限制)的规则,那么ipfw就表现为状态保持。例如:当这种 规则匹配了一个数据包时,ipfw将创建一个动态规则,让后来的,同这个数据包的地址和端口参数严格一致的数据包,与这个动态规则相匹配。
这些动态规则,都有一个有限的生存期。生存期在第一次遇到check-state、keep-state或者是limit规则时被检查。这些动 态规则的典型应用,是用来保证合法的数据包在通过时开启防火墙。查看下面的“状态防火墙”和“示例”章节,获取更多关于状态保持的信息。
包括动态规则在内的所有规则,都有几个相关的计数器:一个包计数器,一个字节计数器,一个记录计数器和一个用来提示最后匹配时间的时间戳。这些计数器都可以被ipfw命令显示和清零。
可以用add命令来添加规则;可以用delete命令来删除单个或一组规则;可以用flush命令来删除所有的规则(set 31除外);可以用show和list命令来显示包括计数器在内的信息。最后,计数器可以被zero和resetlog命令清零。
并且,每个规则隶属于32个不同的规则集中的一个。有一些ipfw的命令可以独立地操作这些规则集,例如:启动、禁止、交换规则集;把一个规则 集中的所有规则移动到另一个规则集;删除一个规则集中的所有规则。这对于安装临时配置,或者是测试配置是非常有用的。查看“规则的规则集”一节,获取更多 关于 set的信息。
以下是ipfw命令可以利用的选项:
当显示pipe时,按照四个计数器之一排序显示。(对于所有的数据包、当前数据包或者是字节数)
为了使配置更容易,规则可以写到一个文件里供ipfw处理,语法规则参照大纲栏最后一行(译注:指本文最前面的语法部分最后一行,即:“ipfw [-cfnNqS] [-p preproc [preproc-flags]] pathname”)。注意必须是绝对路径。这个文件会被逐行读取并且将作为ipfw工具的参数处理。
作为选择,你还可以使用-p preproc参数来使用预处理程序,其中路径是用管道来传入的。可以使用的预处理程序包括cpp和m4。如果preproc不是以”/“开头,那么将搜 索PATH里的路径。在使用它的时候,要谨慎,因为在很多时候,ipfw启动时,系统文件可能没有挂接(比如:文件系统是通过NFS挂接的)。一旦使用了 -p参数,所有其它的参数,都被传送到预处理程序去等待处理。
ipfw的pipe和queue命令用来配置流量整形,你可以下面的“TRAFFIC SHAPER (DUMMYNET) CONFIGURATION”节看到看到相关内容。
如果系统的world和内核不能保持一致,ipfw的ABI将失效,以防你加入任何规则。这样能保证你的顺利地完成启动过程。你可以利用ipfw临时禁止的机会登录你的网络,来修复你的问题。
一个数据包在几个内核变量的控制下,在协议栈的多个地方被防火墙检查。这些地方和变量如下图所示,要在头脑中保持下图,它对于规划一下正确的规则集是非常重要的。
^ 到上层 V
| |
+----------->-----------+
^ V
[ip(6)_input] [ip(6)_output] net.inet(6).ip(6).fw.enable=1
| |
^ V
[ether_demux] [ether_output_frame] net.link.ether.ipfw=1
| |
+-->--[bdg_forward]-->--+ net.link.bridge.ipfw=1
^ V
| 到设备 |
如图所示,根据数据包的源和目的,以及系统配置的不同,同一个数据包穿过防火墙的次数在0和4之间变化。
需要注意的是,在包通过不同栈的时候,头会被加上或剥离,可能会导致这些包被检查不到。例如:当ipfw被ether_demux()调用的时 候,进入的包将包括MAC头;而当ipfw被ip_input()或者ip6_input()调用的时候,同一个数据包的MAC头将被剥离。
还需要注意的是:不论检查发生在什么地方,也不论数据包源自哪里,每个数据包也整个规则进行对照。(译注:是整个规则集吗?有时候只对照一部分 的时候就中止检查)。如果一个规则包含的一些匹配模型或动作不符合某处的要求(比如:在ip_input或ip6_input处匹配MAC头),那么匹配 模型将不会被匹配。然而,带有not前缀操作符的匹配模型,总是可以和这些数据包相匹配。所以,如果有必要,在有很多地方可以选择的时候,有能力的程序员 可以为写出的规则集选择一个合适的位置。skipto规则在这儿是非常有用的。例如:
# packets from ether_demux or bdg_forward
ipfw add 10 skipto 1000 all from any to any layer2 in
# packets from ip_input
ipfw add 10 skipto 2000 all from any to any not layer2 in
# packets from ip_output
ipfw add 10 skipto 3000 all from any to any not layer2 out
# packets from ether_output_frame
ipfw add 10 skipto 4000 all from any to any layer2 out
(对,在这儿,没有办法区别ether_demux和bdg_forward)
通常,每个关键字或者参数都是作为独立的命令行参数来提供的,前后都不能有空格。关键字是区分大小写的,而参数按照它们本身的含义,可以是,也可以不是区分大小写的。(例如,udi要区分大小写,而hostname没有必要区分大小写)
在ipfw2中,为了提高可读性,可以在逗号后面加上一个空格。当然,你也可以把整个命令(包括参数)当作一个参数来处理(译注:就是加上双引号,作为一个字符串放到某个命令中)。例如,以下几种格式效果相同:
ipfw -q add deny src-ip 10.0.0.0/24,127.0.0.1/8
ipfw -q add deny src-ip 10.0.0.0/24, 127.0.0.1/8 (译注:127前面有个空格)
ipfw的规则格式如下:
[rule_number] [set set_number] [prob match_probability] action
[log [logamount number]] [altq queue] [{tag | untag} number] body
规则的 body 部分用来设定包过滤的信息,具体细节如下:
Layer-2 header fields 第二层头段,什么时候可用
IPv4 and IPv6 Protocol TCP, UDP, ICMP, 协议等
Source and dest. addresses and ports 源和目的。地址和端口
Direction 数据包的流向,见“流量整形”一节
Transmit and receive interface 传输和接收界面,用名字或地址表示
Misc. IP header fields 组合IP头字段,包括版本、服务类型、报文长度、唯一标识、分段标志、生存期
IP options IP选项字段
IPv6 Extension headers IPv6扩展头,包括分片报头、中继选项报头、路由报头、路路由选择报头、IP层安全服务选项、Source routing rthdr0, Mobile IPv6 rthdr2(译注:最后两项不知道怎么翻译)
IPv6 Flow-ID IPv6的流量标识
Misc. TCP header fields 组合tcp头字段,有TCP标志(syn,fin,ack,rst等)、序列号、回复号,窗口等。
TCP options TCP的选项
ICMP types ICMP包
ICMP6 types ICMP6包
User/group ID 本地栈所归属的用户和组
Divert status 是不是源自一个divert接口(例如:natd(8))
需要注意,上面的很多信息中,比如源MAC和IP地址、TCP/UDP端口等,是比较容易伪造的,所以只对这些信息进行过滤不能保证达到预期的效果。
rule_number
每个规则都有规则号,规则号从1-65535。65535是保留的默认规则。ipfw将按规则号的顺序依次检查这些规则。可以有多个规则用同一 个规则号,在这种情况下,将按照它们的添加顺序依次检查或列出。如果一个规则没有指定规则号,内核将自动赋于一个规则号,使它成为默认规则前的最后一个规 则(即小于65535)。自动添加的规则号有个步进值,在不大于65535的情况下,每次添加规则时,都自动加上这一步进值。它默认是100,对应于 net.inet.ip.fw.autoinc_step这个内核变量。
set set_number
每个规则都对应于一个规则集编号,规则集编号取值为:0-31。规则集可以单独地被禁止或启用,所以,它对于规则集的原子操作是 至关重要的。它还可以轻松地删除掉一组规则。如果一个规则没有指定规则集编号,它默认的规则集编号就是0。规则集编号31是个特殊的规则集,它不能被禁 止,也不能用ipfw的flush命令删除,它是默认规则的规则集。规则集编号31可以用ipfw delete set 31命令删除。(译注:这在远程调试排斥式防火墙时特别有用,防止把自己锁在外面。)
prob match_probability
这个语句只用来指定可能性(从0到1之间浮点值)。这在很多随机丢包的应用中,或者(在同dummynet联用的情况下)模拟多路径条件下无序包传递的效果中,是非常有用的。
注意:在检查时,这个条件比其它所有条件都先被检查,包括会有副作用的keep-state或者check-state。 log [logamount number]
当一个包匹配到一个有log关键字的规则时,一条消息会被syslogd记录到一个LOG_SECURITY设备中。记录动作只会在同时满足下 面两个条件时才发生:1、内核选项net.inet.ip.fw.verbose等于1(当编译内核时带着IPFIREWALL_VERBOSE参数时, 这是默认值);2、所记录的包数量还达不到logamount number所指定的数量。如果没有指定logamuont,则记录的包的上限受内核变量net.inet.ip.fw.verbose_limit的限 制。在这两种情况中,logamount取值为零时,则取消记录数量的限制。
当记录数量达到上限时,可以通过重置记录计数器或包计数器的方式再重新开始记录。详细情况请查看resetlog命令。
注意:记录的时间是在所有的包检测条件都完成之后,并且在采取相应的动作(接受、拒绝等)之前。
tag number
当一个包和一个带有tag关键字的规则相匹配时,一个指定的数字标签就被附加到这个包上,数字标签的范围是1-65534。这个标签作为一个内 部的记号(它不会从线路上发送出去),以便后来可以区分这些数据包。这有时候非常有用,比如:提供接口之间相互信任和开启一个基于安全的过滤。一个包可以 同时有多个标签。标签是“顽固的”,意思就是只要标签被应用到某个数据包上,那么它就一直存在,直到被明确地解除为止。这个标签在内核中一直存在,但是一 旦数据包离开内核,标签就会消失。例如:在当把数据包发送到网络或者发送到divert栈时,标签就会消失。
要检查已经存在的标签,可以用tagged规则选项,要删除存在的标签,用untag关键字。
注意:既然标签是在内核空间内一直存在的,所以它们就可以被内核的网络子系统(使用mbuf_tags设备)设置或者是取消,而不仅仅依靠 ipfw的 tag和untag关键字。例如:可以存在一个netgraph(4)节点做流量分析,并且给数据包打上标签,以供后面的防火墙进行检查。
untag number
当一个数据包匹配一个带有untag关键字的规则时,将在这个数据包所附加的标签中查找untag number所指定的标签号,如果找到,则将其删除。对于数据包上的其它标签号,则原封不动。
altq queue(译注:交错队列, Alternate Queuing,参见PF文档)
当一个数据包匹配了一个带有altq关键字的规则时,将被附加上一个指定队列(见altq(4))的ALTQ标识符。注意,ALTQ标签只适用 于从 ipfw“向外出”的数据包,而不是被防火墙拒绝或者准备到divert界面的数据包。还要注意的是,当处理数据包的时候,如果没有足够内存,那么数据包 将不会被打上标签,所以,一个明智的做法,是使你的“默认”ALTQ队列规则记录下这种情况。如果有多个altq规则匹配于同一个数据包,那么只加上第一 条规则的ALTQ类别标签。那样做的效果是:在规则集中靠前的altq队列规则能起到流量整形的作用,而靠后的规则只达到过滤的效果。例如:把还有 ALTQ标签的、check-state和keep-state规则放到规则集的后部,有提供真正的包过滤的作用。
在IPFW使用altq的名字来检查队列之前,你必须用pfctl(8)工具来设置队列。一旦重置ALTQ命令,那么包含队列标识符的规则将过期,需要被重新读取才能生效。过期的队列标识符将会产生错误的分类。
所有的ALTQ系统进程都可以被ipfw的enable altq和disable altq命令启用或关闭。ALTQ流量整形有真实规则动作,永远附加在ALTQ标签之后,和net.inet.ip.fw.one_pass毫无关系。
一个规则可以和下面动作中的一个或多个相关联,如果数据包匹配了规则体,那么这些动作将被执行。
allow | accept | pass | permit 允许匹配规则的数据包通过,规则搜索中止。
check-state
将数据包和动态规则集里的规则进行对照。如果找到,则执行产生这条动态规则的那个规则相关联的动作,如果没找到,则进行下一条规则。 check- state没有规则体。如果在规则集里没有找到check-state,则在第一次出现keep-state或者limit规则时,检查动态规则集。
count 更新和本条规则相匹配的所有数据包的计数器。继续进行下一条搜索。
deny | drop 抛弃和本条规则相匹配的数据包。搜索中止。
divert port 把和本条规则相匹配的数据包转向到divert(4)栈所绑定的端口。搜索中止。
fwd | forward ipaddr | tablearg[,port]
修改匹配数据包的下一跳地址,有可能是一个IP地址,也有可能是一个主机名。当使用tablearg关键字时,下一跳地址可以不是一个明确的地址,它可以从最后的数据包检查表中获取。如果匹配本规则,搜索中止。 (译注:tablearg是FreeBSD7.0的ipfw新增功能,它可以使查表的结果作为规则的一部分来使用,可以用来优化一些规则集,或实现基于策略的路由,在使用时,要配合table命令使用。)
如果指定的ipaddr是一个本地地址,所匹配数据包将被传递到本地机器的相应端口(如果在规则中没有指定端口,则是数据包中的端口号)。如果ipaddr不是一个本地地址,那么端口号(如果指定的话)将被忽略,数据包将会根据本机的路由表被转发到远程地址。
一个fwd规则不会和一个二层的数据包(用ether_input,ether_output或者bridged接受)相匹配。fwd不会修改 数据包中的任何数据,特别是目的地址。所以,除非在目的系统中有相应的规则接受这些数据包,否则,被转发到目的系统的数据包将被拒绝。对于被转发到本地的 数据包,将把本地地址作为它的原始目的地址。这将使netstat(1)看起来有些古怪,但是,对于透明代理服务器来说,却是有用的。
要使用fwd,必须在内核中使用IPFIREWALL_FORWARD选项,并重新编译内核。
nat nat_nr
Pass packet to a nat instance (for network address translation,
address redirect, etc.): see the NETWORK ADDRESS TRANSLATION
(NAT) Section for further information.
将数据包传送到一个nat界面(用来做地址转换或地址重定向等):详细信息,请查看NETWORK ADDRESS TRANSLATION (NAT)一节。
pipe pipe_nr
将数据包传送到一个dummynet(4)的“管道”(用来实现流量整形,延迟等)。详细信息请查看TRAFFIC SHAPER (DUMMYNET) CONFIGURATION一节。如果匹配,则搜索中止。但是,在离开管道的时候,并且内核变量net.inet.ip.fw.one_pass没有设 置,数据包将被送回到防火墙的下一条规则。
queue queue_nr
将数据包传送到一个dummynet(4)的“队列”(用WF2Q+实现流量整形)
reject (Deprecated).
不可到达的同义词。
reset
丢掉和本规则相匹配的数据包,如果是一个TCP数据包,则发送一个重置(RST)消息。搜索中止。
reset6
丢掉和本规则相匹配的数据包,如果是一个TCP数据包,则发送一个重置(RST)消息。搜索中止。
skipto number
跳到指定的规则,忽略掉比指定规则号小的规则。搜索从指定的规则号继续。
tee port
将相匹配的数据包复制一份到divert(4)所绑定的端口。搜索继续。
unreach code
丢弃和本规则相匹配的包,并且尝试发送一条编号为指定编号的不可到达的ICMP消息。编号范围是0-255,或者使用下列别名之一:net, host, protocol, port, needfrag, srcfail, net-unknown,host-unknown, isolated,net-prohib, host-prohib, tosnet, toshost, filter-prohib,host-precedence or precedence-cutoff。搜索中止。
unreach6 code
丢弃和本规则相匹配的包,并且尝试发送一条编号为指定编号的不可到达的ICMPv6消息。编号为0,1,3,4或者是下列别名之一:no-route, admin-prohib, address or port。搜索中止。
netgraph cookie
使用指定的cookie把数据包转向到netgraph。搜索中止。如果稍后数据包众netgraph返回,则受内核变量net.inet.ip.fw.one_pass的影响,或者被接受,或者从下一条规则继续。
ngtee cookie
将数据包复制一份,转发到netgraph,原始的数据包则受net.inet.ip.fw.one_pass的影响,或者被接受,或者从下一条规则继续。查看ng_ipfw(4)可以获取更多关于netgraph和ngtee动作的信息。
规则体包括零个或多个匹配模式(比如指定源和目的的地址或端口,协议选项,进入和流出的界面等。),这些匹配模式必须按顺序对数据包进行识别。 通常,匹配模式之间用一个隐含的“和”操作连接,比如:所有规则中的模型都必须去和数据包进行匹配。单个的模型可以使用“not”操作符作为前缀,用来表 示相反的匹配结果,就象在下例中:
ipfw add 100 allow ip from not 1.2.3.4 to any
特别的,当用来表示二选一的“或”模型时,可以使用一对小括号“()”或一对大括号“{}”括起来的“or”操作符,比如:
ipfw add 100 allow ip from { x or not y or z } to any
只允许有一级小括号。特别提示:大多数shell中,小括号和大括号都有特殊的意义,所以,为了防止被shell误解,强烈建议在小括号和大括号前加上反斜杠“\”。
规则体中,至少要包含一个源地址和目的地址的语句。”any”关键字可以用在多种地方,用来表明源或目标可以是任意的。
规则体的格式如下:
[proto from src to dst] [options]
第一个部分(proto from src to dst)用来和早期版本的FreeBSD相兼容。在新的FreeBSD版本中,所有的匹配模型(包括MAC头,IP协议,地址和端口)都可以在options里指定。
各规则字段的含义如下:
proto: protocol | { protocol or ... }
protocol:[not] protocol-name | protocol-number
一个IP协议可以用序号或名称(在/etc/protocols里可以查看完整的名称列表),或者是下面的关键字之一:
ip4 | ipv4 和IPv4数据包相匹配
ip6 | ipv6 和IPv6数据包相匹配
ip | all 和所有数据包相匹配
ipv6在proto选项中,将被作为内部协议。并且,ipv4在prto选项中是不适用的。
{ protocol or … } 格式的“或”的功能只是方便使用,不提倡这种做法。
src and dst: {addr | {addr or ... }} [[not] ports]
一个地址(或者一个列表,见后),可以使用ports修饰符。
第二种格式(用or连接起来的多个地址)只是方便使用,却不提倡这种做法。
addr: [not] {any | me | me6 | table(number[,value]) | addr-list | addr-set}
any 匹配任意地址
me 匹配系统中所有设置的IP地址。
me6 匹配系统中所有设置的IPv6地址。地址列表是在分析数据包的时候才被列出的。
table(number[,value])
如果一个数据包在表序号为number的表中存在相应的条目,则匹配。如果指定了一个可选的32位无符号数value,则条目序号为value的数据包才被匹配。查看LOOKUP TABLES一节,可以获取更多信息。
addr-list: ip-addr[,addr-list]
ip-addr: 一个主机或者一段地址,可以采用下列格式:
numeric-ip | hostname
匹配单个IPv4地址,可以采用点分四组格式或主机名。当采用主机的时候,在添加规则的时候将被解析为相应的IP。
addr/masklen
匹配于指定的基地址addr(可以是单个IP地址、网络地址或者是主机名)和掩码位masklen。例如:1.2.3.4/25或1.2.3.0/25 匹配于从1.2.3.0到1.2.3.127的IP地址。
addr:mask
匹配于指定的基地址addr(可以是单个地址、网络地址或主机名)和指定的子网掩码mask,用点分四组表示法。例如:1.2.3.4: 255.0.255.0或者1.0.3.0:255.0.255.0匹配于1.*.3.*。只建议在非连续的子网掩码中使用这种格式,对于连续的子网掩码,请使用addr/masklen格式,它的格式更紧凑,也不容易出错。
(译注:这个和反掩码有此类似)
addr-set: addr[/masklen]{list}
list:{num | num-num}[,list]
匹配于基地址是addr(可以是IP地址、网络地址或主机名),并且最后一个数字在{ }列表中的地址。请注意在大括号和数字之间不能有空格(在逗号之后可以有空格)。大括号中的每个元素可以是单个的条目,也可以是一个范围。masklen字段通常用来限制某一段地址的长度,取值范围为24到32,默认取24。
这个对处理在某一个地址段内多个分散的IP地址最有效,因为是用掩码位来匹配,所以节省了时间,也显著地降低了规则的复杂程序。
一个例子:1.2.3.4/24{128,35-55,89}或者1.2.3.0/24{128,35-55,89}表示下列的IP地址:
1.2.3.128, 1.2.3.35到1.2.3.55,1.2.3.89
(译注:由于很多shell中大括号有特殊意义,所以通常情况下,要在大括号前加上反斜杠“\”,表示转义)
addr6-list: ip6-addr[,addr6-list]
ip6-addr: 一个主机或者一段地址,可以采用下列格式:
numeric-ip | hostname
同单一的inet_pton(3)所允许的IP地址或主机名相匹配。主机名在添加规则时被解析为相应的IP。
addr/masklen
匹配所有基地址是addr(可以是inet_pton允许的地址或主机名),并且掩码位数是masklen的地址。
没有提供批量的IPv6地址,因为IPv6地址在前缀之后,是随机的。(译注:不是很懂)
ports: {port | port-port}[,ports]
对于支持端口的协议,可以指定一个可选的单个端口、多个端口、或者是一段端口,这些端口用逗号分开,并且不能有空格。还可以使用可选的not操作符。“-”用来表示包括边界在内的一段范围。
可以使用服务名(见/etc/services)来代替端口号。端口号或端口范围列表最大数目是30,如果想指定更多的端口,可以在options中使用or符号。
如果服务名中存在减号“-”,可以在前面加上反斜杠来转义。(在shell中,如果要使用反斜杠,则要用两次,否则,shell将会把它作为转义字符处理。),例如:
ipfw add count tcp from any ftp\\-date-ftp to any
非零偏移量的分段的数据包(比如不是第一个段),不会匹配于任何带端口号的规则。参见frag一节。