# 15.3 IPFilter（IPF）

IPFilter（IPF）是一款早期跨平台开源防火墙，支持包括 FreeBSD 在内的多种操作系统。IPF 提供包过滤和网络地址转换（NAT）功能，作者为 Darren Reed。

> **警告**
>
> 本文所述内容建议读者在测试环境中验证后再用于生产部署。

## 启用 IPF 防火墙

IPF 防火墙的启用需要准备配置文件并启动相关服务。配置文件定义防火墙规则，服务进程负责加载和执行这些规则，具体步骤如下。

如需启用 IPF，可执行以下命令：

```sh
# 复制示例文件作为默认配置规则集文件，否则 ipfilter 启动后会没有规则。示例文件自带的规则不影响使用
# cp /usr/share/examples/ipfilter/ipf.conf.sample /etc/ipf.rules
```

* 启动 IPF 防火墙：

```sh
# service ipfilter enable   # 设置 ipfilter 在系统启动时自动启动
# service ipfilter start    # 启动 ipfilter 服务
```

* 启动 ipnat：

ipnat 是 IPF 的组成部分，专门用于处理网络地址转换（NAT）规则的维护。NAT 可以实现内网 IP 与公网 IP 的转换，以及端口转发等功能。

```sh
# cp /usr/share/examples/ipfilter/ipnat.conf.sample /etc/ipnat.rules   # 复制示例文件作为默认配置规则集，否则 ipnat 无法启动
# service ipnat enable   # 设置 ipnat 在系统启动时自动启动
# service ipnat start    # 启动 ipnat 服务
```

> **注意**
>
> ipfilter 服务重启后，ipnat 也需要重启。

IPF 的管理命令主要有 ipf、ipfstat 和 ipnat，常用操作示例如下：

### 相关文件结构

```sh
/
├── etc
│   ├── ipf.rules                     # IPFilter 防火墙规则文件
│   └── ipnat.rules                    # IPNAT 转发规则文件
└── usr
    └── share
        └── examples
            └── ipfilter
                ├── ipf.conf.sample   # IPFilter 示例配置
                └── ipnat.conf.sample # IPNAT 示例配置
```

### IPF 命令操作

IPF 提供了一系列命令用于管理防火墙的运行状态和规则。

1. **启动 IPF 防火墙**

```sh
ipf -E
```

相当于执行 `service ipfilter start`。

2. **停止 IPF**

```sh
ipf -D
```

相当于执行 `service ipfilter stop`。

3. **加载规则集文件**

```sh
ipf -f /etc/ipf.rules
```

从指定文件加载防火墙规则。

4. **查看所有规则**

```sh
ipfstat
```

显示当前加载的所有规则。

5. **查看详细规则信息**

```sh
ipfstat -iohn
```

参数说明：

* `i`：显示输入规则
* `o`：显示输出规则
* `h`：显示通过该规则的流量
* `n`：显示规则编号

6. **显示状态表**

```sh
ipfstat -t
```

实时监控 IPF 流量，按 `Q` 退出。

7. **清理已加载规则**

```sh
ipf -Fa
```

删除所有已加载的规则。

### NAT 操作命令

NAT 规则的管理需要使用 ipnat 命令，常用操作如下。

1. **加载 NAT 规则**

```sh
ipnat -f /etc/ipnat.rules
```

加载 NAT 规则文件。

2. **汇总 NAT 状态**

```sh
ipnat -s
```

显示 NAT 运行状态的汇总。

3. **列表显示 NAT 规则**

```sh
ipnat -lh
```

列出 NAT 规则，`h` 表示显示通过该规则的流量。

4. **清理 NAT 规则**

```sh
ipnat -CF
```

删除已加载的 NAT 规则。

### IPF 规则说明

以下是一些常用的 IPF 规则示例，可根据实际需求调整。注意：示例中使用的 IP 地址 192.168.1.184 为演示用途，请根据实际情况修改。

1. **拒绝所有访问**

```ini
block all
```

阻止所有流量。

说明：IPF 默认是明示禁止的防火墙，需要明确写规则以阻止访问。

2. **拒绝所有进入流量**

```ini
block in all
```

阻止所有入站流量。

参数说明：

* `block`：拒绝
* `pass`：允许
* `in`：入站（`out` 表示出站）
* `all`：等价于执行 `from any to any`

地址表示：

* IP 或网段：如 `192.168.1.100` 或 `192.168.1.0/24`
* `any`：任意地址

### 阻止与放行流量

流量控制是防火墙的核心功能之一，以下是相关规则示例。

1. **拒绝所有外部流量**

```ini
block out all
```

阻止所有从本机发出的外部流量。

2. **放行回环接口的输入流量**

```ini
pass in quick on lo0 all
```

允许本机回环接口（lo0）的所有输入流量。

说明：`quick` 表示匹配规则后立即停止，不再匹配后续规则。

3. **放行回环接口输出流量**

```ini
pass out quick on lo0 all
```

允许本机回环接口的所有输出流量。

### TCP/UDP 访问控制

针对 TCP 和 UDP 协议的访问控制规则如下。

1. **允许访问服务器的特定端口**

```ini
pass in quick proto tcp from any to 192.168.1.184 port = { 22,80,443,4200,10000 }
```

允许外部设备通过 TCP 访问服务器指定端口（SSH、HTTP、HTTPS 等）。

2. **允许本机访问外部设备的特定端口**

```ini
pass out quick proto tcp from 192.168.1.184 port = { 22,80,443,4200,10000 } to any
```

允许服务器访问这些外部端口。

3. **允许本机访问 HTTP/HTTPS 并保持状态**

```ini
pass out quick proto tcp from 192.168.1.184 to any port = { 80,443 } keep state
```

访问外部 HTTP/HTTPS 服务，并跟踪连接状态。

4. **DNS/UDP 访问**

```ini
pass out quick proto udp from any to any port = 53 keep state
pass out quick proto udp from any to any port = 67 keep state
```

允许服务器访问 DNS（53 端口）和 DHCP（67 端口）服务器，并保持状态。

### ICMP 流量控制

ICMP 协议用于网络诊断和错误报告，相关规则如下。

1. **本机发送 ping 请求**

```ini
pass out quick proto icmp from 192.168.1.184 to any icmp-type 8 keep state
```

允许本机向外发送 ICMP 回显请求（ping）。

2. **外部设备 ping 本机**

```ini
pass in quick proto icmp from any to 192.168.1.184 icmp-type 8 keep state
```

允许外部设备 ping 本机。

3. **本机 ICMP 回显应答**

```ini
pass out quick proto icmp from 192.168.1.184 to any icmp-type 0
```

允许本机响应 ping 请求。

### Traceroute 流量

Traceroute 用于网络路径诊断，需要开放特定的 ICMP 和 UDP 端口。

1. **通过 ICMP traceroute**

```ini
pass out quick proto icmp from 192.168.1.184 to any icmp-type 0
```

允许 ICMP 回显应答用于 traceroute。

2. **通过 UDP traceroute**

```ini
pass out quick proto udp from 192.168.1.184 to any port 33434 <> 34500 keep state
```

放行从 192.168.1.184 发出的 UDP 流量，源端口范围 33434-34500，快速处理并保持状态。

### 端口转发规则（NAT）

网络地址转换（NAT）可用于端口转发，以下是相关规则示例。

1. **本机端口映射 8080 → 80**

```ini
rdr on em0 proto tcp from any to 192.168.1.184 port 8080 -> 192.168.1.184 port 80
```

将本机 em0 网卡的 8080 端口流量转发到本机 80 端口。

### 加载规则命令

规则编辑完成后，需要执行以下命令使其生效。

1. **清理并加载防火墙规则**

```sh
ipf -Fa -f /etc/ipf.rules
```

参数说明：

* `-Fa`：清除所有已加载规则
* `-f`：加载指定规则文件

2. **清理并加载 NAT 规则**

```sh
ipnat -CF -f /etc/ipnat.rules
```

参数说明：

* `-CF`：清除 NAT 规则
* `-f`：加载指定 NAT 规则文件

## 课后习题

1. 查找 IPFilter 的历史源码存档，选取其早期版本（如 4.x 系列），为其补全在现代 FreeBSD 上的构建脚本，并对比其架构与 PF 或 IPFW 的差异。
2. 选取 IPFilter 的 NAT 规则实现机制，重构一个最小化的端口转发规则处理模块。
3. 修改 IPFilter 的默认策略（如禁用 quick 选项或改变默认 block 行为），验证其行为变化。
