7205 Commits

Author SHA1 Message Date
Jeremy Grossmann
555c3ecab5
Merge pull request #2845 from yueguobin/fix/iou-serial-capture-linktype
Fix IOU serial-link captures always written as Ethernet (DLT_EN10MB)
2026-08-06 19:07:22 +02:00
YueGuobin
17020b7f7b
iou: forward data_link_type on capture start so serial pcaps use the correct linktype
The IOU compute capture/start route received data_link_type in the NodeCapture body but dropped it when calling node.start_capture(), so iou_vm.start_capture always defaulted to DLT_EN10MB -- every IOU serial capture (Cisco HDLC / PPP / Frame Relay) was written as an Ethernet pcap. Dynamips, cloud and the L2 switches already forward this value; IOU was the only serial-capable node that omitted it. One-argument fix: the node method already accepts and forwards data_link_type to 'iol_bridge start_capture', so only the route was missing it.

Verified: IOU serial captures now produce the expected linktype (C_HDLC / PPP_SERIAL / FRELAY) instead of Ethernet.
2026-08-07 00:35:39 +08:00
Jeremy Grossmann
deba9f8409
Merge pull request #2834 from yueguobin/mcp-tool-descriptions-enhancement
Improve MCP tool descriptions and add traffic-insight marker tools
2026-07-26 19:09:05 +02:00
YueGuobin
309d388b0b
Add MCP tools for traffic-insight marker feature
Add 2 new MCP tools to expose the marker (traffic-insight) REST API:

- link_marker: per-link marker CRUD (create/update/delete)
  POST/PUT/DELETE /projects/{pid}/links/{lid}/markers
- marker_definition: project-level marker definition CRUD (create/update/delete/list)
  POST/PUT/DELETE/GET /projects/{pid}/marker-definitions
  Create auto-fans out global-{name} to every link

Read operations use existing link_get (returns markers dict).
2026-07-26 13:01:06 +08:00
YueGuobin
a8c6546c44
Improve MCP tool descriptions for link_update, device_show_run, and link_reset
- link_update: add bidirectional filter effect note, packet loss formula
  (packet_loss [50] ≈ 75% observed), filter clearing syntax (filters: {}),
  and ARP-also-filtered warning with static ARP recommendation
- device_show_run: add prerequisite section — device_type:<type> tag
  required, Docker/Linux nodes unsupported (use node_console)
- link_reset: clarify that filter state machines (e.g. frequency_drop
  counters) restart while filter configuration is preserved
2026-07-26 12:50:14 +08:00
Jeremy Grossmann
f1be5eae9b
Merge pull request #2829 from cristian-ciobanu/docker-pull-image
Add controller and compute API support for explicitly pulling or updating Docker images
2026-07-24 20:14:55 +02:00
Cristi
2abc7e09a3 Adds controller and compute API support for explicitly pulling or updating Docker images. 2026-07-24 16:40:20 +03:00
Jeremy Grossmann
28f883b46a
Merge pull request #2828 from GNS3/set-ulimit-pytest
feat(tests): increase maximum open file descriptors for test runs on Unix
2026-07-23 18:05:19 +02:00
grossmj
c010be1a02
feat(tests): increase maximum open file descriptors for test runs on Unix 2026-07-23 18:00:11 +02:00
Jeremy Grossmann
2b949b1a77
Merge pull request #2826 from cristian-ciobanu/iol-l1-keepalive
(feat): Implement functional Layer 1 keepalive support for IOU/IOL nodes
2026-07-22 18:10:42 +02:00
Cristian Ciobanu
ac003c0a71
Merge branch 'GNS3:master' into iol-l1-keepalive 2026-07-22 11:50:09 +03:00
Cristi
dd497e4308 (feat): Implement functional Layer 1 keepalive support for IOU/IOL nodes 2026-07-22 10:47:09 +03:00
grossmj
7bc9ae4aa8
Fix typo with ovmf_firmware_dir 2026-07-21 17:34:05 +02:00
grossmj
1f6e00d29f
Fix issues after merging 2026-07-21 17:20:46 +02:00
grossmj
0091f2c64a
Merge remote-tracking branch 'origin/3.1' into 3.1 2026-07-21 17:04:35 +02:00
grossmj
7487ec14e0
Merge branch '2.2' into 3.1
# Conflicts:
#	CHANGELOG
#	conf/gns3_server.conf
#	gns3server/compute/docker/__init__.py
#	gns3server/compute/docker/docker_vm.py
#	gns3server/compute/qemu/qemu_vm.py
#	gns3server/controller/import_project.py
#	gns3server/crash_report.py
#	gns3server/version.py
2026-07-21 17:03:00 +02:00
Jeremy Grossmann
e90f250294
Merge pull request #2823 from yueguobin/feature/cloud-node-interface-refresh
fix(controller): refresh cloud/nat node interfaces from compute on GET, plus NAT interface info
2026-07-19 12:49:07 +02:00
YueGuobin
977d9628e5
fix(builtin): hide GNS3 internal bridge interfaces from Cloud node
Filter out host interfaces whose name starts with 'gns3' (e.g.
EthernetSwitch kernel bridges) from Cloud.asdict() interfaces list.
Other special interfaces (virbr0, docker0, etc.) remain visible.
2026-07-19 18:21:22 +08:00
YueGuobin
4943567962
feat(builtin): NAT node returns its interface IP info in asdict()
NAT now includes an 'interfaces' field in its response, filtered to
contain only the mapped NAT interface (virbr0 on Linux, vmnet8 on
macOS/Windows). This lets connected nodes discover the NAT subnet
and gateway address without needing to list all host interfaces.

The field format mirrors Cloud.asdict(): name, type, special, and
ip_addresses list (both IPv4 and IPv6).
2026-07-19 18:10:51 +08:00
YueGuobin
ddf7d4ca60
fix(controller): refresh cloud/nat node interfaces from compute on GET
Cloud and NAT nodes need live host network interface data. Previously,
GET /projects/{project_id}/nodes/{node_id} returned cached properties
from creation time, so newly added host interfaces (e.g. kernel bridges
created by EthernetSwitch nodes) were invisible until the node was
deleted and recreated.

Now the controller fetches fresh data from the compute node before
returning the response, so host interface changes are reflected
immediately. Falls back to cached data if compute is unreachable.
2026-07-19 17:41:37 +08:00
Jeremy Grossmann
e1b09e683e
Merge pull request #2822 from yueguobin/feature/ethernet-switch-ubridge-brctl
POC: migrate builtin EthernetSwitch from Dynamips ethsw to ubridge brctl
2026-07-17 23:49:01 +02:00
YueGuobin
7c320d6100
docs: add builtin Ethernet Switch uBridge brctl implementation document 2026-07-18 01:27:53 +08:00
YueGuobin
18a123e0b9
refactor(builtin): use deterministic bridge name from switch UUID
Replace psutil.net_if_addrs() scan with a deterministic name derived from
the switch's node UUID: gns3 + first 6 hex chars (10 chars, fits kernel
IFNAMSIZ limit of 15). Taps: <bridge>-<port> (12-13 chars).

Crash recovery: brctl delete the bridge first (best-effort), then create
fresh. Stale bridges from abnormal gns3server shutdown are automatically
reclaimed on the next start — no EEXIST or leaked interfaces.

Remove _free_iface helper and psutil import (no longer needed).
2026-07-18 01:18:32 +08:00
YueGuobin
aabbee3a04
fix(builtin): bring kernel bridge UP after creation
brctl create leaves the bridge administratively DOWN. Add link set up
so the bridge actually forwards frames between enslaved ports.

Also record Docker iptables FORWARD DROP pitfall in project memory.
2026-07-18 01:11:25 +08:00
YueGuobin
fd4ac4460d
feat: migrate builtin EthernetSwitch from Dynamips ethsw to ubridge brctl
Replace the builtin EthernetSwitch stub with a Linux kernel bridge backed
by uBridge's brctl module. Each switch node creates one kernel bridge
(gns3br{N}) with VLAN filtering; each port is a persistent TAP enslaved to
the bridge, relayed by a per-port uBridge bridge (nio_tap <-> nio_udp).

- Access/dot1q/qinq port modes translated to brctl vlan primitives
- Compute router repointed from Dynamips to Builtin manager
- Tests updated: 21 router-level tests + 215 surrounding tests pass
- Real-kernel e2e verified: access 100 PVID untagged, dot1q trunk
  VIDs 1-4094 + native 1, qinq 802.1ad proto + 200 PVID

Ethertype 0x9100/0x9200 handling and port-count guard relaxation are
deferred pending resolution.
2026-07-18 00:21:19 +08:00
Jeremy Grossmann
22a1026633
Merge pull request #2821 from yueguobin/fix/mcp-remove-node-reload
fix(mcp): remove unreliable node_reload / node_reload_all tools
2026-07-16 12:51:29 +02:00
YueGuobin
37867a1e44
fix(mcp): remove unreliable node_reload / node_reload_all tools
For Docker nodes, reload bottoms out as a raw POST /containers/{id}/restart
to the Docker daemon, bypassing GNS3's start/stop lifecycle (uBridge
re-attach, console servers, NIC setup). The container restarts at the
Docker level but GNS3's plumbing goes out of sync, and the daemon call can
block up to the controller's 240s timeout — manifesting as "reload hangs /
no response". stop+start runs the full lifecycle and is reliable.

Remove the node_reload and node_reload_all MCP tools (tool functions,
handlers, NODE_TOOLS entry, tests, docs). The underlying REST endpoints
(POST /nodes/{id}/reload, POST /nodes/reload) are kept for native API
users. MCP callers should use node_stop + node_start (partial) or
close/open project (full restart) instead.
2026-07-16 13:27:21 +08:00
Jeremy Grossmann
013ca442f4
Merge pull request #2816 from yueguobin/feature/traffic-insight-markers
feat: add traffic-insight markers (ubridge mark filter integration)
2026-07-15 22:12:11 +02:00
grossmj
c75e1ba0be
Development on 2.2.61.dev1 2026-07-15 20:45:33 +02:00
Jeremy Grossmann
c1e845cb22
Merge pull request #2819 from GNS3/release/v2.2.60
Release v2.2.60
2026-07-15 20:44:04 +02:00
Jeremy Grossmann
3954d6477a
Merge pull request #2817 from yueguobin/fix/mcp-rbac-token-hardening
fix: thread token_version through MCP token minting and guard websocket auth failure
2026-07-15 19:20:44 +02:00
Guobin Yue
64da926d2f
Merge branch '3.1' into fix/mcp-rbac-token-hardening 2026-07-16 01:03:11 +08:00
Guobin Yue
2e9073be39
Merge branch '3.1' into feature/traffic-insight-markers 2026-07-16 01:03:06 +08:00
Jeremy Grossmann
3e12ddf321
Merge pull request #2818 from yueguobin/docs/api-testing-skill
docs: add a Claude Code skill with curl patterns for testing the REST API
2026-07-15 18:57:19 +02:00
YueGuobin
dc7f5d76f4
docs(api-testing): add curl-based GNS3 API testing skill 2026-07-16 00:54:33 +08:00
YueGuobin
884c40038c
docs(mcp): note --no-close for node_console websocat usage
A heredoc (<<<) closes stdin at once, so websocat dropped the WebSocket
before the device's reply arrived. Add --no-close to the node_console usage
examples and the returned command field so the connection stays open while
output is read.
2026-07-16 00:51:45 +08:00
YueGuobin
d4389b7068
fix(rbac): guard None current_user on websocket auth failure
get_current_active_user_from_websocket returns None after closing the socket
on an auth failure (revoked token, bad credentials, inactive user).
has_privilege_on_websocket dereferenced current_user.is_superadmin without a
None check, so any websocket auth failure surfaced as an AttributeError
traceback instead of a clean close. Bail out early when current_user is None,
mirroring the guard already present in ws_console.
2026-07-16 00:51:45 +08:00
YueGuobin
3d6d9a3396
fix(mcp): thread token_version into console/download token minting
Token revocation is a strict version check (token_data.token_version !=
user.token_version). create_access_token defaults token_version to 0, so the
short-lived JWTs minted for the console WebSocket URL (nodes) and the download
URLs (symbols, links) carried ver=0. Any user who had logged out at least once
(token_version >= 1, e.g. the default admin) got tokens rejected as "revoked"
on first use.

e433991cf fixed this in _resolve_token's API-key branch but missed these three
independent minting sites. Now resolve token_version during _resolve_token (the
JWT branch decodes it, the API-key branch reads user.token_version), carry it
through gns3_ctx, and pass it at every minting call.
2026-07-16 00:51:45 +08:00
YueGuobin
89e11d265f
fix(mcp): pass user.token_version when generating temp JWT from API key
_resolve_token generated a temp JWT with a hardcoded ver=0 after
validating the API key. Users who had logged out at least once
(token_version >= 1) would hit "Token has been revoked" 401 on
every MCP tool call, because the REST auth chain rejects ver=0
when the user's token_version no longer matches.

Fix: pass the user's actual token_version to create_access_token
so the temp JWT carries the correct ver claim.
2026-07-16 00:51:45 +08:00
YueGuobin
45d18de8af
fix(marker): avoid reentrant-lock deadlock when uBridge starts lazily
_start_ubridge configures the marker sink via _ubridge_configure_marker_sink, which used _ubridge_send. But _start_ubridge itself is reached THROUGH _ubridge_send when uBridge is started lazily — e.g. linking a stopped node. _ubridge_send's @locking lock (___ubridge_send_lock) is non-reentrant, so the nested _ubridge_send('marker sink') deadlocked forever, making the NIO create POST time out after 120s. Started nodes never hit this (uBridge already running, no _start_ubridge), so only stopped-node links (notably dynamips) hung.

_ubridge_configure_marker_sink now talks to self._ubridge_hypervisor.send directly: it runs inside _start_ubridge right after connect(), so uBridge is already up and the raw send is safe, with no reentrant lock acquisition.
2026-07-16 00:39:52 +08:00
YueGuobin
08f4b5ae0d
docs(marker): document per-link attribution and IOU multi-interface
Add a Per-link attribution section (link field semantics, pcap path keyed on link) with an IOU subsection explaining the shared IOL-BRIDGE and per-port filter lists. Note that the marker.match link_id comes from the signal link= field, and call out shared capture-side node behavior in Notes.
2026-07-16 00:39:52 +08:00
YueGuobin
794bfef450
fix(marker): per-link attribution via link field in MARK signals
A uBridge MARK signal carries only node= and filter= (no bridge/link field), so when one node is the capture side for several links that share a marker name (always the case for global-{name} definitions on a multi-interface node) the signals were indistinguishable and the (node, filter) registry collapsed them to a single link.

The mark filter is now stamped with its link id (mark <bpf> ... link <link_id>); uBridge echoes it verbatim (link=<link_id>) and the listener uses the signal's link= as the authoritative link_id of the marker.match event, falling back to the registry only for legacy signals without it. base_node and iou apply paths pass link_id; covered by two new listener tests.
2026-07-16 00:39:52 +08:00
YueGuobin
27356da62d
fix(marker): set nio.markers on iou/dynamips/cloud nio update
The PUT /nio (update) endpoints for iou, dynamips and cloud copied nio.filters from the request body but never copied nio.markers. So adding a marker (e.g. a project-level global definition) to an already-created link of these node types left nio.markers empty: no mark filter was installed on uBridge and no marker.match signal was emitted. vpcs/qemu/docker already set it; mirror them (getattr for the Union NIO types).
2026-07-16 00:39:52 +08:00
YueGuobin
a732d8d521
test(marker): include marker_definitions in project-asdict expectations
project.asdict() and project_to_topology() gained a marker_definitions key.
Two tests that assert exact dict equality needed the new field.
2026-07-16 00:39:52 +08:00
YueGuobin
213738f0a5
test(marker): add controller + API route tests for traffic-insight
19 controller-layer tests covering start/stop/update_marker (storage,
inheritance guards + bypasses, partial-update preservation of render
hints), project-def CRUD (fan-out, sync, delete-cleanup regression),
apply_defs_to_new_link, persist_markers/asdict, and aggregation.

15 API-route tests covering per-link create/update/delete (201/200/204),
global-prefix rejection on create (409 regression), bad-format rejection
(422), PUT/DELETE-on-inherited guard (409 regression), project-def CRUD
endpoints, and the aggregation view.

All 34 tests pass when run as part of the full suite.
2026-07-16 00:39:52 +08:00
YueGuobin
cbb4854d3d
docs(marker): add traffic-insight feature documentation
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.
2026-07-16 00:39:52 +08:00
YueGuobin
b21780605e
fix(marker): reject reserved global-prefix names only on create
The global-prefix reservation belongs on the create path, where it keeps
user-chosen names disjoint from inherited global-{name} markers. It was
implemented as a field_validator on MarkerCreate / MarkerDefinitionCreate,
which also back the PUT update bodies — so updating an inherited marker
while echoing its name in the body tripped the validator with a generic
422 before the controller could return the actionable 409 ("inherited,
use the marker-definitions API").

Drop the schema validators and enforce the prefix in the two create routes
(ControllerError -> 409). PUT no longer validates the body name (ignored
anyway — the target is the {name} path param), so editing an inherited
marker now reaches the controller's inheritance guard and returns the
clear 409. The name-format regex stays on the schema (still 422).
2026-07-16 00:39:52 +08:00
YueGuobin
cdc27c37a7
fix(marker): clean inherited markers on def delete
delete_marker_definition removed the def but left the inherited global-*
copies on every link: it called stop_marker(), which rejects inherited
markers (409), and the ControllerError was swallowed as a warning. The
orphaned copies were in-memory only (_persist_markers filters inherited
markers) so a restart hid the symptom, but during a running session they
were undeletable via either the per-link or project API.

Add the same inherited=True bypass that update_marker already has, and
pass it from the def-delete fan-out so the copies are removed for real.
2026-07-16 00:39:52 +08:00
YueGuobin
98bcd5eddd
feat(marker): add highlight_duration render hint
Per-marker UI hint (milliseconds, ge=1) for how long the Web UI keeps a
marker highlighted after a match. Mirrors color: stored on the link,
persisted in the topology, inherited via project-level definitions, and
never sent to uBridge. Omitted = null = frontend uses its own default.

The default is intentionally NOT set in the schema: MarkerCreate also
backs PUT updates, so a schema default would make every partial update
silently reset the value. None means "not provided" (keep existing on
update / frontend decides on create).
2026-07-16 00:39:52 +08:00
YueGuobin
179f072c33
fix(marker): fix inheritance hook, topology load, update sync, and asdict
Bugs found via end-to-end testing of project-level marker definitions:

1. New links didn't inherit — apply_defs_to_new_link is async but was
   called without await in UDPLink.create().

2. Project load crashed — load_project passed marker_definitions to
   Project.__init__. Now popped in load_project and restored separately
   in Project.open() (it backs a read-only property).

3. PUT on a definition didn't sync to links — update_marker's guard
   rejected even the project-layer sync call. Added an `inherited`
   bypass flag used by update_marker_definition.

4. _ubridge_add_marker_filter raised re.PatternError — the name regex
   used (?i)(?!global) look-around, invalid in Python's re module.
   Dropped the prefix check there: "global-*" names are legitimate at
   the uBridge boundary (inherited definitions); forbidden only at the
   user-facing schema.

5. GET /links hid inherited markers — asdict()'s runtime branch used
   _persist_markers() (which filters inherited markers). Restored
   self._markers for the runtime branch; only the topology_dump branch
   filters (inherited markers are rebuilt from definitions on load).

6. Duplicate "already exists" warnings on project open — open() fanned
   out definitions to all links, but UDPLink.create() had already done
   so via its inheritance hook. Removed the redundant fan-out in open().
2026-07-16 00:39:52 +08:00