# 機能設計書 42-PF（Packet Filter）

## 概要

本ドキュメントは、OpenBSD由来の高機能パケットフィルタであるPF（Packet Filter）のFreeBSDにおける機能設計について記述する。PFはカーネル内でパケットフィルタリング・NAT・トラフィック正規化を行い、pfctlコマンドを通じて管理される。

### 本機能の処理概要

PFはOpenBSDプロジェクトで開発された高機能パケットフィルタで、FreeBSDに移植されている。ステートフルパケットインスペクション、NAT、リダイレクト、ロードバランシング、テーブル管理、OS指紋検出など多彩な機能を持つ。

**業務上の目的・背景**：企業ネットワークやデータセンターのファイアウォール、ロードバランサとしてFreeBSDを利用する際に、高度なパケットフィルタリングとNAT機能を提供する。pf.confによる宣言的な構成管理が可能で、大規模なルールセットの管理に適している。

**機能の利用シーン**：ファイアウォールルールの設定と管理、NAT/BINATの構成、テーブルを用いた動的IP管理（ブラックリスト等）、ALTQ連携によるQoS制御、pfsyncによるファイアウォール冗長化構成、pflogによるパケットロギングに使用される。

**主要な処理内容**：
1. ルールセットの読み込みと適用（pfctl -f pf.conf）
2. PFの有効化・無効化（pfctl -e / pfctl -d）
3. ルール・NAT・テーブルの表示（pfctl -s rules / pfctl -s nat / pfctl -t table -T show）
4. テーブルへのエントリ追加・削除（pfctl -t table -T add/delete）
5. 統計情報の表示・クリア（pfctl -s info / pfctl -z）
6. 状態テーブルの表示・フラッシュ（pfctl -s states / pfctl -F states）
7. OS指紋データベースの読み込み（pfctl -o）
8. アンカー機能によるルールの階層管理
9. pfsyncによるステート同期（HA構成）
10. pflogによるフィルタリングログの生成

**関連システム・外部連携**：pfil（パケットフィルタフック）経由でネットワークスタックに接続。pfsyncインタフェースで状態テーブルの冗長同期。pflogインタフェースでBPF経由のパケットロギング。ALTQフレームワークとのQoS連携。

**権限による制御**：pfctlの操作には/dev/pfデバイスへのアクセス権限が必要で、事実上root権限が要求される。jail環境でのPF操作は制限される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | （CLIベースのためGUI画面なし） | - | pfctlコマンドおよびpf.conf設定ファイルによる操作 |

## 機能種別

パケットフィルタリング / NAT変換 / ルール管理（CRUD操作） / 状態追跡 / ロードバランシング

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| config_file | string | No | pf.confルールファイルパス | 読み取り可能なファイル |
| command | string | Yes | 操作コマンド（-e/-d/-f/-s/-F/-t/-k等） | 有効なpfctlオプション |
| anchor | string | No | アンカー名 | 有効なアンカーパス文字列 |
| table_name | string | No | テーブル名 | 有効なテーブル名文字列 |
| table_entry | string | No | テーブルエントリ（IPアドレス/CIDR） | 有効なIPアドレス形式 |

### 入力データソース

- pf.conf設定ファイル
- pfctlコマンドライン引数
- /dev/pfデバイス経由のioctl

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| rules | text | フィルタリングルール一覧 |
| states | text | 接続状態テーブル |
| statistics | text | パケット/バイトカウンタ、状態テーブル統計 |
| table_entries | text | テーブル内容一覧 |
| nat_rules | text | NAT/BINATルール一覧 |

### 出力先

- 標準出力（pfctl実行結果）
- カーネルPFルールセット（ルール適用時）
- pflogインタフェース（ログアクション使用時）
- syslog

## 処理フロー

### 処理シーケンス

```
1. pfctlコマンド解析（pfctl.c: main()）
   └─ getoptによるオプション解析、/dev/pfデバイスオープン
2. ルールセット読み込みの場合（pfctl.c: pfctl_rules()）
   └─ pf.confのパース（parse.y）、ルール最適化（pfctl_optimize.c）
3. カーネルへのルール適用
   └─ DIOCADDRULE等のioctlでルールをカーネルPFに投入
4. パケットフィルタリング（pf.c: pf_test()）
   └─ pfilフック経由で呼ばれ、パケットをルールセットと照合
5. 状態テーブル管理
   └─ 新規接続の状態エントリ作成、既存接続のマッチング
6. NAT処理（pf_lb.c）
   └─ NAT/RDR/BINATルールに基づくアドレス変換
```

### フローチャート

```mermaid
flowchart TD
    A[パケット到着] --> B[pfil フック]
    B --> C[pf_test: ルール評価]
    C --> D{状態テーブル照合}
    D -->|既存接続| E[状態に基づく処理]
    D -->|新規接続| F{ルールマッチ}
    F -->|pass| G[状態作成・パケット通過]
    F -->|block| H[パケット破棄]
    F -->|match| I[ログ等のアクション後続行]
    E --> J[パケット通過/破棄]
    G --> K{NAT必要?}
    K -->|Yes| L[アドレス変換]
    K -->|No| M[パケット転送]
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-42-01 | last-match評価 | PFはデフォルトで最後にマッチしたルールが適用される（IPFWと異なる） | ルール評価時（quickキーワードで即時マッチに変更可能） |
| BR-42-02 | 状態追跡デフォルト | passルールはデフォルトで状態追跡を行う（keep state） | passアクション時 |
| BR-42-03 | アンカー階層 | ルールセットをアンカーにより階層的に構造化できる | アンカー定義時 |
| BR-42-04 | テーブル永続性 | テーブルはルールセットリロード後も永続する（persistフラグ時） | テーブル定義時 |

### 計算ロジック

- 状態テーブルのハッシュ計算：送信元/宛先IP・ポート・プロトコルの組み合わせでハッシュ
- ソースハッシュNAT：送信元IPに基づく一貫性のあるNATマッピング

## データベース操作仕様

### 操作別データベース影響一覧

本機能はデータベースを使用しない。カーネル内のルールセット・状態テーブル・NAT状態を直接操作する。

| 操作 | 対象データ構造 | 操作種別 | 概要 |
|-----|-------------|---------|------|
| pfctl -f | pf_ruleset | REPLACE | ルールセット全体を置換 |
| pfctl -t add | pf_table | INSERT | テーブルエントリ追加 |
| pfctl -F states | pf_state | DELETE | 状態テーブルフラッシュ |
| pfctl -s states | pf_state | SELECT | 状態テーブル参照 |

### テーブル別操作詳細

#### pf_ruleset

| 操作 | 項目 | 更新値・取得条件 | 備考 |
|-----|------|-----------------|------|
| REPLACE | rules[], nat[], rdr[] | pf.confパース結果 | 原子的にルールセット全体を更新 |
| SELECT | 全フィールド | アンカー指定可 | pfctl -s rules/natで取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| EACCES | 権限エラー | /dev/pfへのアクセス権限不足 | root権限で実行 |
| ENOENT | デバイス未検出 | pfモジュール未ロード | kldload pf |
| EINVAL | 構文エラー | pf.confの構文不正 | 設定ファイルを修正 |
| ENOMEM | メモリ不足 | 状態テーブルやルール数の上限超過 | 制限値を調整 |

### リトライ仕様

ルールセットの適用は原子的に行われ、失敗した場合は以前のルールセットが維持される。明示的なリトライ機構は不要。

## トランザクション仕様

PFのルールセット更新は原子的に行われる。新しいルールセットが完全にパースされた後に一括で適用され、途中でエラーが発生した場合は以前のルールセットが維持される。pfsyncによる状態同期はイベントドリブンで非同期的に行われる。

## パフォーマンス要件

- 状態テーブルのルックアップはハッシュテーブルによるO(1)
- ルール評価は線形走査だがquickキーワードで早期終了可能
- テーブルルックアップは基数木（radix tree）によるO(log n)
- pfsyncによる状態同期は非同期でネットワーク帯域を消費する

## セキュリティ考慮事項

- /dev/pfデバイスへのアクセス制御がPF管理の要
- pf.confファイルのパーミッション管理が重要
- scrub（パケット正規化）によるフラグメンテーション攻撃対策
- syncookies機能によるSYN Flood攻撃対策
- OS指紋検出によるパッシブフィンガープリンティング

## 備考

- PFはOpenBSD由来であり、FreeBSD版は独自の拡張（Ethernet層フィルタリング、netlinkサポート等）を含む
- pf.conf構文はIPFWと大きく異なり、宣言的な記法を採用
- pflogdデーモンによりpcap形式でパケットログを保存可能

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

PFの内部データ構造（ルール、状態、テーブル）を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | pf.h | `sys/netpfil/pf/pf.h` | pf_rule構造体、pf_state構造体、pf_addr構造体など核心的データ構造を把握する |
| 1-2 | pfvar.h | `sys/net/pfvar.h` | PFのioctl定義（DIOCADDRULE等）とユーザ空間-カーネル間のインタフェース構造体を理解する |

**読解のコツ**: PFのデータ構造はOpenBSD由来のため、FreeBSD固有の拡張部分（VNET対応等）に注意する。pf_ruleの各フィールドはpf.conf構文のキーワードに対応している。

#### Step 2: エントリーポイントを理解する

pfctlコマンドのメイン処理フローを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pfctl.c | `sbin/pfctl/pfctl.c` | main()関数のgetoptループでコマンドオプションの処理フローを理解する。pfctl_enable/pfctl_disable等の関数宣言（71-80行目）でサブコマンドの全体像を把握する |

**主要処理フロー**:
1. **71-80行目**: 関数プロトタイプ宣言。pfctl_enable, pfctl_disable, pfctl_clear_stats, pfctl_flush_rules等
2. main()内のgetoptで-e/-d/-f/-s/-F/-t/-k等のオプションを処理

#### Step 3: ルール解析処理を理解する

pf.confのパースとルール構造体への変換を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | parse.y | `sbin/pfctl/parse.y` | yacc文法定義によるpf.conf構文の解析。ルール、NAT、テーブル等の各セクションの文法を理解する |
| 3-2 | pfctl_parser.c | `sbin/pfctl/pfctl_parser.c` | パーサー補助関数によるアドレス解決、サービス名解決等を理解する |
| 3-3 | pfctl_optimize.c | `sbin/pfctl/pfctl_optimize.c` | ルール最適化（skip step計算等）の処理を理解する |

#### Step 4: カーネルパケット処理エンジンを理解する

カーネル内のパケット評価メインルーチンを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | pf.c | `sys/netpfil/pf/pf.c` | pf_test()関数がパケット処理の中核。ルール評価、状態テーブル操作、NATリダイレクトのメインフロー |
| 4-2 | pf_ioctl.c | `sys/netpfil/pf/pf_ioctl.c` | /dev/pfデバイスのioctlハンドラ。ユーザ空間からのルール追加・削除・参照リクエスト処理 |
| 4-3 | pf_lb.c | `sys/netpfil/pf/pf_lb.c` | NAT/RDR/BINATのロードバランシングとアドレス変換処理 |
| 4-4 | pf_norm.c | `sys/netpfil/pf/pf_norm.c` | パケット正規化（scrub）処理 |

### プログラム呼び出し階層図

```
pfctl（ユーザ空間コマンド）
    │
    ├─ main() [pfctl.c]
    │      ├─ pfctl_enable() / pfctl_disable()
    │      ├─ pfctl_rules() → parse.y パーサー
    │      │      ├─ pfctl_optimize() [pfctl_optimize.c]
    │      │      └─ ioctl(DIOCADDRULE) → カーネル
    │      ├─ pfctl_show_rules/states/nat()
    │      └─ pfctl_table() [pfctl_table.c]
    │
カーネル空間:
    │
    ├─ pf_ioctl.c: pfioctl()
    │      ├─ DIOCADDRULE → ルール追加
    │      ├─ DIOCGETSTATE → 状態取得
    │      └─ DIOCRADDTABLES → テーブル追加
    │
    └─ pf.c: pf_test()  ← pfil フック経由
           ├─ pf_test_rule() → ルール評価
           ├─ pf_state_lookup() → 状態テーブル照合
           ├─ pf_lb.c → NAT/RDR処理
           └─ pf_norm.c → パケット正規化
```

### データフロー図

```
[入力]                    [処理]                         [出力]

pf.conf ─────────▶ parse.y パーサー ─────▶ カーネルルールセット
                    pfctl_rules()             (ioctl DIOCADDRULE)

pfctl -s states ──▶ pfctl.c ─────────────▶ 標準出力（状態一覧）
                    ioctl(DIOCGETSTATE)

ネットワークパケット ─▶ pfil フック ──────────▶ パケット通過/破棄/
                      pf.c: pf_test()           NAT変換/pflogログ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| pfctl.c | `sbin/pfctl/pfctl.c` | ソース | pfctlメインコマンド処理 |
| pfctl.h | `sbin/pfctl/pfctl.h` | ヘッダ | pfctlの共通定義 |
| parse.y | `sbin/pfctl/parse.y` | ソース | pf.confパーサー（yacc文法） |
| pfctl_parser.c | `sbin/pfctl/pfctl_parser.c` | ソース | パーサー補助関数 |
| pfctl_optimize.c | `sbin/pfctl/pfctl_optimize.c` | ソース | ルール最適化処理 |
| pfctl_table.c | `sbin/pfctl/pfctl_table.c` | ソース | テーブル管理コマンド |
| pfctl_osfp.c | `sbin/pfctl/pfctl_osfp.c` | ソース | OS指紋データベース処理 |
| pfctl_radix.c | `sbin/pfctl/pfctl_radix.c` | ソース | 基数木テーブル操作 |
| pfctl_altq.c | `sbin/pfctl/pfctl_altq.c` | ソース | ALTQ QoS連携 |
| pf.c | `sys/netpfil/pf/pf.c` | ソース | カーネルパケット処理エンジン |
| pf.h | `sys/netpfil/pf/pf.h` | ヘッダ | PF内部データ構造定義 |
| pf_ioctl.c | `sys/netpfil/pf/pf_ioctl.c` | ソース | ioctlハンドラ |
| pf_lb.c | `sys/netpfil/pf/pf_lb.c` | ソース | ロードバランシング・NAT処理 |
| pf_norm.c | `sys/netpfil/pf/pf_norm.c` | ソース | パケット正規化 |
| pf_table.c | `sys/netpfil/pf/pf_table.c` | ソース | テーブル管理（カーネル側） |
| pf_if.c | `sys/netpfil/pf/pf_if.c` | ソース | インタフェース管理 |
| pf_osfp.c | `sys/netpfil/pf/pf_osfp.c` | ソース | OS指紋検出 |
| pf_ruleset.c | `sys/netpfil/pf/pf_ruleset.c` | ソース | ルールセット管理 |
| pf_syncookies.c | `sys/netpfil/pf/pf_syncookies.c` | ソース | SYN Cookie処理 |
| pf_nl.c | `sys/netpfil/pf/pf_nl.c` | ソース | Netlinkインタフェース |
| if_pflog.c | `sys/netpfil/pf/if_pflog.c` | ソース | pflogインタフェース |
| if_pfsync.c | `sys/netpfil/pf/if_pfsync.c` | ソース | pfsync状態同期 |
| pfvar.h | `sys/net/pfvar.h` | ヘッダ | PFのioctl・共通構造体定義 |
| pfctl.8 | `sbin/pfctl/pfctl.8` | マニュアル | pfctlのmanページ |
| pf.os | `sbin/pfctl/pf.os` | データ | OS指紋データベース |
