gns3-server/docs/features/web-wireshark-business-process.md
YueGuobin 4dab36c08c
docs: replace 'wireshark' with 'gns3server-web-wireshark-setup' in installation commands
The CLI entry point was renamed from 'wireshark' to 'gns3server-web-wireshark-setup'
to avoid conflicts with the system wireshark package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 10:33:41 +08:00

21 KiB
Raw Blame History

This documentation is organized by AI with reference to actual code. AI can make mistakes — please verify against the source code when in doubt.

Web Wireshark Feature - Business Process Documentation

Overview

The Web Wireshark feature enables users to run Wireshark packet capture analysis directly in a web browser without requiring a desktop environment or VNC connection. This is achieved through an xpra (persistent remote applications) HTML5 client running inside a Docker container.

Feature Summary

  • Core Capability: Web-based packet capture visualization using Wireshark
  • Technology Stack: Docker container + xpra + HTML5 WebSocket proxy
  • Target Users: Network engineers and students who need to analyze network traffic in GNS3 topologies
  • Key Benefit: Zero-install, browser-based packet capture analysis

Installation

Before using Web Wireshark, install the GNS3 server and set up the Docker image:

# Development install
pip install -e . && gns3server-web-wireshark-setup

# Production install
pip install gns3-server && gns3server-web-wireshark-setup

This command will:

  1. Install the gns3-server package
  2. Pull the gns3/web-wireshark:latest image from Docker Hub
  3. If pull fails, build the image locally using the included Dockerfile

The wireshark command shows the raw output from docker pull or docker build, allowing you to see the full progress.


Architecture Overview

graph TD
    subgraph GNS3Server["GNS3 Server"]
        WebUI["Web UI<br/>(Browser)"]
        Controller["GNS3 Controller"]
        LinkCtrl["Link Controller<br/>(start_capture / stop_capture)"]
        Manager["WebWiresharkManager<br/>(called directly, not via CLI)"]
        DockerClient["DockerHTTPClient<br/>(HTTP via Unix Socket)"]
    end

    subgraph DockerDaemon["Docker Daemon"]
        Container["gns3-wireshark-{project_id}"]
        Xvfb["Xvfb (virtual framebuffer)"]
        Xpra["xpra server"]
        Wireshark["Wireshark"]
    end

    ClientBrowser["Client Browser<br/>(HTML5 Client)"]

    ClientBrowser -->|REST API| WebUI
    WebUI -->|capture/start| Controller
    Controller --> LinkCtrl
    LinkCtrl -->|start/stop/restart session| Manager
    Manager -->|Docker API<br/>/var/run/docker.sock| DockerClient
    DockerClient -->|container lifecycle| Container
    Container --- Xvfb
    Container --- Xpra
    Container --- Wireshark
    ClientBrowser -.->|WebSocket proxy| Xpra
    Xpra -.->|xpra HTML5| ClientBrowser

    style GNS3Server fill:#e8f4fd,stroke:#2196f3
    style DockerDaemon fill:#fff3e0,stroke:#ff9800
    style ClientBrowser fill:#e8f5e9,stroke:#4caf50

Component Description

1. Web UI (Client Browser)

  • User interface for starting/stopping packet capture
  • Receives WebSocket URL for connecting to xpra HTML5 client
  • No plugins required - pure HTML5/JavaScript

2. GNS3 Controller

  • Orchestrates the capture workflow
  • Validates user permissions (RBAC)
  • Manages link capture state

3. manage_wireshark.py (Management CLI)

  • Command-line interface for container and session management
  • For manual debugging and testing only — the server calls WebWiresharkManager directly at runtime
  • Handles Docker container lifecycle
  • Manages xpra sessions per link

4. WebWiresharkManager

  • Core business logic for Web Wireshark
  • Handles container creation, session startup/shutdown
  • Deterministic port allocation based on link_id

5. DockerHTTPClient

  • Async HTTP client for Docker API
  • Communicates via Unix socket (/var/run/docker.sock)
  • Manages container lifecycle

6. Docker Container (gns3-wireshark-{project_id})

  • Runs xpra server with HTML5 support
  • Contains Xvfb (virtual framebuffer) for headless Wireshark
  • Streams display to browser via WebSocket

Business Processes

Process 1: Start Packet Capture with Web Wireshark

sequenceDiagram
    actor User
    participant WebUI as Web UI
    participant Controller as Controller
    participant LinkCtrl as Link Controller
    participant Manager as WebWiresharkManager

    User->>WebUI: 1. Start Capture
    WebUI->>Controller: 2. POST /capture/start
    Controller->>LinkCtrl: 3. _start_web_wireshark(jwt_token)
    LinkCtrl->>Manager: 4. start_wireshark_session()
    Note right of Manager: 5. Ensure network exists
    Note right of Manager: 6. Get/create container
    Note right of Manager: 7. Start xpra session<br/>(display + port)
    Note right of Manager: 8. Start Wireshark<br/>(curl | wireshark -i - -k)
    Manager-->>LinkCtrl: ws_url
    LinkCtrl-->>Controller: ws_url
    Controller-->>WebUI: 10. {capturing: true, ws_url: "ws://..."}
    WebUI-->>User: 9. ws_url
    Note over User,Manager: 11-12. Browser connects via WebSocket<br/>Server proxies to container xpra

API Endpoint: POST /v3/projects/{project_id}/links/{link_id}/capture/start

Request Body:

{
  "wireshark": true,
  "data_link_type": "DLT_EN10MB",
  "capture_file_name": "capture.pcap"
}

Response:

{
  "id": "link-uuid",
  "capturing": true,
  "ws_url": "ws://192.168.1.100:14500"
}

Process 2: Container Lifecycle (Per Project)

flowchart TD
    A["Project Open"] --> B["First Link Capture"]
    B --> C["Container Created<br/>(one per project)"]
    C --> D["Container Running"]

    subgraph SessionArchitecture["Container Session Architecture"]
        direction TB
        D --> E["gns3-wireshark-{project_id}"]

        subgraph Link1Session["Link 1 Session"]
            L1Xpra["xpra :14503"]
            L1Xvfb["Xvfb :14503<br/>(1920x1080x24)"]
            L1WS["Wireshark<br/>(curl | wireshark -i -)"]
            L1Bind["bind-ws=0.0.0.0:14503"]
            L1Xpra --- L1Xvfb
            L1Xpra --- L1WS
            L1Xpra --- L1Bind
        end

        subgraph Link2Session["Link 2 Session"]
            L2Xpra["xpra :11024"]
            L2Xvfb["Xvfb :11024<br/>(1920x1080x24)"]
            L2WS["Wireshark<br/>(curl | wireshark -i -)"]
            L2Bind["bind-ws=0.0.0.0:11024"]
            L2Xpra --- L2Xvfb
            L2Xpra --- L2WS
            L2Xpra --- L2Bind
        end

        E --> Link1Session
        E --> Link2Session

        Note1["display = port = 10000 + hash(link_id) % 10000<br/>Each xpra creates its own Xvfb (NOT shared)"]
    end

    D --> F["Project Close"]
    F --> G["Stop Container<br/>(all sessions terminate)"]
    G --> H["Container Stopped<br/>(preserved for reuse)"]
    H -->|Project Reopened| D

    I["Project Delete"] --> J["Delete Container"]
    J --> K["Container Removed"]

    style SessionArchitecture fill:#fff8e1,stroke:#ffa000
    style Link1Session fill:#e3f2fd,stroke:#1976d2
    style Link2Session fill:#e8f5e9,stroke:#388e3c

Process 3: WebSocket Connection Flow

sequenceDiagram
    participant Browser as Client Browser
    participant Server as GNS3 Server (API Route)
    participant Container as Docker Container (xpra)

    Browser->>Server: 1. WSS Connect (ws://.../web-wireshark?token=jwt)
    Note right of Server: 2. Validate JWT<br/>(RBAC: Link.Capture)
    Note right of Server: 3. Get container IP<br/>from Docker API
    Server-->>Browser: 4. Accept connection
    Note right of Server: 5. Start WebSocket proxy
    Server->>Container: 6. Connect to xpra<br/>ws://container:port
    Container-->>Server: 7. xpra validates subprotocol
    Note over Browser,Container: 8-9. Bidirectional WebSocket proxy<br/>Browser ⟺ Server ⟺ Container
    Note left of Browser: 10. HTML5 client renders<br/>Wireshark window

WebSocket Endpoint: ws://host/v3/projects/{project_id}/links/{link_id}/capture/web-wireshark?token=<jwt_token>

Process 4: Capture Data Flow

flowchart LR
    subgraph GNS3Node["GNS3 Node"]
        Router["Router / Switch"]
    end

    subgraph ComputeNode["Compute Node"]
        Capture["Link Capture Buffer"]
    end

    Router -->|"TAP (raw packets)"| Capture

    subgraph GNS3Server["GNS3 Server"]
        APIRoute["GET /capture/stream<br/>Proxies pcap stream"]
    end

    Capture -->|"pcap stream"| APIRoute

    subgraph DockerContainer["Docker Container"]
        direction TB
        Curl["curl -N -H 'Authorization: Bearer {jwt}'<br/>'{server}/capture/stream'"]
        Wireshark["wireshark -i - -k"]
        Xvfb["Xvfb :display<br/>(1920x1080x24)"]
        Xpra["xpra :display<br/>ws://0.0.0.0:{port}"]

        Curl -->|"stdin pipe"| Wireshark
        Wireshark -->|"renders to"| Xvfb
        Xvfb -->|"X11 display"| Xpra
    end

    APIRoute -.->|"pcap stream<br/>(curl fetches from container)"| Curl

    subgraph Browser["Client Browser"]
        HTML5Client["HTML5/xpra Client"]
        WSUI["Wireshark Web Interface"]
        HTML5Client --> WSUI
    end

    Xpra <-->|"WebSocket"| HTML5Client

    style GNS3Node fill:#f3e5f5,stroke:#7b1fa2
    style ComputeNode fill:#e8eaf6,stroke:#283593
    style GNS3Server fill:#e8f4fd,stroke:#2196f3
    style DockerContainer fill:#fff3e0,stroke:#ff9800
    style Browser fill:#e8f5e9,stroke:#4caf50

Port Allocation Strategy

Deterministic Port Mapping

flowchart LR
    LinkID["link_id (UUID)"] --> Hash["MD5 Hash"] --> Modulo["hash % 10000"]
    Modulo --> Offset["+ 10000"]
    Offset --> Result["display = port<br/>Range: 10000 - 19999"]

    style LinkID fill:#e3f2fd,stroke:#1976d2
    style Result fill:#e8f5e9,stroke:#388e3c
link_id port/display
f233f27f-7432-49c3-9aa2-50e326a10eec 14503
a1b2c3d4-1234-5678-90ab-cdef12345678 11024
12345678-90ab-cdef-1234-567890abcdef 17892

Benefits:

  • Same link always gets same port (deterministic)
  • Display number = port number (same hash)
  • No port conflicts between sessions
  • Easy to predict and debug

Network Architecture

graph TD
    subgraph HostMachine["Host Machine"]
        subgraph GNS3ServerHost["GNS3 Server<br/>192.168.1.100:3080"]
            WSProxy["WebSocket Proxy"]
        end

        subgraph DockerNetwork["Docker Bridge: gns3-wireshark<br/>Subnet: 172.31.0.0/22"]
            Gateway["Bridge Gateway<br/>172.31.0.1"]
            Container["gns3-wireshark-{project_id}<br/>172.31.0.x"]
            Gateway --- Container
        end
    end

    Client["Client Browser"] -->|"ws://192.168.1.100:3080<br/>/capture/web-wireshark"| WSProxy
    WSProxy -->|"via gateway 172.31.0.1<br/>ws://172.31.0.x:{port}"| Container

    style HostMachine fill:#fafafa,stroke:#9e9e9e
    style DockerNetwork fill:#e3f2fd,stroke:#1976d2
    style GNS3ServerHost fill:#e8f5e9,stroke:#388e3c

Session Management Commands

Note

: These commands are for manual debugging and testing only. The GNS3 server calls WebWiresharkManager directly at runtime.

start

Starts Web Wireshark session for a specific link.

Argument Required Default Description
--project-id Yes - Project UUID
--link-id Yes - Link UUID
--jwt-token Yes - JWT authentication token
--capture-url No auto-detected PCAP stream URL
--image No gns3/web-wireshark:latest Docker image
--memory No 2g Memory limit
--cpus No 1.0 CPU cores
--pids-limit No 1000 Process limit
python manage_wireshark.py start \
  --project-id "5af0fe00-..." \
  --link-id "f233f27f-..." \
  --jwt-token "eyJhbG..."

Other Commands

Command Description Key Arguments
stop Stop session for a specific link --project-id, --link-id
restart Restart session (reopens Wireshark window) --project-id, --link-id, --jwt-token
stop-all Stop all sessions for a project --project-id
delete Delete container (alias for delete-container) --project-id
stop-container Stop container without deleting --project-id
delete-container Delete the container --project-id

Project Close/Delete Workflow

flowchart TD
    A["Project Close"] --> B["_stop_web_wireshark_container()"]
    B --> C["WebWiresharkManager.stop_container(project_id)"]
    C --> D["docker stop<br/>(all xpra/Xvfb/Wireshark terminate)"]
    D --> E["Container Stopped<br/>(preserved for reuse)"]

    E -->|Project Reopened| F["First Capture Start"]
    F --> G["docker start<br/>(container already exists, fast)"]
    G --> H["New xpra sessions created"]

    I["Project Delete"] --> J["_cleanup_web_wireshark_container()"]
    J --> K["WebWiresharkManager.delete_container(project_id)"]
    K --> L["docker rm<br/>(container removed)"]

    style A fill:#e3f2fd,stroke:#1976d2
    style I fill:#ffebee,stroke:#c62828
    style E fill:#fff8e1,stroke:#ffa000
    style L fill:#ffebee,stroke:#c62828

API Endpoints Summary

Method Endpoint Description Privilege
POST /v3/projects/{id}/links/{id}/capture/start Start capture with Web Wireshark Link.Capture
POST /v3/projects/{id}/links/{id}/capture/stop Stop capture Link.Capture
POST /v3/projects/{id}/links/{id}/capture/wireshark/restart Restart Wireshark window Link.Capture
GET /v3/projects/{id}/links/{id}/capture/stream Stream PCAP data Link.Capture
GET /v3/projects/{id}/links/{id}/capture/file Download PCAP file Link.Capture
WS /v3/projects/{id}/links/{id}/capture/web-wireshark WebSocket proxy for xpra Link.Capture

Security Considerations

  1. RBAC Authentication: All endpoints require Link.Capture privilege
  2. JWT Token Validation: WebSocket connections validate JWT token
  3. WebSocket Subprotocol Negotiation: Proper xpra subprotocol handling
  4. Container Isolation: Each project gets its own container with isolated resources
  5. Network Segmentation: Container runs on isolated Docker network (not host network)

Performance Characteristics

Startup & Shutdown Performance

Step Before Optimization After Optimization Improvement
Health Check ~1.0s ~0s Docker native status
Gateway Detection 0.85s ~0.001s Docker API vs exec
Process Cleanup ~850ms ~40ms Host perspective recursive tree walk
Xpra Startup 6.4s ~3s HTML5 client disabled
Wireshark Launch ~1s ~1s No change
Total Startup ~15s ~5-6s 67% faster
Step Before Optimization After Optimization Improvement
Process Termination ~8s ~20-40ms Recursive tree walk, no orphans
File Cleanup ~850ms ~850ms Docker exec (safety)
Total Shutdown ~9s ~2s 78% faster

Startup Breakdown: First vs Subsequent

Phase First Startup (Container stopped) Subsequent Startup (Container running)
Container startup ~1-2s ~0s (already running)
Container health check ~1s (unhealthy→healthy) ~0s (already healthy)
Gateway detection ~0.001s ~0.001s
Process cleanup ~40ms ~40ms
Xpra startup ~3s ~3s
Wireshark launch ~1s ~1s
Total ~6s ~5s

Measured Performance Data

Startup (from production logs):

13:46:53 → 13:46:59 = 6s (first startup with container start)
13:47:38 → 13:47:43 = 5s (subsequent startup, container running)

Shutdown (from production logs):

13:48:15 → 13:48:17 = 2s (complete cleanup, no orphan processes)
13:48:50 → 13:48:52 = 2s (complete cleanup, no orphan processes)

Key Optimizations

  • Complete Process Cleanup: Recursive process tree traversal eliminates orphaned processes (Xvfb, pulseaudio, ibus-daemon)
  • Fast Gateway Detection: Docker API query instead of container exec
  • Smart Health Check: Trust Docker built-in status, no manual ping
  • Xpra Optimization: Disabled unnecessary HTML5 client (--html=off)
  • Reduced Docker Exec Calls: Combined X lock and xpra socket cleanup into single exec call

Docker Exec Performance Limitations

Important: Docker daemon has internal queuing for concurrent exec requests to the same container.

Test Results (Same Container)

Test Scenario Execution Time Avg Per Exec
Single docker exec 0.854s 0.854s
Serial 7 docker exec 7.225s 1.032s
Parallel 7 docker exec 10.253s 1.465s

Key Finding: Parallel execution is 42% slower than serial execution.

Serial:   7.225s  (7 requests processed sequentially)
Parallel: 10.253s (Docker daemon still processes sequentially + context switch overhead)

Impact on Web Wireshark

When stopping multiple capture sessions quickly:

  • Each stop requires 1 docker exec (cleanup files)
  • Docker daemon processes exec requests sequentially
  • 7 sessions × ~1s each = ~7-10 seconds total
  • User requests appear to "queue" even though they're concurrent

Optimization Strategy

Since Docker exec cannot be parallelized effectively:

  1. Minimize exec calls - Already implemented: 2 calls → 1 call
  2. Accept serial processing - No benefit to parallel execution
  3. Focus on fast exec content - Use simple rm -f commands

Future Optimization Options

  • Use Docker API instead of exec (requires container filesystem access)
  • Delay cleanup to next startup (increases startup complexity)
  • Batch multiple stops into single operation (requires API changes)

Resource Usage (Per Wireshark Instance)

Resource Typical Usage
Memory 150-250 MB
CPU 0.5-2% (idle to active)
Threads ~30 threads
Disk I/O Minimal

Container Configuration

Configured via WebWiresharkSettings in gns3server/schemas/config.py:

Parameter Default Recommended Description
enabled true - Enable/disable Web Wireshark feature
image gns3/web-wireshark:latest - Docker image name
network_subnet 172.31.0.0/22 - Docker bridge network subnet
Memory 2GB 2-4GB Container memory limit
CPUs 1.0 1.0-2.0 Container CPU limit
PIDs Limit 1000 1000 Container process limit

Scaling Guidelines

Instances Memory Use Case
1-3 450-750 MB Light projects
4-6 600-1.5 GB Medium projects
7-10 1-2.5 GB Large projects
10+ >2.5 GB Increase memory

Docker API Compatibility

Version Negotiation Mechanism

The Web Wireshark feature implements automatic Docker API version negotiation to ensure compatibility across different Docker versions:

  1. Primary: Try API version 1.44 first

    • Supports Docker 29.3+ (API 1.54+), which requires minimum API 1.44
  2. Fallback: If server rejects 1.44 with 400 error, downgrade to API 1.40

    • Supports Docker 20.10 (API 1.41)

Tested Configurations

Docker Version API Version API 1.40 API 1.44 Solution
20.10 1.41 Fallback to 1.40
29.3+ 1.54+ Use 1.44

Container IP Retrieval

The get_container_ip() method implements a dual-strategy approach for retrieving container IP addresses:

  1. Primary Method: Query Docker Container API

    • Uses docker inspect via HTTP API
    • Safe access to NetworkSettings.Networks field using .get()
    • Handles missing fields gracefully (fixes KeyError bug)
  2. Fallback Method: Execute hostname -I command inside container

    • Works when Docker API response lacks network information
    • Compatible with containers without ip command
    • Returns first IP address from hostname -I output

This dual approach ensures compatibility across different Docker API versions that may have varying response formats.


Known Limitations

  1. JWT Token Visibility: Token passed via command-line arguments (visible in /proc/<pid>/cmdline)
  2. Single Container: All Wireshark instances run in a single container per project
  3. Docker Dependency: Requires Docker daemon running on the server
  4. Browser Support: Requires modern browser with WebSocket support
  5. Port Range: Limited to 10,000 unique ports (10000-19999)

File Structure

gns3server/
├── controller/
│   ├── link.py                    # Link capture lifecycle
│   └── project.py                 # Project cleanup hooks
├── api/routes/controller/
│   └── links.py                   # REST/WebSocket API endpoints
└── agent/web_wireshark/
    ├── setup_wireshark_image.py   # Docker image setup tool
    ├── manage_wireshark.py        # CLI management tool (manual/debug use only)
    ├── manager.py                 # Session management logic (called by server)
    ├── docker_client.py           # Docker API client
    ├── stats.py                   # Container statistics collection
    ├── docker/
    │   └── Dockerfile            # Container image definition
    └── WEB_WIRESHARK.md          # Technical documentation