系統架構
本文件詳細說明 SimNet 的系統架構,涵蓋技術棧(Tech Stack)、元件(Component)階層、資料流(Data Flow)與主題系統。
1. 技術棧總覽
| 層級 | 技術 | 角色 |
|---|---|---|
| 靜態網站產生器 | VitePress 2.0 (SSG) | 將 Markdown 內容轉為靜態網頁,支援自訂 Layout(版面配置) |
| UI 框架 | Vue 3 Composition API | 響應式(Reactive)UI 元件系統 |
| 樣式系統 | Tailwind CSS 4.2 | Utility-first CSS 框架,搭配 CSS custom properties(CSS 自訂屬性)實作主題切換 |
| 模擬引擎 | Rust / WebAssembly (WASM) | 網路協定模擬核心,編譯為 WASM 在瀏覽器中執行 |
| 終端機 | xterm.js 6.0 | 瀏覽器內嵌終端機模擬器,提供 CLI 互動介面 |
| 拓撲圖 | Canvas 2D | 網路拓撲(Topology)視覺化渲染 |
2. 架構圖
┌─────────────────────────────────────────────┐
│ VitePress (SSG) │
│ ┌────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ Layout │→ │ SimNet │→ │ Resizable │ │
│ │ .vue │ │ .vue │ │ Panel .vue │ │
│ └────────┘ └────┬─────┘ └──────┬──────┘ │
│ │ │ │
│ ┌──────────────┼───────────────┤ │
│ ▼ ▼ ▼ │
│ Topology TrafficLog Terminal │
│ (Canvas2D) (Vue DOM) (xterm.js) │
│ │ │ │ │
│ └──────────────┼───────────────┘ │
│ ▼ │
│ useSimulation.ts │
│ useTerminal.ts │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ WASM Bridge │ │
│ │ (simnet_wasm) │ │
│ └────────┬────────┘ │
└──────────────────┼───────────────────────────┘
▼
┌─────────────────┐
│ Rust Engine │
│ (simnet-core) │
│ Protocols, Sim │
└─────────────────┘架構說明
上層:VitePress + Vue 元件
Layout.vue是全域進入點,偵測 frontmatter 中的layout: challenge來決定是否啟用挑戰模式ChallengeLayout.vue組裝SimNet.vue,將 Markdown 內容注入#missionslot(插槽)SimNet.vue是核心容器,負責初始化模擬引擎並透過provide/inject(提供/注入)機制將引擎實例與封包資料傳遞給子元件
中層:Composable 邏輯層
useSimulation.ts— 管理 WASM 引擎生命週期、requestAnimationFrame迴圈、封包正規化(Normalization)useChallenge.ts— 載入挑戰設定、管理狀態機(loading → running → completed)、處理 Flag 驗證useTerminal.ts— 將 xterm.js 與 WASM 引擎命令橋接,提供 CLI 互動
底層:Rust WASM 引擎
- 以 JSON 格式的 config 初始化,模擬網路拓撲、協定行為與事件系統
- 透過
wasm-bindgen匯出SimNetEngineclass 供 JavaScript 呼叫
3. 目錄結構
network-sim/
├── .vitepress/
│ ├── theme/
│ │ ├── layouts/
│ │ │ ├── Layout.vue # 全域 Layout — 偵測挑戰頁面
│ │ │ └── ChallengeLayout.vue # 挑戰專用 Layout — 全螢幕模擬器
│ │ ├── components/
│ │ │ ├── SimNet.vue # 核心容器 — 初始化引擎、provide 資料
│ │ │ ├── ResizablePanel.vue # 可拖曳調整大小的面板系統
│ │ │ ├── TopologyView.vue # Canvas 2D 網路拓撲圖
│ │ │ ├── TrafficLog.vue # 封包流量日誌(Vue DOM 渲染)
│ │ │ ├── Terminal.vue # xterm.js 終端機元件
│ │ │ ├── IconBar.vue # 側邊功能列
│ │ │ ├── IconBarPanel.vue # 功能列展開面板
│ │ │ ├── FlagSubmit.vue # Flag 提交表單(導覽列嵌入)
│ │ │ ├── DeviceModal.vue # 裝置詳情彈窗
│ │ │ └── ChallengeSidebar.vue # 挑戰列表側邊欄
│ │ ├── composables/
│ │ │ ├── useSimulation.ts # WASM 引擎管理 + RAF 迴圈
│ │ │ ├── useChallenge.ts # 挑戰狀態機 + Flag 驗證
│ │ │ ├── useTerminal.ts # 終端機命令橋接
│ │ │ ├── useBreakpoint.ts # 響應式斷點偵測
│ │ │ └── injectionKeys.ts # Vue provide/inject Symbol Keys
│ │ ├── loaders/
│ │ │ └── challenge.data.ts # VitePress data loader — 建置時載入挑戰資料
│ │ ├── styles/
│ │ │ ├── animations.css # 動畫定義
│ │ │ └── vitepress-overrides.css # VitePress 預設樣式覆寫
│ │ ├── style.css # SimNet 色彩系統(CSS custom properties)
│ │ └── index.ts # 主題進入點
│ └── types/
│ └── wasm.d.ts # WASM 模組型別宣告
├── docs/
│ ├── index.md # 首頁
│ ├── challenges/
│ │ ├── index.md # 挑戰列表頁
│ │ ├── flags.secret.yaml # Flag 明文(不進版控)
│ │ └── 01-ethernet-basics/ # 範例情境
│ │ ├── index.md # Markdown 頁面(frontmatter 指定 layout)
│ │ └── config.yaml # 拓撲、事件、Flag 設定
│ └── public/
│ └── wasm/ # 編譯後的 WASM 產物
│ ├── simnet_wasm.js # JS glue code
│ ├── simnet_wasm_bg.wasm # WASM binary
│ └── simnet_wasm.d.ts # TypeScript 型別定義
├── simnet-engine/ # Rust workspace
│ └── crates/
│ ├── simnet-core/ # 核心型別:Device、Frame、Protocol、Event
│ ├── simnet-devices/ # 裝置實作:PC、Switch、Server
│ ├── simnet-protocols/ # 協定實作:Ethernet、ARP、IPv4、TCP、HTTP
│ ├── simnet-sim/ # 模擬引擎:Topology、Simulation、EventEngine
│ ├── simnet-crypto/ # 加密模組:Flag 加密與驗證
│ └── simnet-wasm/ # WASM 橋接層:wasm-bindgen API
└── tests/ # Vitest 測試4. 資料流
以下說明模擬引擎在每一個 tick(時鐘週期)中如何驅動整個系統。
一次 tick 的完整流程
requestAnimationFrame 迴圈
│
▼
engine.tick() ← 呼叫 WASM:推進模擬一個時鐘週期
│
▼
engine.get_new_packets() ← 取得本次 tick 產生的新封包(JSON 陣列)
│
▼
normalizePacket() ← 將 snake_case 欄位轉為 camelCase
(srcDevice, dstDevice, protocol, summary, layers...)
│
▼
packets.value = [...] ← 更新 Vue reactive ref(上限 500 筆)
│
▼
provide(SIM_PACKETS_KEY) ← 透過 Vue provide/inject 傳遞給子元件
│
┌────┼────┐
▼ ▼ ▼
Topology TrafficLog Terminal
(Canvas) (Vue DOM) (xterm.js)初始化流程
- 使用者進入挑戰頁面,
ChallengeLayout.vue被掛載 SimNet.vue的onMounted觸發loadConfig(path)useChallenge.ts透過fetch()取得config.yaml(已由 VitePress 建置流程轉為 JSON)- 將 config JSON 傳入
useSimulation.init(configJson) init()動態載入 WASM 模組(import('/wasm/simnet_wasm.js')),呼叫__wbg_init()初始化- 以 config JSON 建構
SimNetEngine實例 requestAnimationFrame迴圈啟動,模擬開始運行
封包正規化
WASM 引擎回傳的封包使用 Rust 風格的 snake_case 命名,normalizePacket() 函式負責轉換為前端慣用的 camelCase:
| WASM 欄位 | 正規化後 |
|---|---|
src_device | srcDevice |
dst_device | dstDevice |
protocol | protocol(不變) |
summary | summary(不變) |
layers | layers(不變) |
每個封包會被賦予遞增的唯一 id,用於 Vue 的 v-for :key 追蹤。
5. 主題系統
SimNet 使用 CSS custom properties 實作深色/淺色主題切換,與 VitePress 的 .dark class 機制整合。
色彩架構
所有色彩定義於 .vitepress/theme/style.css,分為以下類別:
| 類別 | 變數前綴 | 範例 |
|---|---|---|
| 基底色 | --color-sim-bg, --color-sim-panel | 背景、面板、邊框 |
| 強調色 | --color-sim-accent | 深色模式:Cyber Green #00ffaa;淺色模式:Indigo #4f46e5 |
| 語意色 | --color-sim-info, --color-sim-warn, --color-sim-danger | 狀態指示 |
| 文字色 | --color-sim-text, --color-sim-text-muted, --color-sim-text-dim | 主要、次要、暗淡文字 |
| 協定色 | --color-proto-ethernet, --color-proto-arp, ... | 封包依協定類型上色 |
深色/淺色切換
/* 深色模式(預設) */
@theme {
--color-sim-bg: #101828;
--color-sim-accent: #00ffaa;
/* ... */
}
/* 淺色模式覆寫 */
:root:not(.dark) {
--color-sim-bg: #f1f5f9;
--color-sim-accent: #4f46e5;
/* ... */
}VitePress 在 <html> 元素上切換 .dark class,所有 SimNet 元件自動跟隨切換。
終端機始終深色
即使在淺色模式下,終端機面板也維持深色主題(符合真實終端機的視覺慣例):
:root:not(.dark) .sim-terminal {
--color-sim-panel: #1e293b;
--color-sim-bg: #0f172a;
/* ... */
}6. 挑戰框架
挑戰系統的資料流從 YAML 設定檔開始,經過多個階段最終呈現為互動式模擬。
挑戰載入流程
config.yaml frontmatter (index.md)
│ │
▼ ▼
VitePress build Layout.vue
(YAML → JSON) 偵測 layout: "challenge"
│ │
▼ ▼
fetch(config.json) ChallengeLayout.vue
│ │
▼ ▼
useChallenge.ts SimNet.vue
(狀態機管理) (provide engine + packets)
│ │
▼ ▼
useSimulation.init() 子元件渲染
(WASM 引擎初始化) Topology / TrafficLog / Terminal挑戰狀態機
useChallenge.ts 管理四種狀態:
loading ──→ running ──→ completed
│ ▲
└──→ error │
submitFlag(正確)- loading:正在載入 config 並初始化 WASM 引擎
- running:模擬運行中,使用者可以觀察封包、下達指令
- completed:Flag 驗證成功
- error:config 載入失敗或 WASM 初始化錯誤
Flag 驗證機制
Flag 驗證在瀏覽器端使用 Web Crypto API 計算 SHA-256 hash(雜湊值),與 config 中預存的 hash 進行比對。明文 Flag 永遠不會出現在前端程式碼中。
使用者輸入 → SHA-256(input) → 與 config.flag.hash 比對 → correct / incorrect響應式版面
SimNet.vue 透過 useBreakpoint.ts 偵測螢幕寬度,提供三種版面:
| 斷點 | 版面 | 特色 |
|---|---|---|
| Desktop(桌面) | 三面板 + 側邊功能列 | ResizablePanel 可拖曳調整大小 |
| Tablet(平板) | 上方標籤切換 + 下方終端機 | Topology / Traffic 標籤切換 |
| Mobile(手機) | 底部導覽列 + 單一全螢幕面板 | 五個標籤:Topo / Traffic / Term / Mission / Hints |
7. Semantic Engine(語意引擎)
Semantic Engine 負責解析挑戰設定(config.yaml),建構語意模型(Semantic Model)。它不只是讀取拓撲資料,而是理解網路的語意——路由關係、DNS 解析鏈、服務可達性。
職責
- 拓撲建模:將
topology.nodes與topology.links轉換為圖結構,計算可達性 - 路由語意:根據裝置的 IP/子網路設定,建構路由表與轉發邏輯
- DNS 語意:解析 DNS 服務設定,建構域名→IP 對應表
- 服務語意:標記哪些裝置提供哪些服務(HTTP、DNS),用於行為引擎的事件派發
故障與攻擊模型
DSL v2 的 faults 與 attacks 區塊由 Semantic Engine 解析為語意操作:
- 故障(Faults):路由缺失、DNS 紀錄錯誤、介面停用——這些是初始狀態的「損壞」
- 攻擊(Attacks):ARP spoofing、MITM、DoS——這些是環境中持續進行的惡意行為
8. Behavior Engine(行為引擎)
Behavior Engine 負責驅動模擬的動態行為,包括事件規則執行、故障/攻擊展開、以及勝利條件評估。
事件規則
事件規則定義在 config.yaml 的 events 區塊中,Behavior Engine 在每個 tick 中評估:
- 時間觸發:
periodic_frame、periodic_communication等依照interval_ms週期觸發 - 條件觸發:
trigger欄位指定的條件成立時觸發(如mitm_active) - 鏈式觸發:一個事件的結果可觸發另一個事件
故障/攻擊展開
Semantic Engine 提供的故障與攻擊模型,由 Behavior Engine 在模擬啟動時展開為具體的事件序列:
- 路由缺失 → 刪除路由表中的特定條目
- ARP spoofing → 週期性注入偽造的 ARP reply
- DoS flood → 產生大量 SYN 封包
勝利條件評估
win_condition 定義了挑戰完成的判定邏輯,Behavior Engine 在每個 tick 後檢查:
| 類型 | 說明 |
|---|---|
capture_payload | 玩家擷取到包含特定內容的封包 |
connectivity_restored | 指定裝置間的連線恢復 |
defense_active | 玩家成功啟用所有要求的防禦措施 |
service_available | 指定服務恢復可用狀態 |
9. DSL v2 架構
DSL v2 是 SimNet 的挑戰設定語言,以 config.yaml 為載體,描述完整的挑戰情境。
資料流
graph TB
YAML[config.yaml] --> Parser[DSL v2 Parser]
Parser --> SM[Semantic Model]
SM --> BE[Behavior Engine]
BE --> Sim[Simulation Loop]
Sim --> UI[Vue Components]
UI --> TL[TrafficLog]
UI --> TV[TopologyView]
UI --> Term[Terminal]config.yaml 結構總覽
challenge: # 挑戰中繼資料
id, title, difficulty, category, flag
topology: # 網路拓撲
nodes: [...] # 裝置定義
links: [...] # 連線定義
faults: # 故障定義(Network Repair 類別使用)
- type: missing_route
device: router1
destination: 10.0.2.0/24
attacks: # 攻擊定義(Security Attack / Transmission Defense 類別使用)
- type: arp_spoofing
attacker: eve
target: pc1
impersonates: gateway
player_tools: # 玩家可用工具
- tcpdump # 封包擷取
- arp # ARP 表查看
- ip # 路由與介面操作
- ping # 連通性測試
hints: # 分階段提示
- trigger: after_30s
text: "..."
win_condition: # 勝利條件
type: capture_payload | connectivity_restored | defense_active從 config.yaml 到模擬器
- VitePress 建置:YAML 被轉換為 JSON,Flag 佔位符被替換
- DSL v2 Parser:驗證結構、展開語法糖、解析引用關係
- Semantic Model:建構網路語意模型(路由表、DNS 表、服務表)
- Behavior Engine:將 faults/attacks/events 展開為事件佇列
- Simulation Loop:每個 tick 推進模擬、評估勝利條件
- Vue Components:響應式渲染拓撲圖、封包日誌、終端機
接下來,閱讀如何擴充模板來了解如何為 SimNet 建立新的內容。