# 15.4 ipfirewall（IPFW）

IPFIREWALL（IPFW，IP 防火墙）是由 FreeBSD 项目开发和维护的防火墙软件，作为可加载模块存在于基本系统内核（GENERIC）中。作为 FreeBSD 原生防火墙，IPFW 基于规则编号的优先级机制，为网络访问控制提供了灵活而精确的管理框架。规则编号越小，优先级越高，可覆盖编号较大的规则。

> **警告**
>
> IPFW 默认包含一条规则，规则号为 `65535`，不可删除：该规则会阻断所有未匹配的流量。因此，在防火墙配置完成之前，请勿启动 IPFW，以免被阻断在防火墙之外。

## 服务项

配置 IPFW 服务首先需要启用系统防火墙，然后进行启动和状态检查等操作，具体步骤如下。

* 启用系统防火墙设置：

```sh
# sysrc firewall_enable="YES"
```

> **技巧**
>
> 参见：routing\[EB/OL]. \[2026-03-26]. <https://github.com/freebsd/freebsd-src/blob/main/libexec/rc/rc.d/routing#L387>

可以使用以下命令设置 IPFW 防火墙在系统启动时自动启动：

```sh
# service ipfw enable # 须重启后生效
```

两种方法功能等价：`sysrc firewall_enable="YES"` 直接修改 rc.conf 配置，`service ipfw enable` 通过服务管理工具设置开机自启，均需重启后生效。

* 启动 IPFW 防火墙服务：

```sh
# service ipfw start

Firewall rules loaded.
Firewall logging enabled.
ifconfig: interface ipfw0 already exists
Firewall logging pseudo-interface (ipfw0) created.
```

* 查看 IPFW 防火墙的当前状态：

```sh
# service ipfw status

ipfw is enabled
```

## 相关文件结构

```sh
/
├── etc
│   └── ipfw.rules
├── usr
│   └── local
│       └── etc
│           └── ipfw.rules
└── boot
    └── loader.conf
```

## 其他 RC 配置

除了基本的服务启用外，还可通过以下配置项进一步调整 IPFW 的行为。

1. **设置防火墙策略类型**

```sh
sysrc firewall_type="open"
```

作用：允许所有流量通过。

说明：

* 如果不设置 `open`，FreeBSD 默认的防火墙规则是 `65535: deny ip from any to any`，即拒绝所有 IP 流量。
* 功能等同于执行：

```sh
sysctl net.inet.ip.fw.default_to_accept=1
```

* 使用 `open` 后，防火墙仍启用，但默认不阻塞流量。firewall\_type 还有其他可选值，如 `client`（适用于客户端）、`workstation`（适用于工作站）等，具体可参考 rc.conf(5) 手册\[EB/OL]. \[2026-03-26]. <https://man.freebsd.org/cgi/man.cgi?rc.conf(5)>

2. **指定防火墙规则文件**

```sh
sysrc firewall_script="/usr/local/etc/ipfw.rules"
```

作用：设置 IPFW 防火墙规则文件路径。

说明：系统启动时会读取该文件并加载其中的防火墙规则。

3. **启用防火墙日志**

```sh
sysrc firewall_logging="YES"
```

作用：开启 IPFW 日志功能，使防火墙能够打印日志信息。

4. **指定日志输出设备**

```sh
sysrc firewall_logif="YES"
```

作用：将防火墙日志输出到 `ipfw0` 伪接口设备。

便于使用 `tcpdump` 或 `ngrep` 等工具监控防火墙日志流量。

***

配置完成后，可以通过以下命令查看当前生效的规则。

按上述配置的规则如下：

```sh
# ipfw list  # 显示当前 IPFW 防火墙规则列表
00100 allow ip from any to any via lo0
00200 deny ip from any to 127.0.0.0/8
00300 deny ip from 127.0.0.0/8 to any
00400 deny ip from any to ::1
00500 deny ip from ::1 to any
00600 allow ipv6-icmp from :: to ff02::/16
00700 allow ipv6-icmp from fe80::/10 to fe80::/10
00800 allow ipv6-icmp from fe80::/10 to ff02::/16
00900 allow ipv6-icmp from any to any icmp6types 1
01000 allow ipv6-icmp from any to any icmp6types 2,135,136
65000 allow ip from any to any   # 注意此处，允许所有其他 IP 流量
65535 deny ip from any to any    # 拒绝所有未匹配流量
```

## 编辑 `/usr/local/etc/ipfw.rules` 文件

如需自定义 IPFW 规则，可编辑规则文件。

以下示例用于指定防火墙规则：

```ini
cmd="ipfw -q add"  # 定义一个变量 cmd，用于简化后续添加规则的命令
ipfw -q -f flush  # -q 选项表示静默模式，避免输出冗余信息

# loopback
$cmd 10 allow all from any to any via lo0  # 允许所有通过回环接口 lo0 的流量
$cmd 20 deny all from any to 127.0.0.0/8  # 拒绝任何来自外部的访问回环地址范围（127.0.0.0/8）的流量，防止伪造的回环地址通信
$cmd 30 deny all from 127.0.0.0/8 to any  # 拒绝来自回环地址范围的任何流量离开本地主机，进一步确保回环地址的安全性
$cmd 40 deny tcp from any to any frag  # 拒绝所有分片的 TCP 数据包，有助于防范某些类型的攻击

# statefull
$cmd 50 check-state  # 检查现有会话状态，允许已建立的连接继续通信
$cmd 60 allow tcp from any to any established  # 允许所有已建立的 TCP 连接的数据包通过
$cmd 70 allow all from any to any out keep-state  # 允许所有向外的流量，并对其创建状态记录，以便响应流量能够自动通过
$cmd 80 allow icmp from any to any  # 允许所有 ICMP 数据包，支持网络诊断和错误报告功能

# open port for ssh
$cmd 110 allow tcp from any to any 22 out  # 允许所有向外的 SSH 流量
$cmd 120 allow tcp from any to any 22 in  # 确保可以通过 SSH 访问该主机

# open port for samba
$cmd 130 allow tcp from any to any 139 out  # 允许 TCP 端口 139 的出站流量
$cmd 140 allow tcp from any to any 139 in  # 允许 TCP 端口 139 的入站流量
$cmd 150 allow tcp from any to any 445 out  # 允许 TCP 端口 445 的出站流量
$cmd 160 allow tcp from any to any 445 in  # 允许 TCP 端口 445 的入站流量
$cmd 170 allow udp from any to any 137 out  # 允许 UDP 端口 137 的出站流量
$cmd 180 allow udp from any to any 137 in  # 允许 UDP 端口 137 的入站流量
$cmd 190 allow udp from any to any 138 out  # 允许 UDP 端口 138 的出站流量
$cmd 200 allow udp from any to any 138 in  # 允许 UDP 端口 138 的入站流量


# deny and log everything
$cmd 500 deny log all from any to any  # 拒绝并记录所有流量的规则 500
```

每条规则的基本结构如下，可据此编写自定义规则：

```sh
# ipfw add [规则编号] ① [动作] [协议] from [源地址] to [目标地址] [其他条件]
```

* ① 按优先级排序，数字越小优先级越高，可覆盖数字较大的规则。

示例：添加规则 500，用于拒绝并记录所有流量：

写入文本：

```ini
$cmd 500 deny log all from any to any
```

或者执行命令：

```sh
# ipfw add 500 deny log all from any to any
```

> **注意**
>
> `ipfw` 命令为一次性生效，执行时即时生效，但不会永久保存规则。需要将规则写入指定文件以保持持久化。

除了编辑规则文件外，还可通过以下命令调整 IPFW 的默认行为。

* 设置 IPFW 默认策略为允许所有流量：

```sh
# echo 'net.inet.ip.fw.default_to_accept="1"' >> /boot/loader.conf
```

* 显示当前 IPFW 防火墙规则列表：

```sh
# ipfw list
00100 allow ip from any to any via lo0
00200 deny ip from any to 127.0.0.0/8
00300 deny ip from 127.0.0.0/8 to any
00400 deny ip from any to ::1
00500 deny ip from ::1 to any
00600 allow ipv6-icmp from :: to ff02::/16
00700 allow ipv6-icmp from fe80::/10 to fe80::/10
00800 allow ipv6-icmp from fe80::/10 to ff02::/16
00900 allow ipv6-icmp from any to any icmp6types 1
01000 allow ipv6-icmp from any to any icmp6types 2,135,136
65535 allow ip from any to any # 默认最后一条规则，放行任何来源到任何目标的所有 IP 流量
```

## 课后习题

1. 查找 FreeBSD 源码中 IPFW 的规则编号优先匹配机制，重构一个最小化的规则匹配引擎。
2. 选取 IPFW 的状态跟踪（keep-state）机制，为其设计并实现一个简化版本，验证其功能。
3. 修改 IPFW 的默认策略（如将 `default_to_accept` 从 1 改为 0 或调整规则 65535 的行为），验证其行为变化。
