2026年2月12日木曜日

Windows のルーティング情報を可視化する Powershell

Windows のルーティング情報を可視化する Powershell

概要

タイトル通り

環境

  • Windows 11

スクリプト

# Visualize-Routes.ps1 (vis.jsを使用したHTMLベースの可視化)

param (
    [string]$OutputPath = "$($PSScriptRoot)\network_routes.html",
    [switch]$IncludeDefaultRoutes = $false,
    [string[]]$ExcludeNextHops = @()
)

function Visualize-NetRouteGraph {
    [CmdletBinding()]
    param (
        [string]$OutputPath = "$($PSScriptRoot)\network_routes.html",
        [switch]$IncludeDefaultRoutes = $false,
        [string[]]$ExcludeNextHops = @()
    )

    # ルーティング情報を取得
    $routes = Get-NetRoute
    
    # デフォルトルートを除外する場合
    if (-not $IncludeDefaultRoutes) {
        $routes = $routes | Where-Object { $_.NextHop -ne "::" -and $_.NextHop -ne "0.0.0.0" }
    }
    
    # 指定されたNextHopを除外する場合
    if ($ExcludeNextHops.Count -gt 0) {
        foreach ($excludeHop in $ExcludeNextHops) {
            $routes = $routes | Where-Object { $_.NextHop -ne $excludeHop }
        }
    }

    if (-not $routes) {
        Write-Warning "ルーティング情報が見つかりませんでした。"
        return
    }

    # ノードとエッジの定義
    $nodes = @{}
    $edges = @()
    $nodeId = 0
    $nodeMap = @{}
    $edgeMap = @{}  # エッジの重複を管理

    foreach ($route in $routes) {
        $destination = $route.DestinationPrefix
        $nextHop = $route.NextHop
        $ifIndex = $route.IfIndex
        $interface = $null
        
        # ネットワークアダプタ名を取得(エラーハンドリング付き)
        try {
            $interface = (Get-NetAdapter -IfIndex $ifIndex -ErrorAction Stop).Name
        }
        catch {
            $interface = $null
        }

        # ノードの作成(重複なし)
        if (-not $nodeMap.ContainsKey($destination)) {
            $nodeMap[$destination] = @{
                id    = $nodeId
                label = $destination
                title = "Destination: $destination"
                color = "lightblue"
                shape = "box"
            }
            $nodeId++
        }

        if (-not $nodeMap.ContainsKey($nextHop)) {
            $nodeMap[$nextHop] = @{
                id    = $nodeId
                label = $nextHop
                title = "NextHop: $nextHop"
                color = "lightgreen"
                shape = "ellipse"
            }
            $nodeId++
        }

        # エッジのキーを作成(重複チェック用)
        # NextHop → Destination の向きにすることで、トラフィックフローを表現
        $edgeKey = "$($nodeMap[$nextHop].id)->$($nodeMap[$destination].id)"
        
        # エッジラベルの作成
        $edgeInfo = "Metric: $($route.RouteMetric)"
        if ($interface) {
            $edgeInfo += " (via $interface)"
        }
        
        # 既存のエッジがあれば統合、なければ新規作成
        if ($edgeMap.ContainsKey($edgeKey)) {
            # 既存のエッジにラベルを追加
            $edgeMap[$edgeKey].labels += $edgeInfo
        }
        else {
            # 新規エッジを作成
            $edgeMap[$edgeKey] = @{
                from   = $nodeMap[$nextHop].id
                to     = $nodeMap[$destination].id
                labels = @($edgeInfo)
            }
        }
    }
    
    # エッジマップからエッジ配列を作成(ラベルを結合)
    foreach ($edgeKey in $edgeMap.Keys) {
        $edge = $edgeMap[$edgeKey]
        $edges += @{
            from  = $edge.from
            to    = $edge.to
            label = $edge.labels -join "`n"
        }
    }

    # JSON形式でノードとエッジを作成
    $nodesJson = $nodeMap.Values | ConvertTo-Json
    $edgesJson = $edges | ConvertTo-Json

    # HTMLテンプレートを生成
    $htmlContent = @"
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ネットワークルーティンググラフ</title>
    <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        html, body {
            width: 100%;
            height: 100%;
            font-family: Arial, sans-serif;
        }
        #network {
            width: 100%;
            height: 100%;
            border: 1px solid lightgray;
        }
        .controls {
            position: absolute;
            top: 10px;
            left: 10px;
            background: white;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
            z-index: 100;
        }
        button {
            padding: 5px 10px;
            margin: 5px 2px;
            cursor: pointer;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
        }
        button:hover {
            background-color: #45a049;
        }
    </style>
</head>
<body>
    <div class="controls">
        <button onclick="resetView()">リセット</button>
        <button onclick="fitToScreen()">画面に合わせる</button>
    </div>
    <div id="network"></div>

    <script type="text/javascript">
        var nodes = new vis.DataSet($nodesJson);
        var edges = new vis.DataSet($edgesJson);

        var container = document.getElementById('network');
        var data = {
            nodes: nodes,
            edges: edges
        };

        var options = {
            physics: {
                enabled: true,
                stabilization: {
                    iterations: 200
                },
                barnesHut: {
                    gravitationalConstant: -26000,
                    centralGravity: 0.3,
                    springLength: 300,
                    springConstant: 0.04,
                    damping: 0.3
                }
            },
            nodes: {
                font: {
                    size: 14,
                    color: 'black'
                },
                borderWidth: 2,
                shadow: true
            },
            edges: {
                arrows: 'to',
                font: {
                    size: 12,
                    align: 'middle'
                },
                shadow: true,
                smooth: {
                    type: 'continuous'
                }
            },
            interaction: {
                navigationButtons: true,
                keyboard: true,
                zoomView: true,
                dragView: true
            }
        };

        var network = new vis.Network(container, data, options);

        function resetView() {
            network.fit();
        }

        function fitToScreen() {
            network.fit({
                animation: {
                    duration: 1000,
                    easingFunction: 'easeInOutQuad'
                }
            });
        }

        window.addEventListener('resize', function() {
            network.redraw();
        });

        network.fit();
    </script>
</body>
</html>
"@

    # HTMLファイルを保存
    $htmlContent | Set-Content $OutputPath -Encoding UTF8

    Write-Host "ネットワークルーティンググラフをHTMLで生成しました: $OutputPath"
    Write-Host "ブラウザで表示しています..."

    # ブラウザで開く
    Invoke-Item $OutputPath
}

# 関数を実行(スクリプトの引数を渡す)
$params = @{
    OutputPath = $OutputPath
}
if ($IncludeDefaultRoutes) {
    $params['IncludeDefaultRoutes'] = $IncludeDefaultRoutes
}
if ($ExcludeNextHops.Count -gt 0) {
    $params['ExcludeNextHops'] = $ExcludeNextHops
}
Visualize-NetRouteGraph @params

最後に

作成された HTML をブラウザ開けば OK です

0 件のコメント:

コメントを投稿