# 通知設計書 10-IFNET LINK_STATE

## 概要

本ドキュメントは、FreeBSDカーネルのネットワークスタックにおいて、ネットワークインターフェースのリンク状態が変更された際に発行されるdevctl通知「IFNET <インターフェース名> LINK_UP/LINK_DOWN」の設計を記述する。

### 本通知の処理概要

ネットワークインターフェースのリンク状態（リンクアップ/リンクダウン）が変化した際に、devctlサブシステムを通じてユーザランドに通知を送信する処理である。

**業務上の目的・背景**：ネットワークケーブルの抜き差しやスイッチポートの状態変化によるリンク状態の変更を、ユーザランドが検知する必要がある。これにより、DHCPの再取得、フェイルオーバー処理、ネットワーク監視アラートの発行、VPNの再接続などの自動化が可能になる。

**通知の送信タイミング**：if_link_state_change_1()関数内で、リンク状態が実際に変化した場合にデフォルトVNET内でのみ送信される。リンク状態はLINK_STATE_UP（アップ）かそれ以外（ダウン）の2状態に分類される。

**通知の受信者**：devd(8)デーモンおよび/dev/devctlをモニタリングしているユーザランドプロセスが受信する。

**通知内容の概要**：インターフェース名がsubsystemとして、リンク状態に応じて"LINK_UP"または"LINK_DOWN"がtypeとして通知される。dataはNULL。

**期待されるアクション**：LINK_UP時はDHCPの再取得やネットワークサービスの再起動を、LINK_DOWN時はフェイルオーバー処理やアラート発行を実行する。

## 通知種別

カーネル内devctl通知（/dev/devctl経由のユーザランド通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（devctlキューへのエンキュー） |
| 優先度 | 通常 |
| リトライ | なし |

### 送信先決定ロジック

devctl_notify()により全リスナーにブロードキャストされる。

## 通知テンプレート

### devctl通知メッセージ

| 項目 | 内容 |
|-----|------|
| system | `IFNET` |
| subsystem | `<インターフェース名>` (例: "em0", "igb0") |
| type | `LINK_UP` または `LINK_DOWN` |
| data | NULL |

### 本文テンプレート

```
!system=IFNET subsystem=<インターフェース名> type=LINK_UP
!system=IFNET subsystem=<インターフェース名> type=LINK_DOWN
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| subsystem (インターフェース名) | リンク状態が変更されたインターフェースの名前 | ifp->if_xname | Yes |
| type (リンク状態) | LINK_UPまたはLINK_DOWN | link_state == LINK_STATE_UP ? "LINK_UP" : "LINK_DOWN" | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| カーネル内部イベント | if_link_state_change_1()実行 | IS_DEFAULT_VNET(curvnet) == true | デフォルトVNET内でのみ通知 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 非デフォルトVNET | jail等で作成された非デフォルトVNETではLINK_STATE通知は発行されない |
| リンク状態未変化 | if_link_state_change()の上位でリンク状態が変化していない場合は呼ばれない（コアレッシング処理あり） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[NICドライバがリンク状態変更を検知] --> B[if_link_state_change呼出]
    B --> C[if_link_state_change_1 タスク実行]
    C --> D[リンク状態の更新]
    D --> E[rt_ifmsg / EVENTHANDLER通知]
    E --> F[bridge/lagg通知]
    F --> G{IS_DEFAULT_VNET?}
    G -->|Yes| H{link_state == LINK_STATE_UP?}
    H -->|Yes| I[devctl_notify IFNET name LINK_UP NULL]
    H -->|No| J[devctl_notify IFNET name LINK_DOWN NULL]
    G -->|No| K[通知なし]
    I --> L[終了]
    J --> L
    K --> L
```

## データベース参照・更新仕様

### カーネル内データ構造

| データ構造 | 用途 | 備考 |
|-----------|------|------|
| struct ifnet | ネットワークインターフェース | if_xname, if_link_stateフィールドを使用 |

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| なし | devctl_notify()は戻り値なし | N/A |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし

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

- /dev/devctlへのアクセスにはroot権限が必要
- 通知に含まれるのはインターフェース名とリンク状態のみ

## 備考

- リンク状態の判定はlink_state == LINK_STATE_UPの場合に"LINK_UP"、それ以外は全て"LINK_DOWN"となる。LINK_STATE_UNKNOWNも"LINK_DOWN"として通知される
- 複数のリンク状態変更が短時間に発生した場合、コアレッシング（結合）が行われる場合がある。pending > 1の場合はif_printf()でコアレッシングの旨が出力される
- bridge_linkstateやlagg_linkstateへの通知もdevctl通知の前に行われる
- typeは通知一覧CSVでは"LINK_STATE"と記載されているが、実際のコードでは"LINK_UP"または"LINK_DOWN"という2つの異なる値で発行される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | if_var.h | `sys/net/if_var.h` | struct ifnetの定義：if_xname, if_link_stateフィールド |
| 1-2 | if.h | `sys/net/if.h` | LINK_STATE_UP, LINK_STATE_DOWN, LINK_STATE_UNKNOWNの定義 |

**読解のコツ**: LINK_STATE_UPは「リンクアップ」、LINK_STATE_DOWNは「リンクダウン」、LINK_STATE_UNKNOWNは「不明」を意味する。devctl通知では「UP以外は全てDOWN」として扱われる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | if.c | `sys/net/if.c` | if_link_state_change_1()関数（行2036-2045付近） |

**主要処理フロー**:
1. **行2036**: bridge_linkstateへの通知
2. **行2037-2038**: lagg_linkstateへの通知
3. **行2040-2043**: IS_DEFAULT_VNET判定後、devctl_notify("IFNET", ifp->if_xname, (link_state == LINK_STATE_UP) ? "LINK_UP" : "LINK_DOWN", NULL)
4. **行2044-2045**: pending > 1の場合にコアレッシングのログ出力

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

```
[NICドライバ]
    |
    +-- if_link_state_change(ifp, link_state)
            |
            +-- [タスクキュー経由]
                    |
                    +-- if_link_state_change_1() [sys/net/if.c]
                            |
                            +-- rt_ifmsg() [ルーティングメッセージ通知]
                            +-- EVENTHANDLER_INVOKE(ifnet_link_event)
                            +-- bridge_linkstate / lagg_linkstate
                            +-- devctl_notify("IFNET", ifp->if_xname, "LINK_UP"/"LINK_DOWN", NULL)
```

### データフロー図

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

NICリンク状態変更         --->  if_link_state_change()       --->  タスクキューにスケジュール
                                    |
                                    v
                          if_link_state_change_1()                  rt_ifmsg (ルーティングソケット)
                                    |                               EVENTHANDLER
                                    v                               bridge/lagg通知
                          IS_DEFAULT_VNET判定                        |
                                    |                               v
                                    v                        devctl_notify("IFNET",name,"LINK_UP"/"LINK_DOWN",NULL)
                          link_state判定                             |
                          (UP→"LINK_UP"                             v
                           else→"LINK_DOWN")                /dev/devctl
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| if.c | `sys/net/if.c` | ソース | if_link_state_change_1()でLINK_UP/LINK_DOWN通知を発行 |
| if_var.h | `sys/net/if_var.h` | ヘッダ | struct ifnetの定義 |
| if.h | `sys/net/if.h` | ヘッダ | LINK_STATE_*定数の定義 |
| devctl.h | `sys/sys/devctl.h` | ヘッダ | devctl_notify()のプロトタイプ |
| subr_bus.c | `sys/kern/subr_bus.c` | ソース | devctl_notify()の実装 |
