# 機能設計書 102-PPPoE

## 概要

本ドキュメントは、FreeBSDのPPP over Ethernet (PPPoE) 接続機能を提供するppoedデーモンの機能設計を記述する。ppoedはnetgraphフレームワークを利用してPPPoEサーバ機能を実現するデーモンプロセスである。

### 本機能の処理概要

ppoedは、イーサネットインタフェース上でPPPoEプロトコルのサーバ側として動作し、クライアントからのPPPoEセッション要求をリッスンし、接続が確立された際にppp(8)プロセスを起動して実際のPPP通信を処理するデーモンである。

**業務上の目的・背景**：PPPoEはDSL（デジタル加入者線）接続等で広く使用されるプロトコルであり、イーサネットフレーム上にPPPプロトコルをカプセル化する。ppoedは、FreeBSDシステムをPPPoEアクセスコンセントレータ（サーバ）として機能させるために必要なデーモンである。ISPやネットワークサービスプロバイダがFreeBSDベースのアクセスサーバを構築する際に使用される。

**機能の利用シーン**：DSLアクセスサーバ、PPPoEアクセスコンセントレータの構築時に使用される。イーサネットインタフェースを指定してppoedを起動すると、そのインタフェース上でPPPoEディスカバリを待ち受け、クライアントの接続要求に応じてPPPセッションを確立する。

**主要な処理内容**：
1. netgraphノードグラフの構築（ether -> pppoe -> socket）
2. PPPoEリッスンモードの設定（NGM_PPPOE_LISTEN）
3. PPPoEディスカバリメッセージの受信待ち
4. セッション確立時のpppプロセスのfork/exec（Spawn関数）
5. 環境変数によるクライアント情報（MACアドレス、セッションID）の伝達
6. シグナル処理によるデーモン終了

**関連システム・外部連携**：netgraph(4)フレームワーク、ng_pppoe(4)カーネルモジュール、ng_ether(4)カーネルモジュール、ng_socket(4)カーネルモジュール、ppp(8)デーモン。

**権限による制御**：netgraphソケットの操作にはroot権限が必要。起動されるpppプロセスの権限はppp(8)の設定に依存する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | CLIデーモン | 主機能 | バックグラウンドデーモンとして動作、画面なし |

## 機能種別

デーモンプロセス / ネットワークプロトコル処理 / プロセス起動制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| interface | 文字列 | Yes | PPPoEをリッスンするイーサネットインタフェース名 | 有効なインタフェース名 |
| -F | フラグ | No | フォアグラウンド実行 | なし |
| -d | フラグ | No | デバッグモード有効化 | なし |
| -P pidfile | 文字列 | No | PIDファイルのパス | 有効なファイルパス |
| -a name | 文字列 | No | アクセスコンセントレータ名 | 任意の文字列 |
| -e exec | 文字列 | No | セッション確立時に実行するコマンド | 有効なコマンド |
| -l label | 文字列 | No | pppのラベル名 | 有効なラベル |
| -n ngdebug | 整数 | No | netgraphデバッグレベル | 0以上の整数 |
| -p provider | 文字列 | No | サービスプロバイダ名 | 任意の文字列 |

### 入力データソース

- ネットワークインタフェース: イーサネットフレームからのPPPoEディスカバリパケット
- netgraphメッセージ: ng_pppoe(4)からのセッション制御メッセージ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 子プロセス (ppp) | プロセス | PPPoEセッション確立時に起動されるpppプロセス |
| syslogメッセージ | テキスト | セッション確立・終了・エラー等のログ |
| 環境変数 HISMACADDR | 文字列 | 接続クライアントのMACアドレス |
| 環境変数 SESSION_ID | 文字列 | PPPoEセッションID |

### 出力先

- syslog
- ppp子プロセス（環境変数経由でクライアント情報を伝達）

## 処理フロー

### 処理シーケンス

```
1. 起動・初期化
   └─ コマンドライン引数解析、デフォルト実行コマンドの設定
2. netgraphソケットノード作成
   └─ NgMkSockNode()で制御ソケット(cs)とデータソケット(ds)を取得
3. netgraphノードグラフ構築（ConfigureNode関数）
   ├─ etherノードへNGM_LISTHOOKSを送信し既存フック確認
   ├─ PPPoEノードが未接続の場合、NGM_MKPEERで新規作成
   ├─ PPPoEノードとソケットノードをNGM_CONNECTで接続
   └─ NGM_PPPOE_LISTENでPPPoEリッスンモードを開始
4. デーモン化
   └─ -Fオプション未指定時、daemon(2)でバックグラウンド化
5. メインループ
   ├─ NgRecvMsg()でnetgraphメッセージを受信
   ├─ NGM_PPPOE_SUCCESS受信時: Spawn()でpppプロセスを起動
   └─ シグナル受信時: 終了処理
```

### フローチャート

```mermaid
flowchart TD
    A[起動] --> B[引数解析]
    B --> C[NgMkSockNode - ソケットノード作成]
    C --> D[ConfigureNode - ノードグラフ構築]
    D --> E{etherノードにPPPoE接続済み?}
    E -->|No| F[NGM_MKPEER - PPPoEノード作成]
    E -->|Yes| G[既存PPPoEノードを使用]
    F --> H[NGM_CONNECT + NGM_PPPOE_LISTEN]
    G --> H
    H --> I[daemon化]
    I --> J{NgRecvMsg 待ち}
    J -->|PPPOE_SUCCESS| K[Spawn - pppプロセス起動]
    J -->|シグナル| L[終了処理]
    K --> J
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-102-1 | ノード再利用 | etherノードに既にng_pppoeノードが接続されている場合はそれを再利用する | 常時 |
| BR-102-2 | 二重fork | Spawn()ではdouble forkパターンを使用して孤児プロセスを回避する | セッション確立時 |
| BR-102-3 | デフォルト実行コマンド | -eオプション未指定時は"exec /usr/sbin/ppp -direct"がデフォルト | 常時 |
| BR-102-4 | 環境変数伝達 | HISMACADDRとSESSION_IDを環境変数としてpppプロセスに伝達 | セッション確立時 |

### 計算ロジック

特になし。

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

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| EX_UNAVAILABLE | netgraph通信失敗 | NgSendMsg/NgRecvMsg失敗 | stderrにエラー出力、終了 |
| EX_DATAERR | ノードタイプ不一致 | etherノードの型がNG_ETHER_NODE_TYPEでない | stderrにエラー出力、終了 |
| EX_OSERR | ノード作成失敗 | NGM_MKPEER/NGM_CONNECT失敗 | stderrにエラー出力、終了 |
| EX_CANTCREAT | ソケットノード作成失敗 | NgMkSockNode失敗 | syslogに記録、子プロセス終了 |
| LOG_ERR | fork失敗 | fork(2)失敗 | syslogに記録 |

### リトライ仕様

リトライ機構は実装されていない。エラー発生時はプロセスが終了する。

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

該当なし。

## パフォーマンス要件

- netgraphメッセージのブロッキング受信による低CPU使用率
- 各PPPoEセッションは個別のpppプロセスとして独立動作

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

- netgraphソケット操作にはroot権限が必須
- 起動されるpppプロセスの権限はppp(8)の設定ファイルに依存
- PPPoEプロトコル自体には認証機構がなく、PPP層のCHAP/PAP認証に依存

## 備考

- ppoedはnetgraph(4)フレームワークに強く依存しており、FreeBSD固有の実装である
- ng_pppoeカーネルモジュールが未ロードの場合、自動ロードを試みる（NOKLDLOADが未定義の場合）
- PPPoEのプロバイダ名フィルタリングにより、特定サービスのみリッスンすることが可能

---

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

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

### 推奨読解順序

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

PPPoEのnetgraph連携で使用される構造体を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | pppoed.c | `libexec/pppoed/pppoed.c` | ngpppoe_init_data構造体、ngm_connect構造体の使用パターン |

**読解のコツ**: netgraphのノードグラフ構造を理解することが重要。pppoed.cの90-113行目のASCIIアート図がノード接続構造を視覚的に示している。ether -> pppoe -> socketの3層構造を把握すること。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pppoed.c | `libexec/pppoed/pppoed.c` | main関数の引数解析とデーモン化 |

**主要処理フロー**:
1. **64行目**: DEFAULT_EXEC_PREFIX定義 ("exec /usr/sbin/ppp -direct ")
2. **65-66行目**: 環境変数名定義（HISMACADDR, SESSION_ID）
3. **72-78行目**: usage関数（コマンドラインヘルプ）

#### Step 3: ノードグラフ構築を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | pppoed.c | `libexec/pppoed/pppoed.c` | ConfigureNode関数（86-250行目） |

**主要処理フロー**:
- **130-143行目**: etherノードへのNGM_LISTHOOKS送信とフック一覧取得
- **167-189行目**: 既存のPPPoEノードの探索（orphan/divertフック確認）
- **191-210行目**: PPPoEノード未接続時のNGM_MKPEERによる新規作成
- **212-221行目**: PPPoEノードとソケットノードのNGM_CONNECT
- **242-247行目**: NGM_PPPOE_LISTENの送信

#### Step 4: セッション起動処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | pppoed.c | `libexec/pppoed/pppoed.c` | Spawn関数（252行目以降） |

**主要処理フロー**:
- **266-279行目**: double fork パターン（孤児プロセス回避）
- **287-290行目**: 新しいnetgraphソケットノード作成
- **293-294行目**: exec-<pid>フック名の生成

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

```
main()
    |
    ├─ NgMkSockNode()           [netgraph ソケット作成]
    |
    ├─ ConfigureNode()
    |     ├─ NgSendMsg(NGM_LISTHOOKS)  [既存フック確認]
    |     ├─ NgRecvMsg()               [応答取得]
    |     ├─ NgSendMsg(NGM_MKPEER)     [PPPoEノード作成]
    |     ├─ NgSendMsg(NGM_CONNECT)    [ノード接続]
    |     └─ NgSendMsg(NGM_PPPOE_LISTEN) [リッスン開始]
    |
    ├─ daemon()                 [バックグラウンド化]
    |
    └─ [メインループ]
          ├─ NgRecvMsg()        [メッセージ受信]
          └─ Spawn()
                ├─ fork() x 2   [double fork]
                ├─ NgMkSockNode() [新ソケット]
                ├─ NgSendMsg(NGM_CONNECT) [exec接続]
                └─ execvp()      [ppp起動]
```

### データフロー図

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

PPPoE Discovery ──────▶ ng_ether ──▶ ng_pppoe ──▶ ng_socket
パケット                                                    |
                                                    NgRecvMsg()
                                                            |
                                                     Spawn() ──▶ ppp -direct
                                                            |     (子プロセス)
                                                            |
                                              HISMACADDR ──▶ 環境変数
                                              SESSION_ID ──▶ 環境変数
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| pppoed.c | `libexec/pppoed/pppoed.c` | ソース | ppoedデーモンの全実装（main, ConfigureNode, Spawn） |
| pppoed.8 | `libexec/pppoed/pppoed.8` | マニュアル | manページ |
| Makefile | `libexec/pppoed/Makefile` | ビルド | ビルド設定 |
