From cbb4854d3deb93b2f7ace9964181a00190b0ceb1 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Tue, 14 Jul 2026 00:11:17 +0800 Subject: [PATCH] docs(marker): add traffic-insight feature documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the marker feature following the gns3-documentation standard: overview, Mermaid architecture and business-process diagrams, API endpoint tables, request/response and field reference, error responses, and notes covering the key design points — immutable name (rename = delete + create), global-prefix reservation, read-only inherited markers, render hints (color / highlight_duration), supported node types, and persistence. Indexed under Features in docs/README.md. --- docs/README.md | 3 + docs/features/marker-traffic-insight.md | 221 ++++++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 docs/features/marker-traffic-insight.md diff --git a/docs/README.md b/docs/README.md index 7b40b951c..bc2d414aa 100644 --- a/docs/README.md +++ b/docs/README.md @@ -72,6 +72,9 @@ Unified error response format across all GNS3 API endpoints. Documents HTTP stat ### Web Wireshark (`features/web-wireshark-business-process.md`) Web-based packet capture analysis using Docker + xpra HTML5 client. Zero-install Wireshark experience directly in the browser, integrated with GNS3 topologies. +### Marker (Traffic Insight) (`features/marker-traffic-insight.md`) +Real-time traffic insight via per-link BPF markers and project-level inherited definitions. A marker taps a link in uBridge, emitting match notifications and pcap capture on BPF hit; definitions fan out to every capable link automatically. + --- ## GNS3 AI Copilot (`gns3-copilot/`) diff --git a/docs/features/marker-traffic-insight.md b/docs/features/marker-traffic-insight.md new file mode 100644 index 000000000..86f893469 --- /dev/null +++ b/docs/features/marker-traffic-insight.md @@ -0,0 +1,221 @@ + + +> This documentation is organized by AI with reference to actual code. AI can make mistakes — please verify against the source code when in doubt. + +# Marker (Traffic Insight) + +## Overview + +A **marker** is a passive traffic-insight tap attached to a link. It runs a libpcap BPF +expression inside uBridge; on every match uBridge emits a real-time `MARK` signal and +appends the matching packet to a per-marker pcap file. Markers exist at two layers that +coexist on the same link: **per-link private markers** and **project-level definitions** +that are inherited by every capable link. + +## Architecture + +```mermaid +graph TB + UI["Web UI"] + + subgraph Controller["Controller"] + DEF["Project definitions
(inheritance templates)"] + LNK["Per-link markers"] + end + + Compute["Compute Node"] + UB["uBridge
mark filter"] + PCAP[("pcap file")] + LSTN["Marker listener
(UDP, per compute)"] + + UI -->|"REST + notifications ws"| Controller + DEF -.->|"fan-out: global-{name}"| LNK + LNK -->|"node.post /markers"| Compute + Compute --> UB + UB -->|"BPF match"| PCAP + UB -->|"UDP MARK signal"| LSTN + LSTN -->|"marker.match"| UI +``` + +Inheritance is a controller-only fan-out: a definition CRUD loops over links and reuses the +existing per-link marker operations, so the compute side sees an ordinary marker and is +unchanged. Each compute process runs one UDP listener serving every uBridge on that host; +the `node` field in each signal identifies the source. + +## Business Process + +```mermaid +sequenceDiagram + participant UI as Web UI + participant C as Controller + participant L as Capable Link + participant N as Compute / uBridge + + UI->>C: POST /marker-definitions {name, bpf, ...} + C->>C: store definition + loop every capable link + C->>L: start_marker("global-{name}") + L->>N: install mark filter (BPF + pcap) + end + C-->>UI: 201 + link_ids + + Note over N: later: a packet matches the BPF + N->>N: emit MARK signal + append pcap + N-->>UI: marker.match notification (per-project ws) +``` + +Updating a definition syncs `bpf / tag / color / highlight_duration` to every inherited +copy; deleting a definition removes every inherited copy. A newly created link inherits all +existing definitions automatically. + +## API Endpoints + +All endpoints require a JWT bearer token (`POST /v3/access/users/authenticate`). The +`Auth` column lists the required privilege. + +### Per-link markers + +| Method | Path | Description | Auth | +|--------|------|-------------|------| +| GET | `/v3/projects/{pid}/links/{lid}/markers` | List markers on a link | Link.Audit | +| POST | `/v3/projects/{pid}/links/{lid}/markers` | Attach a marker | Link.Modify | +| PUT | `/v3/projects/{pid}/links/{lid}/markers/{name}` | Update a marker | Link.Modify | +| DELETE | `/v3/projects/{pid}/links/{lid}/markers/{name}` | Remove a marker | Link.Modify | + +### Project-level definitions + +| Method | Path | Description | Auth | +|--------|------|-------------|------| +| GET | `/v3/projects/{pid}/marker-definitions` | List definitions + bound `link_ids` | Project.Audit | +| POST | `/v3/projects/{pid}/marker-definitions` | Create definition (fans out to every link) | Project.Modify | +| PUT | `/v3/projects/{pid}/marker-definitions/{name}` | Update definition (syncs all copies) | Project.Modify | +| DELETE | `/v3/projects/{pid}/marker-definitions/{name}` | Delete definition (clears all copies) | Project.Modify | + +### Aggregation + +| Method | Path | Description | Auth | +|--------|------|-------------|------| +| GET | `/v3/projects/{pid}/markers` | All markers across links, flat | Project.Audit | + +The link object returned by `GET /v3/projects/{pid}/links[/{lid}]` also carries a `markers` +field (including inherited markers), so the Web UI can render a link's markers without an +extra request. + +## Request / Response + +**Marker create body** (`MarkerCreate`, shared by per-link POST and PUT): + +```json +{ + "name": "icmp", + "bpf": "icmp", + "tag": 1, + "color": "#ff5722", + "highlight_duration": 800, + "enabled": true +} +``` + +**Definition create body** (`MarkerDefinitionCreate`, shared by POST and PUT): + +```json +{ + "name": "arp", + "bpf": "arp", + "tag": 5, + "color": "#ff5722", + "highlight_duration": 1200 +} +``` + +**Marker entry** (returned by GET/POST/PUT, and the value of each link's `markers[name]`): + +```json +{ + "bpf": "icmp", + "tag": 1, + "enabled": true, + "color": "#ff5722", + "highlight_duration": 800, + "capture_node_id": "a37e2235-e21f-46c9-a2ab-ba0f8c5465e6", + "inherited_from": null +} +``` + +**Definition GET response** (adds `link_ids`): + +```json +{ + "arp": { + "bpf": "arp", + "tag": 5, + "color": null, + "highlight_duration": 1200, + "link_ids": ["656ed826-...", "6bd9d156-..."] + } +} +``` + +## Field Reference + +### Marker entry + +| Field | Type | Description | +|-------|------|-------------| +| `bpf` | string | libpcap BPF expression (required) | +| `tag` | int \| null | Correlation id echoed in `MARK` signals | +| `enabled` | bool | Whether the marker is active | +| `color` | string \| null | Hex color render hint, e.g. `#ff5722` | +| `highlight_duration` | int \| null | UI highlight duration in ms after a match; `null` = UI default | +| `capture_node_id` | string | Server-chosen node whose uBridge hosts the marker | +| `inherited_from` | string | Source definition name — present on inherited markers only | + +### Definition + +| Field | Type | Description | +|-------|------|-------------| +| `bpf` | string | libpcap BPF expression (required) | +| `tag` | int \| null | Correlation id | +| `color` | string \| null | Hex color render hint | +| `highlight_duration` | int \| null | UI highlight duration in ms; `null` = UI default | +| `link_ids` | string[] | Links currently carrying an inherited copy (GET only) | + +### Notifications + +| Event | Payload | Delivered to | +|-------|---------|--------------| +| `link.updated` | Link object (its `markers` field is the source of truth) | Project notification ws | +| `marker.match` | `project_id`, `node_id`, `link_id`, `filter`, `tag`, `ts`, `len` | Project notification ws only | + +## Error Responses + +| Status | Description | +|--------|-------------| +| 401 | Not authenticated | +| 404 | Link / marker / definition not found | +| 409 | Per-link edit or delete of an inherited marker; reserved (`global`) name or duplicate name on create | +| 422 | Validation failure (name format, `highlight_duration < 1`, missing `bpf`) | + +## Notes + +- **Marker name is immutable.** It is the identifier across the controller, the uBridge + filter, the pcap filename, and `MARK` signal routing — so rename is a delete + recreate, + not a field update. PUT ignores the body `name`; the `{name}` path parameter identifies + the target, and only `bpf / tag / color / enabled / highlight_duration` are changeable. +- **`global` prefix reserved.** User-chosen names may not start with `global`; inherited + markers are stored as `global-{definition_name}` so the two namespaces cannot collide. + Omitting `name` on create yields an auto-generated, prefix-free name. +- **Inherited markers are read-only per-link.** PUT/DELETE on an inherited marker returns + 409 — edit them through the definitions API. +- **Render hints are not enforced.** `color` and `highlight_duration` (milliseconds, `>= 1`) + are stored on the link and never sent to uBridge; `null` lets the UI apply its own + default. A partial PUT (e.g. changing only `bpf`) leaves them untouched. +- **Supported node types.** A marker needs a uBridge bridge: `vpcs`, `qemu`, `docker`, + `iou`, `dynamips`, `cloud` (one capable endpoint suffices). Types without a uBridge are + silently skipped by the inheritance fan-out. +- **Persistence.** Definitions and private markers persist in the topology; inherited + markers are re-created from definitions on project load, so reopening a project restores + the same configuration and stale inherited copies cannot survive on disk.