> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/man/man4/pf.4.md).

# pf.4

`pf` — 包过滤器

## 名称

`pf`

## 概要

`device pf options PF_DEFAULT_TO_DROP`

`在 rc.conf(5) 中：pf_enable="YES"`

`在 loader.conf(5) 中：net.pf.states_hashsize net.pf.source_nodes_hashsize net.pf.rule_tag_hashsize net.pf.udpendpoint_hashsize net.pf.default_to_drop`

`在 sysctl.conf(5) 中：net.pf.request_maxcount net.pf.filter_local`

## 描述

包过滤在内核中进行。伪设备 `/dev/pf` 允许用户态进程通过 ioctl(2) 接口控制包过滤器的行为。有启用和禁用过滤器、加载规则集、添加和删除单个规则或状态表条目以及检索统计信息的命令。最常用的功能由 pfctl(8) 涵盖。

诸如加载规则集之类涉及多个 ioctl(2) 调用的操作需要所谓的 *ticket,* 以防止出现多个并发操作。

ioctl(2) 参数结构中引用包数据（如地址和端口）的字段通常应使用网络字节序。

规则和地址表包含在所谓的 *锚点（anchor）* 中。在服务 ioctl(2) 请求时，如果参数结构的锚点字段为空，内核将在操作中使用默认锚点（即主规则集）。锚点按名称指定，可嵌套，组件之间以‘/’字符分隔，类似于文件系统层次的布局。锚点路径的最后一个组件是将在其下执行操作的锚点。

## SYSCTL 变量

以下变量可在 [loader(8)](/man/man8/loader.8.md) 提示符处输入、在 loader.conf(5) 中设置、在 [sysctl.conf(5)](/man/man5/sysctl.conf.5.md) 中设置，或在运行时使用 [sysctl(8)](/man/man8/sysctl.8.md) 更改：

**`net.pf.filter_local`** 通知 `pf` 也对环回输出钩子进行过滤。这通常用于允许重定向规则调整源地址。

**`net.pf.request_maxcount`** 单次 ioctl 调用中的最大项数。

## 加载器可调参数

以下可调参数可在 [loader(8)](/man/man8/loader.8.md) 提示符处输入，或在 loader.conf(5) 中设置：

**`net.pf.states_hashsize`** 存储状态的哈希表大小。应为 2 的幂。默认值为 131072。

**`net.pf.source_nodes_hashsize`** 存储源节点的哈希表大小。应为 2 的幂。默认值为 32768。

**`net.pf.rule_tag_hashsize`** 存储标签的哈希表大小。

**`net.pf.udpendpoint_hashsize`** 存储 UDP 端点映射的哈希表大小。应为 2 的幂。默认值为 32768。

**`net.pf.default_to_drop`** 此值覆盖内核配置文件中的 `options PF_DEFAULT_TO_DROP`。

**`net.pf.filter_local`** 通知 `pf` 也对环回输出钩子进行过滤。这通常用于允许重定向规则调整源地址。

**`net.pf.request_maxcount`** 单次 ioctl 调用中的最大项数。

提供具有匹配名称的只读 [sysctl(8)](/man/man8/sysctl.8.md) 变量以在运行时获取当前值。

## 内核选项

内核配置文件中与 `pf` 操作相关的以下选项：

**`PF_DEFAULT_TO_DROP`** 将默认策略更改为默认丢弃

## IOCTL 接口

`pf` 支持以下 ioctl(2) 命令，可通过 <`net/pfvar.h`> 获得：

```sh
struct pfioc_pooladdr {
	u_int32_t		action;
	u_int32_t		ticket;
	u_int32_t		nr;
	u_int32_t		r_num;
	u_int8_t		r_action;
	u_int8_t		r_last;
	u_int8_t		af;
	char			anchor[MAXPATHLEN];
	struct pf_pooladdr	addr;
};
```

```sh
struct pfioc_rule {
	u_int32_t	action;
	u_int32_t	ticket;
	u_int32_t	pool_ticket;
	u_int32_t	nr;
	char		anchor[MAXPATHLEN];
	char		anchor_call[MAXPATHLEN];
	struct pf_rule	rule;
};
```

```sh
struct pfioc_altq {
	u_int32_t	action;
	u_int32_t	ticket;
	u_int32_t	nr;
	struct pf_altq  altq;
};
```

```sh
struct pfioc_qstats {
	u_int32_t	 ticket;
	u_int32_t	 nr;
	void		*buf;
	int		 nbytes;
	u_int8_t	 scheduler;
};
```

```sh
struct pfioc_ruleset {
	u_int32_t	 nr;
	char		 path[MAXPATHLEN];
	char		 name[PF_ANCHOR_NAME_SIZE];
};
```

```sh
struct pfioc_state {
	struct pfsync_state	state;
};
```

```sh
nvlist pf_state_cmp {
	number			id;
	number			creatorid;
	number			direction;
};
nvlist pf_kill {
	nvlist pf_state_cmp	cmp;
	number			af;
	number			proto;
	nvlist pf_rule_addr	src;
	nvlist pf_rule_addr	dst;
	string			ifname[IFNAMSIZ];
	string			label[PF_RULE_LABEL_SIZE];
};
```

```sh
struct pfioc_if {
	char		 ifname[IFNAMSIZ];
};
```

```sh
struct pf_status {
	u_int64_t	counters[PFRES_MAX];
	u_int64_t	lcounters[LCNT_MAX];
	u_int64_t	fcounters[FCNT_MAX];
	u_int64_t	scounters[SCNT_MAX];
	u_int64_t	pcounters[2][2][3];
	u_int64_t	bcounters[2][2];
	u_int32_t	running;
	u_int32_t	states;
	u_int32_t	src_nodes;
	u_int32_t	since;
	u_int32_t	debug;
	u_int32_t	hostid;
	char		ifname[IFNAMSIZ];
	u_int8_t	pf_chksum[MD5_DIGEST_LENGTH];
};
```

```sh
struct pfioc_natlook {
	struct pf_addr	 saddr;
	struct pf_addr	 daddr;
	struct pf_addr	 rsaddr;
	struct pf_addr	 rdaddr;
	u_int16_t	 sport;
	u_int16_t	 dport;
	u_int16_t	 rsport;
	u_int16_t	 rdport;
	sa_family_t	 af;
	u_int8_t	 proto;
	u_int8_t	 direction;
};
```

```sh
enum	{ PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC,
	  PF_DEBUG_NOISY };
```

```sh
struct pfioc_states_v2 {
	int		ps_len;
	uint64_t	ps_req_version;
	union {
		void			*ps_buf;
		struct pf_state_export	*ps_states;
	};
};
struct pf_state_export {
	uint64_t	 version;
	uint64_t	 id;
	char		 ifname[IFNAMSIZ];
	char		 orig_ifname[IFNAMSIZ];
	struct pf_state_key_export	 key[2];
	struct pf_state_peer_export	 src;
	struct pf_state_peer_export	 dst;
	struct pf_addr	 rt_addr;
	uint32_t	 rule;
	uint32_t	 anchor;
	uint32_t	 nat_rule;
	uint32_t	 creation;
	uint32_t	 expire;
	uint32_t	 spare0;
	uint64_t	 packets[2];
	uint64_t	 bytes[2];
	uint32_t	 creatorid;
	uint32_t	 spare1;
	sa_family_t	 af;
	uint8_t		 proto;
	uint8_t		 direction;
	uint8_t		 log;
	uint8_t		 state_flags_compat;
	uint8_t		 timeout;
	uint8_t		 sync_flags;
	uint8_t		 updates;
	uint16_t	 state_flags;
	uint16_t	 qid;
	uint16_t	 pqid;
	uint16_t	 dnpipe;
	uint16_t	 dnrpipe;
	int32_t		 rtableid;
	uint8_t		 min_ttl;
	uint8_t		 set_tos;
	uint16_t	 max_mss;
	uint8_t		 set_prio[2];
	uint8_t		 rt;
	char		 rt_ifname[IFNAMSIZ];
	uint8_t		 spare[72];
};
```

```sh
enum	{ PF_CHANGE_NONE, PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL,
	  PF_CHANGE_ADD_BEFORE, PF_CHANGE_ADD_AFTER,
	  PF_CHANGE_REMOVE, PF_CHANGE_GET_TICKET };
```

```sh
struct pfioc_tm {
	int		 timeout;
	int		 seconds;
};
```

```sh
struct pfioc_limit {
	int		index;
	unsigned	limit;
};
enum	{ PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS,
	  PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX };
```

```sh
struct pfioc_table {
	struct pfr_table	 pfrio_table;
	void			*pfrio_buffer;
	int			 pfrio_esize;
	int			 pfrio_size;
	int			 pfrio_size2;
	int			 pfrio_nadd;
	int			 pfrio_ndel;
	int			 pfrio_nchange;
	int			 pfrio_flags;
	u_int32_t		 pfrio_ticket;
};
#define pfrio_exists    pfrio_nadd
#define pfrio_nzero     pfrio_nadd
#define pfrio_nmatch    pfrio_nadd
#define pfrio_naddr     pfrio_size2
#define pfrio_setflag   pfrio_size2
#define pfrio_clrflag   pfrio_nadd
```

```sh
struct pfr_table {
	char		pfrt_anchor[MAXPATHLEN];
	char		pfrt_name[PF_TABLE_NAME_SIZE];
	u_int32_t	pfrt_flags;
	u_int8_t	pfrt_fback;
};
```

```sh
struct pfr_tstats {
	struct pfr_table pfrts_t;
	u_int64_t	 pfrts_packets
			     [PFR_DIR_MAX][PFR_OP_TABLE_MAX];
	u_int64_t	 pfrts_bytes
			     [PFR_DIR_MAX][PFR_OP_TABLE_MAX];
	u_int64_t	 pfrts_match;
	u_int64_t	 pfrts_nomatch;
	time_t		 pfrts_tzero;
	int		 pfrts_cnt;
	int		 pfrts_refcnt[PFR_REFCNT_MAX];
};
#define pfrts_name	 pfrts_t.pfrt_name
#define pfrts_flags	 pfrts_t.pfrt_flags
```

```sh
struct pfr_addr {
	union {
		struct in_addr	 _pfra_ip4addr;
		struct in6_addr	 _pfra_ip6addr;
	}		 pfra_u;
	u_int8_t	 pfra_af;
	u_int8_t	 pfra_net;
	u_int8_t	 pfra_not;
	u_int8_t	 pfra_fback;
};
#define pfra_ip4addr    pfra_u._pfra_ip4addr
#define pfra_ip6addr    pfra_u._pfra_ip6addr
```

```sh
struct pfr_astats {
	struct pfr_addr	 pfras_a;
	u_int64_t	 pfras_packets
			     [PFR_DIR_MAX][PFR_OP_ADDR_MAX];
	u_int64_t	 pfras_bytes
			     [PFR_DIR_MAX][PFR_OP_ADDR_MAX];
	time_t		 pfras_tzero;
};
```

```sh
struct pfioc_trans {
	int		 size;	/* 元素数量 */
	int		 esize;	/* 每个元素的字节大小 */
	struct pfioc_trans_e {
		int		rs_num;
		char		anchor[MAXPATHLEN];
		u_int32_t	ticket;
	}		*array;
};
```

**`PF_RULESET_SCRUB`** Scrub（包规范化）规则。 **`PF_RULESET_FILTER`** 过滤规则。 **`PF_RULESET_NAT`** NAT（网络地址转换）规则。 **`PF_RULESET_BINAT`** 双向 NAT 规则。 **`PF_RULESET_RDR`** 重定向规则。 **`PF_RULESET_ALTQ`** ALTQ 调度规则。 **`PF_RULESET_TABLE`** 地址表。

```sh
struct pf_osfp_ioctl {
	struct pf_osfp_entry {
		SLIST_ENTRY(pf_osfp_entry) fp_entry;
		pf_osfp_t		fp_os;
		char			fp_class_nm[PF_OSFP_LEN];
		char			fp_version_nm[PF_OSFP_LEN];
		char			fp_subtype_nm[PF_OSFP_LEN];
	} 			fp_os;
	pf_tcpopts_t		fp_tcpopts;
	u_int16_t		fp_wsize;
	u_int16_t		fp_psize;
	u_int16_t		fp_mss;
	u_int16_t		fp_flags;
	u_int8_t		fp_optcnt;
	u_int8_t		fp_wscale;
	u_int8_t		fp_ttl;
	int			fp_getnum;
};
```

```sh
struct pfioc_src_nodes {
	int	psn_len;
	union {
		caddr_t		psu_buf;
		struct pf_src_node	*psu_src_nodes;
	} psn_u;
#define psn_buf		psn_u.psu_buf
#define psn_src_nodes	psn_u.psu_src_nodes
};
```

```sh
struct pfioc_iface {
	char			 pfiio_name[IFNAMSIZ];
	void			*pfiio_buffer;
	int			 pfiio_esize;
	int			 pfiio_size;
	int			 pfiio_nzero;
	int			 pfiio_flags;
};
```

```sh
struct pfi_kif {
	char				 pfik_name[IFNAMSIZ];
	union {
		RB_ENTRY(pfi_kif)	 pfik_tree;
		LIST_ENTRY(pfi_kif)	 pfik_list;
	};
	u_int64_t			 pfik_packets[2][2][2];
	u_int64_t			 pfik_bytes[2][2][2];
	u_int32_t			 pfik_tzero;
	u_int				 pfik_flags;
	struct ifnet			*pfik_ifp;
	struct ifg_group		*pfik_group;
	u_int				 pfik_rulerefs;
	TAILQ_HEAD(, pfi_dynaddr)	 pfik_dynaddrs;
};
```

```sh
#define PFI_IFLAG_SKIP	0x0100	/* 在接口上跳过过滤 */
```

**`DIOCSTART`** 启动包过滤器。

**`DIOCSTOP`** 停止包过滤器。

**`DIOCSTARTALTQ`** 启动 ALTQ 带宽控制系统（参见 [altq(9)](/man/man9/altq.9.md)）。

**`DIOCSTOPALTQ`** 停止 ALTQ 带宽控制系统。

**`DIOCBEGINADDRS`** `struct pfioc_pooladdr *pp` 清除缓冲地址池，并为后续的 `DIOCADDADDR`、`DIOCADDRULE` 和 `DIOCCHANGERULE` 调用获取 `ticket`。

**`DIOCADDADDR`** `struct pfioc_pooladdr *pp` 将池地址 `addr` 添加到缓冲地址池，用于随后的 `DIOCADDRULE` 或 `DIOCCHANGERULE` 调用。结构中的所有其他成员都被忽略。

**`DIOCADDRULE`** `struct pfioc_rule *pr` 在非活动规则集的末尾添加 `rule`。此调用需要通过前一次 `DIOCXBEGIN` 调用获得的 `ticket`，以及通过前一次 `DIOCBEGINADDRS` 调用获得的 `pool_ticket`。如果需要任何池地址，还必须调用 `DIOCADDADDR`。可选的 `anchor` 名称指示要将规则附加到的锚点。`nr` 和 `action` 被忽略。

**`DIOCADDALTQ`** `struct pfioc_altq *pa` 添加一个 ALTQ 调度规则或队列。

**`DIOCGETRULES`** `struct pfioc_rule *pr` 为后续的 `DIOCGETRULE` 调用获取 `ticket` 和活动规则集中的规则数 `nr`。

**`DIOCGETRULE`** `struct pfioc_rule *pr` 使用通过前一次 `DIOCGETRULES` 调用获得的 `ticket` 按其编号 `nr` 获取 `rule`。如果 `action` 设为 `PF_GET_CLR_CNTR`，则清除所请求规则的按规则统计信息。

**`DIOCGETADDRS`** `struct pfioc_pooladdr *pp` 为后续的 `DIOCGETADDR` 调用获取 `ticket` 以及由 `r_action`、`r_num` 和 `anchor` 指定的规则中的池地址数 `nr`。

**`DIOCGETADDR`** `struct pfioc_pooladdr *pp` 使用通过前一次 `DIOCGETADDRS` 调用获得的 `ticket` 按其编号 `nr` 从由 `r_action`、`r_num` 和 `anchor` 指定的规则中获取池地址 `addr`。

**`DIOCGETALTQS`** `struct pfioc_altq *pa` 为后续的 `DIOCGETALTQ` 调用获取 `ticket` 和活动列表中的队列数 `nr`。

**`DIOCGETALTQ`** `struct pfioc_altq *pa` 使用通过前一次 `DIOCGETALTQS` 调用获得的 `ticket` 按其编号 `nr` 获取队列调度规则 `altq`。

**`DIOCGETQSTATS`** `struct pfioc_qstats *pq` 获取队列的统计信息。此调用填充指向长度为 `nbytes` 的统计信息缓冲区 `buf` 的指针，用于由 `nr` 指定的队列。

**`DIOCGETRULESETS`** `struct pfioc_ruleset *pr` 获取直接附加到 `path` 命名锚点的规则集（即锚点）数 `nr`，用于后续的 `DIOCGETRULESET` 调用。嵌套锚点（由于不直接附加到给定锚点）将不被包括在内。如果 `path` 处给定的父锚点不存在，此 ioctl 返回 Er ENOENT。

**`DIOCGETRULESET`** `struct pfioc_ruleset *pr` 从给定锚点 `path` 按编号 `nr` 获取规则集（即锚点）`name`，其最大数量可从前一次 `DIOCGETRULESETS` 调用获得。如果 `path` 处给定的父锚点不存在，此 ioctl 返回 Er ENOENT；如果 `nr` 传入的索引大于锚点数，返回 Er EBUSY。

**`DIOCADDSTATE`** `struct pfioc_state *ps` 添加状态条目。

**`DIOCGETSTATENV`** `struct pfioc_nv *nv` 从状态表中提取由 `state` nvlist 的 `id` 和 `creatorid` 字段标识的条目。

**`DIOCKILLSTATESNV`** `struct pfioc_nv nv` 从状态表中删除匹配的条目。此 ioctl 在 `killed` 中返回被杀掉的状态数。

**`DIOCCLRSTATESNV`** `struct pfioc_nv nv` 清除所有状态。其工作方式类似于 `DIOCKILLSTATESNV`，但忽略 `pf_kill` nvlist 的 `af`、`proto`、`src` 和 `dst` 字段。

**`DIOCSETSTATUSIF`** `struct pfioc_if *pi` 指定用于累计统计信息的接口。

**`DIOCGETSTATUS`** `struct pf_status *s` 获取内部包过滤器统计信息。

**`DIOCCLRSTATUS`** 清除内部包过滤器统计信息。

**`DIOCNATLOOK`** `struct pfioc_natlook *pnl` 按源和目的地址及端口查找状态表条目。

**`DIOCSETDEBUG`** `u_int32_t *level` 设置调试级别。

**`DIOCGETSTATESV2`** `struct pfioc_states_v2 *ps` 获取状态表条目。

**`DIOCCHANGERULE`** `struct pfioc_rule *pcr` 在由 `rule.action` 指定的规则集中添加或删除 `rule`。要执行的操作类型由 `action` 指示，可为以下任意一种：对于除 `PF_CHANGE_GET_TICKET` 之外的所有操作，`ticket` 必须设为通过 `PF_CHANGE_GET_TICKET` 获得的值。对于除 `PF_CHANGE_REMOVE` 和 `PF_CHANGE_GET_TICKET` 之外的所有操作，`pool_ticket` 必须设为通过 `DIOCBEGINADDRS` 调用获得的值。`anchor` 指示操作应用到的锚点。`nr` 指示应用 `PF_CHANGE_ADD_BEFORE`、`PF_CHANGE_ADD_AFTER` 或 `PF_CHANGE_REMOVE` 操作所针对的规则编号。

**`DIOCCHANGEADDR`** `struct pfioc_pooladdr *pca` 从由 `r_action`、`r_num` 和 `anchor` 指定的规则中添加或删除池地址 `addr`。

**`DIOCSETTIMEOUT`** `struct pfioc_tm *pt` 将 `timeout` 的状态超时设为 `seconds`。旧值将被放入 `seconds`。`timeout` 的可能值请参见 <`net/pfvar.h`> 中的 `PFTM_*` 值。

**`DIOCGETTIMEOUT`** `struct pfioc_tm *pt` 获取 `timeout` 的状态超时。该值将被放入 `seconds` 字段。

**`DIOCCLRRULECTRS`** 清除按规则统计信息。

**`DIOCSETLIMIT`** `struct pfioc_limit *pl` 设置包过滤器使用的内存池的硬限制。

**`DIOCGETLIMIT`** `struct pfioc_limit *pl` 获取由 `index` 指示的内存池的硬 `limit`。

**`DIOCRCLRTABLES`** `struct pfioc_table *io` 清除所有表。所有操作 radix 表的 ioctl 都使用下文所述的同一结构。对于 `DIOCRCLRTABLES`，退出时 `pfrio_ndel` 包含已删除的表数。

**`DIOCRADDTABLES`** `struct pfioc_table *io` 创建一个或多个表。在进入时，`pfrio_buffer` 必须指向一个 `struct pfr_table` 数组，其中至少包含 `pfrio_size` 个元素。`pfrio_esize` 必须为 `struct pfr_table` 的大小。退出时，`pfrio_nadd` 包含实际创建的表数。

**`DIOCRDELTABLES`** `struct pfioc_table *io` 删除一个或多个表。在进入时，`pfrio_buffer` 必须指向一个 `struct pfr_table` 数组，其中至少包含 `pfrio_size` 个元素。`pfrio_esize` 必须为 `struct pfr_table` 的大小。退出时，`pfrio_ndel` 包含实际删除的表数。

**`DIOCRGETTABLES`** `struct pfioc_table *io` 获取所有表的列表。在进入时，`pfrio_buffer[pfrio_size]` 包含一个有效的可写缓冲区用于 `pfr_table` 结构。退出时，`pfrio_size` 包含写入缓冲区的表数。如果缓冲区太小，内核不存储任何内容，仅返回所需的缓冲区大小，不返回错误。

**`DIOCRGETTSTATS`** `struct pfioc_table *io` 此调用类似于 `DIOCRGETTABLES`，但用于获取 `pfr_tstats` 结构的数组。

**`DIOCRCLRTSTATS`** `struct pfioc_table *io` 清除一个或多个表的统计信息。在进入时，`pfrio_buffer` 必须指向一个 `struct pfr_table` 数组，其中至少包含 `pfrio_size` 个元素。`pfrio_esize` 必须为 `struct pfr_table` 的大小。退出时，`pfrio_nzero` 包含实际清除的表数。

**`DIOCRCLRADDRS`** `struct pfioc_table *io` 清除表中的所有地址。在进入时，`pfrio_table` 包含要清除的表。退出时，`pfrio_ndel` 包含已删除的地址数。

**`DIOCRADDADDRS`** `struct pfioc_table *io` 向表中添加一个或多个地址。在进入时，`pfrio_table` 包含表 ID，`pfrio_buffer` 必须指向一个 `struct pfr_addr` 数组，其中至少包含 `pfrio_size` 个要添加到表中的元素。`pfrio_esize` 必须为 `struct pfr_addr` 的大小。退出时，`pfrio_nadd` 包含实际添加的地址数。

**`DIOCRDELADDRS`** `struct pfioc_table *io` 从表中删除一个或多个地址。在进入时，`pfrio_table` 包含表 ID，`pfrio_buffer` 必须指向一个 `struct pfr_addr` 数组，其中至少包含 `pfrio_size` 个要从表中删除的元素。`pfrio_esize` 必须为 `struct pfr_addr` 的大小。退出时，`pfrio_ndel` 包含实际删除的地址数。

**`DIOCRSETADDRS`** `struct pfioc_table *io` 用新的地址列表替换表的内容。这是最复杂的命令，使用了所有结构成员。在进入时，`pfrio_table` 包含表 ID，`pfrio_buffer` 必须指向一个 `struct pfr_addr` 数组，其中至少包含 `pfrio_size` 个元素，这些元素成为表的新内容。`pfrio_esize` 必须为 `struct pfr_addr` 的大小。此外，如果 `pfrio_size2` 不为零，`pfrio_buffer[pfrio_size..pfrio_size2]` 必须是可写缓冲区，内核可以将替换操作期间已删除的地址复制到其中。退出时，`pfrio_ndel`、`pfrio_nadd` 和 `pfrio_nchange` 包含内核删除、添加和更改的地址数。如果进入时设置了 `pfrio_size2`，`pfrio_size2` 将指向所用缓冲区的大小，与 `DIOCRGETADDRS` 完全相同。

**`DIOCRGETADDRS`** `struct pfioc_table *io` 获取表的所有地址。在进入时，`pfrio_table` 包含表 ID，`pfrio_buffer[pfrio_size]` 包含一个有效的可写缓冲区用于 `pfr_addr` 结构。退出时，`pfrio_size` 包含写入缓冲区的地址数。如果缓冲区太小，内核不存储任何内容，仅返回所需的缓冲区大小，不返回错误。

**`DIOCRGETASTATS`** `struct pfioc_table *io` 此调用类似于 `DIOCRGETADDRS`，但用于获取 `pfr_astats` 结构的数组。

**`DIOCRCLRASTATS`** `struct pfioc_table *io` 清除一个或多个地址的统计信息。在进入时，`pfrio_table` 包含表 ID，`pfrio_buffer` 必须指向一个 `struct pfr_addr` 数组，其中至少包含 `pfrio_size` 个要从表中清除的元素。`pfrio_esize` 必须为 `struct pfr_addr` 的大小。退出时，`pfrio_nzero` 包含实际清除的地址数。

**`DIOCRTSTADDRS`** `struct pfioc_table *io` 测试给定地址是否与表匹配。在进入时，`pfrio_table` 包含表 ID，`pfrio_buffer` 必须指向一个 `struct pfr_addr` 数组，其中至少包含 `pfrio_size` 个元素，每个元素都将在表中测试是否匹配。`pfrio_esize` 必须为 `struct pfr_addr` 的大小。退出时，内核通过适当设置 `pfra_fback` 成员来更新 `pfr_addr` 数组。

**`DIOCRSETTFLAGS`** `struct pfioc_table *io` 更改表的 `PFR_TFLAG_CONST` 或 `PFR_TFLAG_PERSIST` 标志。在进入时，`pfrio_buffer` 必须指向一个 `struct pfr_table` 数组，其中至少包含 `pfrio_size` 个元素。`pfrio_esize` 必须为 `struct pfr_table` 的大小。`pfrio_setflag` 必须包含要添加的标志，而 `pfrio_clrflag` 必须包含要删除的标志。退出时，`pfrio_nchange` 和 `pfrio_ndel` 包含内核更改或删除的表数。是的，如果删除未引用表的 `PFR_TFLAG_PERSIST` 标志，则可以删除该表。

**`DIOCRINADEFINE`** `struct pfioc_table *io` 在非活动集中定义一个表。在进入时，`pfrio_table` 包含表 ID，`pfrio_buffer[pfrio_size]` 包含要放入表中的 `pfr_addr` 结构数组。还必须向 `pfrio_ticket` 提供有效的 ticket。退出时，如果该表已在非活动列表中定义，`pfrio_nadd` 包含 0；如果已创建新表，包含 1。`pfrio_naddr` 包含实际放入表中的地址数。

**`DIOCXBEGIN`** `struct pfioc_trans *io` 清除 `pfioc_trans_e` 数组中指定的所有非活动规则集。对于每个规则集，返回一个 ticket 用于后续的“添加规则”ioctl，以及 `DIOCXCOMMIT` 和 `DIOCXROLLBACK` 调用。由 `rs_num` 标识的规则集类型包括以下几种：

**`DIOCXCOMMIT`** `struct pfioc_trans *io` 原子地将一组非活动规则集切换为活动规则集。此调用实现为标准的两阶段提交，要么所有规则集都失败，要么完全成功。所有 ticket 都必须有效。如果另一个进程正在并发更新某些相同的规则集，此 ioctl 返回 Er EBUSY。

**`DIOCXROLLBACK`** `struct pfioc_trans *io` 通过撤销自上次 `DIOCXBEGIN` 以来在非活动规则集上发生的所有更改来清理内核。`DIOCXROLLBACK` 将静默忽略 ticket 无效的规则集。

**`DIOCSETHOSTID`** `u_int32_t *hostid` 设置主机 ID，[pfsync(4)](/man/man4/pfsync.4.md) 使用它来标识哪个主机创建了状态表条目。

**`DIOCOSFPFLUSH`** 刷新被动 OS 指纹表。

**`DIOCOSFPADD`** `struct pf_osfp_ioctl *io` 向表中添加被动 OS 指纹。将 `fp_os.fp_os` 设为打包的指纹，`fp_os.fp_class_nm` 设为类名（Linux、Windows 等），`fp_os.fp_version_nm` 设为版本名（NT、95、98），`fp_os.fp_subtype_nm` 设为子类型或补丁级别名。成员 `fp_mss`、`fp_wsize`、`fp_psize`、`fp_ttl`、`fp_optcnt` 和 `fp_wscale` 分别设为 TCP SYN 包的 TCP MSS、TCP 窗口大小、IP 长度、IP TTL、TCP 选项数和 TCP 窗口缩放常数。`fp_flags` 成员根据 <`net/pfvar.h`> 包含文件的 `PF_OSFP_*` 定义填充。`fp_tcpopts` 成员包含打包的 TCP 选项。每个选项在打包值中使用 `PF_OSFP_TCPOPT_BITS` 位。选项包括 `PF_OSFP_TCPOPT_NOP`、`PF_OSFP_TCPOPT_SACK`、`PF_OSFP_TCPOPT_WSCALE`、`PF_OSFP_TCPOPT_MSS` 或 `PF_OSFP_TCPOPT_TS` 中的任意一个。`fp_getnum` 成员不用于此 ioctl。结构的空闲空间必须清零才能正确操作；在填充和发送到内核之前，将整个结构 memset(3) 为零。

**`DIOCOSFPGET`** `struct pf_osfp_ioctl *io` 从内核的指纹列表中获取被动 OS 指纹编号 `fp_getnum`。结构的其余成员将被填充返回。通过重复递增 `fp_getnum` 编号直到 ioctl 返回 Er EBUSY 来获取整个列表。

**`DIOCGETSRCNODES`** `struct pfioc_src_nodes *psn` 获取由粘性地址和源跟踪保留的源节点列表。必须先用 `psn_len` 设为 0 调用此 ioctl。如果 ioctl 无错误返回，`psn_len` 将被设为保存表中所有 `pf_src_node` 结构所需缓冲区的大小。然后应分配此大小的缓冲区，并将指向此缓冲区的指针放入 `psn_buf`。然后必须再次调用此 ioctl 以用实际源节点数据填充此缓冲区。该调用之后，`psn_len` 将被设为实际使用的缓冲区长度。

**`DIOCCLRSRCNODES`** 清除源跟踪节点树。

**`DIOCIGETIFACES`** `struct pfioc_iface *io` 获取 `pf` 已知的接口和接口组列表。所有操作接口的 ioctl 都使用下文所述的同一结构：如果不为空，`pfiio_name` 可用于将搜索限制为特定接口或组。`pfiio_buffer[pfiio_size]` 是用户提供的用于返回数据的缓冲区。在进入时，`pfiio_size` 包含可放入缓冲区的 `pfi_kif` 条目数。内核将用其要返回的实际条目数替换此值。`pfiio_esize` 应设为 `sizeof(struct pfi_kif)`。数据以下文所述的 `pfi_kif` 结构返回：

**`DIOCSETIFFLAG`** `struct pfioc_iface *io` 设置 `pf` 内部接口描述的用户可设置标志（如上所述）。过滤过程与 `DIOCIGETIFACES` 相同。

**`DIOCCLRIFFLAG`** `struct pfioc_iface *io` 工作方式类似于上面的 `DIOCSETIFFLAG`，但清除标志。

**`DIOCKILLSRCNODES`** `struct pfioc_iface *io` 显式删除源跟踪节点。

## 文件

**`/dev/pf`** 包过滤设备。

## 实例

以下示例演示如何使用 `DIOCNATLOOK` 命令查找 NAT 连接的内部主机/端口：

```sh
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/pfvar.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
u_int32_t
read_address(const char *s)
{
	int a, b, c, d;
	sscanf(s, "%i.%i.%i.%i", &a, &b, &c, &d);
	return htonl(a << 24 | b << 16 | c << 8 | d);
}
void
print_address(u_int32_t a)
{
	a = ntohl(a);
	printf("%d.%d.%d.%d", a >> 24 & 255, a >> 16 & 255,
	    a >> 8 & 255, a & 255);
}
int
main(int argc, char *argv[])
{
	struct pfioc_natlook nl;
	int dev;
	if (argc != 5) {
		printf("%s <gwy addr> <gwy port> <ext addr> <ext port>n",
		    argv[0]);
		return 1;
	}
	dev = open("/dev/pf", O_RDWR);
	if (dev == -1)
		err(1, "open(\"/dev/pf\") failed");
	memset(&nl, 0, sizeof(struct pfioc_natlook));
	nl.saddr.v4.s_addr	= read_address(argv[1]);
	nl.sport		= htons(atoi(argv[2]));
	nl.daddr.v4.s_addr	= read_address(argv[3]);
	nl.dport		= htons(atoi(argv[4]));
	nl.af			= AF_INET;
	nl.proto		= IPPROTO_TCP;
	nl.direction		= PF_IN;
	if (ioctl(dev, DIOCNATLOOK, &nl))
		err(1, "DIOCNATLOOK");
	printf("internal host ");
	print_address(nl.rsaddr.v4.s_addr);
	printf(":%un", ntohs(nl.rsport));
	return 0;
}
```

## 参见

ioctl(2), altq(4), if\_bridge(4), [pflog(4)](/man/man4/pflog.4.md), [pfsync(4)](/man/man4/pfsync.4.md), pfctl(8), [altq(9)](/man/man9/altq.9.md)

## 历史

`pf` 包过滤机制首次出现于 OpenBSD 3.0，然后是 FreeBSD 5.2。

此实现派生自 OpenBSD 4.5。许多单独的功能、改进、错误修复和安全修复已从更高版本的 OpenBSD 移植。已对其进行了大量修改，使其能够在多线程 FreeBSD 内核中运行并在多个 CPU 上扩展性能。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/man/man4/pf.4.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
