From 8ba1f064d2bb8187e8ab0c548bd707afaa0ea2e5 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sun, 24 May 2026 21:25:46 +0800 Subject: [PATCH 1/6] feat: add packet filter management tool for GNS3-Copilot fault injection Add comprehensive packet filter management functionality to GNS3-Copilot, enabling AI-powered fault injection scenarios with network simulation capabilities like latency, packet loss, and corruption. ## Changes ### New Features - **GNS3PacketFilterTool**: New LangChain tool for managing packet filters on GNS3 links with support for delay, packet loss, corruption, frequency_drop, and BPF filtering - Actions: get_available, set, get, clear - Integrated into troubleshooting_injection mode for fault scenarios ### API Integration - **Link.available_filters()**: Added method to custom_gns3fy.py Link class - Queries available filter types for specific links - API v3+ only (raises ValueError for v2 connectors) - Returns filter definitions with parameters and constraints ### Tool Integration - Added GNS3PacketFilterTool to TROUBLESHOOTING_INJECTION_MODE_TOOLS - Positioned as 3rd tool in fault injection workflow - Optimized for troubleshooting practice scenarios ## Files Modified - gns3server/agent/gns3_copilot/agent/gns3_copilot.py - gns3server/agent/gns3_copilot/gns3_client/custom_gns3fy.py - gns3server/agent/gns3_copilot/tools_v2/__init__.py ## Files Added - gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py ## Testing - All validation tests passed - Version checking verified (v3+ only) - Tool integration confirmed in troubleshooting mode --- .../agent/gns3_copilot/agent/gns3_copilot.py | 2 + .../gns3_copilot/gns3_client/custom_gns3fy.py | 60 +++ .../agent/gns3_copilot/tools_v2/__init__.py | 4 + .../tools_v2/gns3_packet_filter.py | 365 ++++++++++++++++++ 4 files changed, 431 insertions(+) create mode 100644 gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py diff --git a/gns3server/agent/gns3_copilot/agent/gns3_copilot.py b/gns3server/agent/gns3_copilot/agent/gns3_copilot.py index a675d2074..b5173668c 100644 --- a/gns3server/agent/gns3_copilot/agent/gns3_copilot.py +++ b/gns3server/agent/gns3_copilot/agent/gns3_copilot.py @@ -88,6 +88,7 @@ from gns3server.agent.gns3_copilot.tools_v2 import ( ) from gns3server.agent.gns3_copilot.tools_v2 import GNS3CreateNodeTool from gns3server.agent.gns3_copilot.tools_v2 import GNS3LinkTool +from gns3server.agent.gns3_copilot.tools_v2 import GNS3PacketFilterTool from gns3server.agent.gns3_copilot.tools_v2 import GNS3StartNodeTool from gns3server.agent.gns3_copilot.tools_v2 import GNS3StopNodeTool from gns3server.agent.gns3_copilot.tools_v2 import GNS3SuspendNodeTool @@ -144,6 +145,7 @@ LAB_AUTOMATION_ASSISTANT_MODE_TOOLS = [ TROUBLESHOOTING_INJECTION_MODE_TOOLS = [ ExecuteMultipleDeviceCommands(), # Get device configurations (READ-ONLY) ExecuteMultipleDeviceConfigCommands(), # Inject configuration changes + GNS3PacketFilterTool(), # Manage packet filters on links (delay, loss, corrupt, etc.) InjectionSkillsTool(), # Query injection skills and fault types GNS3TopologyTool(), # Get topology information ] diff --git a/gns3server/agent/gns3_copilot/gns3_client/custom_gns3fy.py b/gns3server/agent/gns3_copilot/gns3_client/custom_gns3fy.py index cb3770d9f..d565975e7 100644 --- a/gns3server/agent/gns3_copilot/gns3_client/custom_gns3fy.py +++ b/gns3server/agent/gns3_copilot/gns3_client/custom_gns3fy.py @@ -1042,6 +1042,66 @@ class Link: # Update object self._update(_response.json()) + @verify_connector_and_id + def available_filters(self) -> list[dict[str, Any]]: + """ + Gets the list of available packet filters for this link. + + **NOTE:** This endpoint is only available in GNS3 API v3 or later. + Attempting to call this method with a v2 connector will raise an error. + + **Required Attributes:** + + - `project_id` + - `connector` (must be API v3 or later) + - `link_id` + + **Returns:** + + List of available filter types with their parameters (e.g., frequency_drop, + packet_loss, delay, corrupt, bpf). + + **Example:** + + ```python + >>> link = Link(project_id=, link_id=, connector=) + >>> filters = link.available_filters() + >>> print(filters) + [ + { + "type": "frequency_drop", + "name": "Frequency drop", + "description": "It will drop everything with a -1 frequency...", + "parameters": [...] + }, + ... + ] + ``` + """ + _conn = self.connector + _project_id = self.project_id + + if _conn is None: + raise ValueError("Gns3Connector not assigned under 'connector'") + if _project_id is None: + raise ValueError("Need to submit project_id") + + # Check API version - available_filters endpoint is only available in v3+ + if not hasattr(_conn, "api_version") or _conn.api_version < 3: + raise ValueError( + "The available_filters() method requires GNS3 API v3 or later. " + f"Current connector version: v{getattr(_conn, 'api_version', 2)}. " + "Please use api_version=3 when creating the Gns3Connector." + ) + + _url = ( + f"{_conn.base_url}/projects/{_project_id}/links/{self.link_id}/" + "available_filters" + ) + _response = _conn.http_call("get", _url) + + return cast(list[dict[str, Any]], _response.json()) + @dataclass(config=config) class Node: diff --git a/gns3server/agent/gns3_copilot/tools_v2/__init__.py b/gns3server/agent/gns3_copilot/tools_v2/__init__.py index f99450be4..49957fc79 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/__init__.py +++ b/gns3server/agent/gns3_copilot/tools_v2/__init__.py @@ -32,6 +32,7 @@ This package provides various tools for interacting with GNS3 network simulator: - Multiple device command execution using Nornir - VPCS device configuration using Netmiko - Node and link management +- Packet filter management Main modules: - config_tools_nornir: Multiple device configuration command execution tool using Nornir @@ -42,6 +43,7 @@ Main modules: - gns3_start_node: GNS3 node startup tool - gns3_get_node_temp: GNS3 template retrieval tool - gns3_update_node_name: GNS3 node name update tool +- gns3_packet_filter: GNS3 packet filter management tool Note: GNS3TopologyTool is now available from gns3_client package @@ -54,6 +56,7 @@ from .display_tools_nornir import ExecuteMultipleDeviceCommands from .gns3_create_link import GNS3LinkTool from .gns3_create_node import GNS3CreateNodeTool from .gns3_get_node_temp import GNS3TemplateTool +from .gns3_packet_filter import GNS3PacketFilterTool from .gns3_start_node import GNS3StartNodeQuickTool from .gns3_start_node import GNS3StartNodeTool from .gns3_stop_node import GNS3StopNodeTool @@ -79,6 +82,7 @@ __all__ = [ "ExecuteMultipleDeviceCommands", "GNS3CreateNodeTool", "GNS3LinkTool", + "GNS3PacketFilterTool", "GNS3StartNodeTool", "GNS3StartNodeQuickTool", "GNS3StopNodeTool", diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py new file mode 100644 index 000000000..e2a8dee49 --- /dev/null +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py @@ -0,0 +1,365 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# GNS3-Copilot - AI-powered Network Lab Assistant for GNS3 +# +# This file is part of GNS3-Copilot project. +# +# GNS3-Copilot is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# GNS3-Copilot is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNS3-Copilot. If not, see . +# +# Copyright (C) 2025 Yue Guobin (岳国宾) +# Author: Yue Guobin (岳国宾) +# +# Project Home: https://github.com/yueguobin/gns3-copilot +# + +""" + +GNS3 packet filter management tool for network simulation. + +Provides functionality to manage packet filters on GNS3 links, +including latency, packet loss, corruption, and BPF filtering. +""" + +import json +import logging +from pprint import pprint +from typing import Any + +from langchain.tools import BaseTool +from langchain_core.callbacks import CallbackManagerForToolRun + +from gns3server.agent.gns3_copilot.gns3_client import Link +from gns3server.agent.gns3_copilot.gns3_client import get_gns3_connector + +# Configure logging +logger = logging.getLogger(__name__) + + +class GNS3PacketFilterTool(BaseTool): + """ + A LangChain tool to manage packet filters on GNS3 links. + + Supports getting available filters, setting filters, and clearing filters + on network links to simulate various network conditions. + + **Input:** + A JSON object with project_id, link_id, action, and optional filter parameters. + + Example input for getting available filters: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "get_available" + } + + Example input for setting filters: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "set", + "filters": { + "delay": [100, 10], + "packet_loss": [5] + } + } + + Example input for getting current filters: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "get" + } + + Example input for clearing filters: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "clear" + } + + **Output:** + A dictionary containing the action result. + + For "get_available": returns list of available filter types + For "set": returns updated link information with applied filters + For "get": returns current filters configured on the link + For "clear": returns confirmation that filters were cleared + """ + + name: str = "manage_gns3_packet_filter" + description: str = """ + Manages packet filters on GNS3 links to inject network faults and simulate network conditions. + + This tool is primarily used for fault injection scenarios to create realistic network problems + for troubleshooting practice, such as latency, packet loss, and corruption. + + Supported actions: + - "get_available": Get list of available filter types for the link + - "set": Set packet filters on the link to inject network faults + - "get": Get current filters configured on the link + - "clear": Clear all filters from the link (remove injected faults) + + Common filter types for fault injection: + - "frequency_drop": Drop every Nth packet (parameter: frequency, -1 to 32767) + - "packet_loss": Packet loss percentage (parameter: chance, 0-100) + - "delay": Delay in ms with optional jitter (parameters: latency 0-32767, jitter 0-32767) + - "corrupt": Packet corruption percentage (parameter: chance, 0-100) + - "bpf": Berkeley Packet Filter (parameter: filter expression text) + + Input is a JSON object with: + - project_id (str): GNS3 project UUID + - link_id (str): GNS3 link UUID + - action (str): One of "get_available", "set", "get", "clear" + - filters (dict, optional): Filter configuration for "set" action + + Example for getting available filters: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "get_available" + } + + Example for setting delay and packet loss: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "set", + "filters": { + "delay": [100, 10], + "packet_loss": [5] + } + } + + Example for clearing filters: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "clear" + } + + Returns a dictionary with action result, filter information, or error message. + """ + + def _run( + self, + tool_input: str, + run_manager: CallbackManagerForToolRun | None = None, + **kwargs: Any, + ) -> dict[str, Any]: + """ + Manages packet filters on a GNS3 link. + + Args: + tool_input: A JSON string with project_id, link_id, action, and optional filters. + run_manager: LangChain run manager (unused). + + Returns: + dict: A dictionary with action result or an error message. + """ + # Log received input + logger.info("Received input: %s", tool_input) + + try: + # Parse input JSON + input_data = json.loads(tool_input) + project_id = input_data.get("project_id") + link_id = input_data.get("link_id") + action = input_data.get("action") + + # Validate required fields + if not project_id: + logger.error("Invalid input: Missing project_id.") + return {"error": "Missing project_id."} + + if not link_id: + logger.error("Invalid input: Missing link_id.") + return {"error": "Missing link_id."} + + if not action: + logger.error("Invalid input: Missing action.") + return {"error": "Missing action."} + + # Validate action + valid_actions = ["get_available", "set", "get", "clear"] + if action not in valid_actions: + logger.error("Invalid action: %s. Must be one of %s", action, valid_actions) + return { + "error": f"Invalid action: {action}. Must be one of {valid_actions}" + } + + # Validate filters for "set" action + if action == "set": + filters = input_data.get("filters") + if not filters or not isinstance(filters, dict): + logger.error("Invalid input: 'set' action requires 'filters' dict.") + return { + "error": "'set' action requires 'filters' dict with filter configuration." + } + + # Initialize Gns3Connector using factory function + logger.info("Connecting to GNS3 server...") + gns3_server = get_gns3_connector() + + if gns3_server is None: + logger.error("Failed to create GNS3 connector") + return { + "error": "Failed to connect to GNS3 server. " + "Please check your configuration." + } + + # Create Link object + logger.info( + "Processing packet filter action '%s' for link %s...", action, link_id + ) + link = Link( + project_id=project_id, link_id=link_id, connector=gns3_server + ) + + # Execute action + if action == "get_available": + result = self._get_available_filters(link) + elif action == "set": + filters = input_data.get("filters", {}) + result = self._set_filters(link, filters) + elif action == "get": + result = self._get_filters(link) + elif action == "clear": + result = self._clear_filters(link) + else: + result = {"error": f"Unknown action: {action}"} + + # Log result + logger.info("Packet filter action '%s' completed successfully.", action) + + return result + + except json.JSONDecodeError as e: + logger.error("Invalid JSON input: %s", e) + return {"error": f"Invalid JSON input: {e}"} + except Exception as e: + logger.error("Failed to process packet filter request: %s", e) + return { + "error": f"Failed to process packet filter request: {str(e)}" + } + + def _get_available_filters(self, link: Link) -> dict[str, Any]: + """Get available filter types for the link.""" + try: + filters = link.available_filters() + logger.info("Retrieved %d available filter types.", len(filters)) + return { + "action": "get_available", + "link_id": link.link_id, + "available_filters": filters, + "count": len(filters), + "status": "success", + } + except Exception as e: + logger.error("Failed to get available filters: %s", e) + return { + "action": "get_available", + "link_id": link.link_id, + "error": f"Failed to get available filters: {str(e)}", + "status": "failed", + } + + def _set_filters(self, link: Link, filters: dict[str, Any]) -> dict[str, Any]: + """Set packet filters on the link.""" + try: + # Update filters + link.update(filters=filters) + + # Get updated link info + link.get() + + logger.info("Successfully set filters on link %s", link.link_id) + return { + "action": "set", + "link_id": link.link_id, + "filters": link.filters, + "status": "success", + "message": "Filters applied successfully", + } + except Exception as e: + logger.error("Failed to set filters: %s", e) + return { + "action": "set", + "link_id": link.link_id, + "error": f"Failed to set filters: {str(e)}", + "status": "failed", + } + + def _get_filters(self, link: Link) -> dict[str, Any]: + """Get current filters configured on the link.""" + try: + # Get link information + link.get() + + logger.info("Retrieved current filters for link %s", link.link_id) + return { + "action": "get", + "link_id": link.link_id, + "filters": link.filters, + "status": "success", + } + except Exception as e: + logger.error("Failed to get filters: %s", e) + return { + "action": "get", + "link_id": link.link_id, + "error": f"Failed to get filters: {str(e)}", + "status": "failed", + } + + def _clear_filters(self, link: Link) -> dict[str, Any]: + """Clear all filters from the link.""" + try: + # Clear filters by setting empty dict + link.update(filters={}) + + # Get updated link info to confirm + link.get() + + logger.info("Successfully cleared filters on link %s", link.link_id) + return { + "action": "clear", + "link_id": link.link_id, + "filters": link.filters, + "status": "success", + "message": "Filters cleared successfully", + } + except Exception as e: + logger.error("Failed to clear filters: %s", e) + return { + "action": "clear", + "link_id": link.link_id, + "error": f"Failed to clear filters: {str(e)}", + "status": "failed", + } + + +if __name__ == "__main__": + # Test the tool locally + # TODO: Replace with actual project and link UUIDs + test_input = json.dumps( + { + "project_id": "d7fc094c-685e-4db1-ac11-5e33a1b2e066", + "link_id": "link-uuid-here", + "action": "get_available", + } + ) + + tool = GNS3PacketFilterTool() + result = tool._run(test_input) + pprint(result) From dc04c29e7e24f4aab5109aa7c5060ef11607e593 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sun, 24 May 2026 21:54:50 +0800 Subject: [PATCH 2/6] docs: add GNS3 appliance loading mechanism to memory Document how GNS3 loads appliance files from builtin and custom directories with priority rules, including storage locations and the design rationale that allows users to customize devices without losing changes during registry updates. Co-Authored-By: Claude Sonnet 4.6 --- .claude/memory/MEMORY.md | 3 +++ .claude/memory/gns3-appliance-loading.md | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 .claude/memory/gns3-appliance-loading.md diff --git a/.claude/memory/MEMORY.md b/.claude/memory/MEMORY.md index b1891785a..9451a0f36 100644 --- a/.claude/memory/MEMORY.md +++ b/.claude/memory/MEMORY.md @@ -16,5 +16,8 @@ - Key point: UDPLink only passes through jwt_token, ultimately used by curl command inside Web Wireshark container to authenticate with GNS3 capture stream API - **[Xpra HTML5 Client](./xpra-html5-client.md)** - Xpra HTML5 client menu control parameters for customizing the web interface +### Appliance Management +- **[GNS3 Appliance Loading](./gns3-appliance-loading.md)** - How GNS3 loads appliance files from builtin and custom directories with priority rules + ### uBridge Permission - **[uBridge Permission Issue](./gns3-ubridge-permission.md)** - Docker containers fail to start due to missing CAP_NET_ADMIN/CAP_NET_RAW capabilities on uBridge diff --git a/.claude/memory/gns3-appliance-loading.md b/.claude/memory/gns3-appliance-loading.md new file mode 100644 index 000000000..1032f0373 --- /dev/null +++ b/.claude/memory/gns3-appliance-loading.md @@ -0,0 +1,20 @@ +--- +name: gns3-appliance-loading +description: GNS3 appliance file loading mechanism and storage locations +metadata: + type: reference +--- + +GNS3 loads appliance (.gns3a) files from two locations with specific priority order: + +1. **Builtin appliances directory**: `~/.local/share/GNS3/appliances/` + - Stores automatically downloaded devices from GNS3 registry + - Maintained and updated by the system automatically + +2. **Custom appliances directory**: `~/GNS3/appliances/` + - Stores user-customized or modified appliance files + - Manually managed by users + +**Loading priority**: System loads builtin appliances first, then custom appliances. If both directories contain devices with the same `device_id`, the custom appliance overwrites the builtin one. This design allows users to customize devices without having their modifications overwritten by automatic registry updates. + +**Implementation**: See `gns3server/controller/appliance_manager.py` in the `load_appliances()` method (lines 314-351). From e71931d096e0017f5d5ea2a4ea59510c36df77d5 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sun, 24 May 2026 22:46:52 +0800 Subject: [PATCH 3/6] feat: add show_filters_icon parameter to packet filter tool - Add show_filters_icon parameter with default value False - Pass show_filters_icon to link.update() in set and clear operations - Update tool description to explain default behavior - Remove "clear" action from description to simplify interface - Hide filter icon in GNS3 Web UI by default for cleaner UI during fault injection --- .../tools_v2/gns3_packet_filter.py | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py index e2a8dee49..9d96239c8 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py @@ -55,6 +55,8 @@ class GNS3PacketFilterTool(BaseTool): **Input:** A JSON object with project_id, link_id, action, and optional filter parameters. + Note: show_filters_icon is automatically set to false by default to hide the filter + icon in the GNS3 Web UI. Example input for getting available filters: { @@ -71,7 +73,8 @@ class GNS3PacketFilterTool(BaseTool): "filters": { "delay": [100, 10], "packet_loss": [5] - } + }, + "show_filters_icon": false } Example input for getting current filters: @@ -104,11 +107,13 @@ class GNS3PacketFilterTool(BaseTool): This tool is primarily used for fault injection scenarios to create realistic network problems for troubleshooting practice, such as latency, packet loss, and corruption. + By default, the filter icon in the GNS3 Web UI is hidden (show_filters_icon=false) to avoid + visual clutter when injecting faults for troubleshooting exercises. + Supported actions: - "get_available": Get list of available filter types for the link - "set": Set packet filters on the link to inject network faults - "get": Get current filters configured on the link - - "clear": Clear all filters from the link (remove injected faults) Common filter types for fault injection: - "frequency_drop": Drop every Nth packet (parameter: frequency, -1 to 32767) @@ -141,13 +146,6 @@ class GNS3PacketFilterTool(BaseTool): } } - Example for clearing filters: - { - "project_id": "uuid-of-project", - "link_id": "uuid-of-link", - "action": "clear" - } - Returns a dictionary with action result, filter information, or error message. """ @@ -176,6 +174,7 @@ class GNS3PacketFilterTool(BaseTool): project_id = input_data.get("project_id") link_id = input_data.get("link_id") action = input_data.get("action") + show_filters_icon = input_data.get("show_filters_icon", False) # Validate required fields if not project_id: @@ -231,11 +230,11 @@ class GNS3PacketFilterTool(BaseTool): result = self._get_available_filters(link) elif action == "set": filters = input_data.get("filters", {}) - result = self._set_filters(link, filters) + result = self._set_filters(link, filters, show_filters_icon) elif action == "get": result = self._get_filters(link) elif action == "clear": - result = self._clear_filters(link) + result = self._clear_filters(link, show_filters_icon) else: result = {"error": f"Unknown action: {action}"} @@ -274,11 +273,13 @@ class GNS3PacketFilterTool(BaseTool): "status": "failed", } - def _set_filters(self, link: Link, filters: dict[str, Any]) -> dict[str, Any]: + def _set_filters( + self, link: Link, filters: dict[str, Any], show_filters_icon: bool = False + ) -> dict[str, Any]: """Set packet filters on the link.""" try: # Update filters - link.update(filters=filters) + link.update(filters=filters, show_filters_icon=show_filters_icon) # Get updated link info link.get() @@ -322,11 +323,13 @@ class GNS3PacketFilterTool(BaseTool): "status": "failed", } - def _clear_filters(self, link: Link) -> dict[str, Any]: + def _clear_filters( + self, link: Link, show_filters_icon: bool = False + ) -> dict[str, Any]: """Clear all filters from the link.""" try: # Clear filters by setting empty dict - link.update(filters={}) + link.update(filters={}, show_filters_icon=show_filters_icon) # Get updated link info to confirm link.get() From ce58e9adc861868a11ae8f9d1a74bcc5f939e3c9 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sun, 24 May 2026 22:49:59 +0800 Subject: [PATCH 4/6] feat: add BPF syntax validation using tshark - Add _validate_bpf_syntax() method to validate BPF expressions - Use tshark with 1-second timeout for syntax checking - Check for "Invalid" in output to detect syntax errors - Validate BPF filters before applying them to links - Handle tshark not installed scenario gracefully - Support both single and multiple BPF expressions - Return detailed error messages for syntax validation failures --- .../tools_v2/gns3_packet_filter.py | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py index 9d96239c8..5bb88de33 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py @@ -33,6 +33,7 @@ including latency, packet loss, corruption, and BPF filtering. import json import logging +import subprocess from pprint import pprint from typing import Any @@ -273,11 +274,93 @@ class GNS3PacketFilterTool(BaseTool): "status": "failed", } + def _validate_bpf_syntax(self, bpf_expression: str) -> dict[str, Any]: + """ + Validate BPF filter expression syntax using tshark. + + Args: + bpf_expression: BPF filter expression to validate + + Returns: + dict with 'valid' (bool) and 'error' (str or None) keys + """ + try: + # Use tshark to validate BPF syntax with 1 second timeout + result = subprocess.run( + ["tshark", "-f", bpf_expression], + timeout=1, + capture_output=True, + text=True, + ) + + # Check if output contains "Invalid" indicating syntax error + if "Invalid" in result.stdout or "Invalid" in result.stderr: + error_lines = [] + if "Invalid" in result.stderr: + error_lines.extend( + line for line in result.stderr.split("\n") if "Invalid" in line + ) + if "Invalid" in result.stdout: + error_lines.extend( + line for line in result.stdout.split("\n") if "Invalid" in line + ) + + error_msg = " ".join(error_lines) if error_lines else "Invalid BPF syntax" + logger.warning("BPF syntax validation failed: %s", error_msg) + return {"valid": False, "error": error_msg} + + logger.info("BPF syntax validation passed") + return {"valid": True, "error": None} + + except subprocess.TimeoutExpired: + # Timeout is expected behavior - tshark waits for traffic + # No "Invalid" in output means syntax is correct + logger.info("BPF syntax validation passed (timeout expected)") + return {"valid": True, "error": None} + + except FileNotFoundError: + # tshark not installed - skip validation + logger.warning( + "tshark not found, skipping BPF syntax validation. " + "Install tshark to enable BPF validation." + ) + return {"valid": True, "error": None} + + except Exception as e: + logger.error("Unexpected error during BPF validation: %s", e) + return {"valid": False, "error": f"BPF validation error: {str(e)}"} + def _set_filters( self, link: Link, filters: dict[str, Any], show_filters_icon: bool = False ) -> dict[str, Any]: """Set packet filters on the link.""" try: + # Validate BPF syntax if BPF filter is present + if "bpf" in filters: + bpf_filters = filters["bpf"] + if isinstance(bpf_filters, list): + # Validate each BPF expression + for idx, bpf_expr in enumerate(bpf_filters): + if isinstance(bpf_expr, str): + validation = self._validate_bpf_syntax(bpf_expr) + if not validation["valid"]: + return { + "action": "set", + "link_id": link.link_id, + "error": f"BPF syntax error at index {idx}: {validation['error']}", + "status": "failed", + } + elif isinstance(bpf_filters, str): + # Single BPF expression + validation = self._validate_bpf_syntax(bpf_filters) + if not validation["valid"]: + return { + "action": "set", + "link_id": link.link_id, + "error": f"BPF syntax error: {validation['error']}", + "status": "failed", + } + # Update filters link.update(filters=filters, show_filters_icon=show_filters_icon) From 045db5bdd8c26674ff2a11efaa778e9da6229fe2 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sun, 24 May 2026 23:00:41 +0800 Subject: [PATCH 5/6] fix: clean BPF syntax error message and specify loopback interface - Add -i lo to tshark command to avoid "(null)" interface in errors - Strip "for interface" suffix from error message for cleaner output --- .../agent/gns3_copilot/tools_v2/gns3_packet_filter.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py index 5bb88de33..d4afb4b60 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py @@ -286,8 +286,9 @@ class GNS3PacketFilterTool(BaseTool): """ try: # Use tshark to validate BPF syntax with 1 second timeout + # Use -i lo (loopback) to avoid "(null)" interface in error messages result = subprocess.run( - ["tshark", "-f", bpf_expression], + ["tshark", "-f", bpf_expression, "-i", "lo"], timeout=1, capture_output=True, text=True, @@ -305,7 +306,13 @@ class GNS3PacketFilterTool(BaseTool): line for line in result.stdout.split("\n") if "Invalid" in line ) - error_msg = " ".join(error_lines) if error_lines else "Invalid BPF syntax" + # Strip interface suffix (e.g., "for interface 'lo'") for cleaner error + error_msg_parts = [] + for line in error_lines: + clean = line.split(" for interface")[0].strip() + if clean: + error_msg_parts.append(clean) + error_msg = " ".join(error_msg_parts) if error_msg_parts else "Invalid BPF syntax" logger.warning("BPF syntax validation failed: %s", error_msg) return {"valid": False, "error": error_msg} From ee80df9e9cccbb672e7234a8c0539966f38e1e18 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sun, 24 May 2026 23:25:01 +0800 Subject: [PATCH 6/6] docs: add AIOps fault injection testing pipeline roadmap Roadmap for duplicating company network architecture into GNS3 and building an automated pipeline to inject faults and validate AIOps diagnosis, with traffic injection support for enhanced realism. --- ...ault-injection-testing-pipeline-roadmap.md | 280 ++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 docs/gns3-copilot/roadmap/aiops-fault-injection-testing-pipeline-roadmap.md diff --git a/docs/gns3-copilot/roadmap/aiops-fault-injection-testing-pipeline-roadmap.md b/docs/gns3-copilot/roadmap/aiops-fault-injection-testing-pipeline-roadmap.md new file mode 100644 index 000000000..760785efd --- /dev/null +++ b/docs/gns3-copilot/roadmap/aiops-fault-injection-testing-pipeline-roadmap.md @@ -0,0 +1,280 @@ + + +> This document is a roadmap/planning document. The described features have not been implemented yet. + +# AIOps Fault Injection Testing Pipeline — Roadmap + +## Overview + +Build a realistic testing pipeline that duplicates the company's production network architecture into GNS3, then systematically injects network faults using the AI Copilot's fault injection capabilities to validate and train the AIOps module before production deployment. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ AIOps Testing Pipeline │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ +│ │ Network │ │ Fault │ │ AIOps │ │ +│ │ Duplication │───▶│ Injection │───▶│ Validation │ │ +│ │ (Phase 1) │ │ (Phase 2) │ │ (Phase 3) │ │ +│ └──────────────┘ └──────────────┘ └──────────────────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ +│ │ GNS3 Network │ │ Fault │ │ Results & │ │ +│ │ Replica │ │ Scenarios │ │ Reporting │ │ +│ └──────────────┘ └──────────────┘ └──────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Core Concept + +1. **Duplicate** the company's production network architecture into a GNS3 simulation environment +2. **Select** a set of fault types to test (OSPF, BGP, VxLAN, STP, packet filter, etc.) +3. **Inject** faults automatically using the AI Copilot's fault injection capabilities +4. **Validate** whether the AIOps module correctly identifies and reports each fault +5. **Loop** through all selected fault scenarios, building a comprehensive test matrix +6. **Train** the AIOps module on results to improve accuracy before production deployment + +## Phase 1: Network Architecture Duplication + +### Goal + +Create a high-fidelity replica of the company production network in GNS3. + +### Key Tasks + +- [ ] **Topology Mapping**: Document production network topology (devices, links, protocols) +- [ ] **Device Selection**: Map production devices to GNS3-compatible images (Cisco IOSv, XRv, Juniper vSRX, etc.) +- [ ] **Configuration Extraction**: Export sanitized production configs (remove passwords, public IPs, sensitive data) +- [ ] **GNS3 Deployment**: Build the network in GNS3 with accurate device placement and links +- [ ] **Config Replication**: Apply adapted configurations to GNS3 devices +- [ ] **Connectivity Validation**: Verify OSPF/BGP adjacencies, VLANs, VRFs, and end-to-end reachability +- [ ] **Baseline Capture**: Record normal operation metrics (CPU, memory, interface counters, routing tables) + +### Considerations + +- Sanitize all production configurations before importing into GNS3 +- Use environment-specific IP addressing where necessary (loopbacks, management) +- Document all deviations from production for traceability + +## Phase 2: Fault Injection Pipeline + +### Goal + +Systematically inject network faults and validate AIOps detection using the existing AI Copilot fault injection infrastructure. + +### Components + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ Fault Injection Pipeline │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ +│ │ Scenario │ │ Inject │ │ AIOps │ │ Record │ │ +│ │ Selector │──▶│ Fault │──▶│ Validate │──▶│ & Report │ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ +│ │ │ │ │ │ +│ │ │ │ │ │ +│ └──────────────┴──────────────┴───────────────┘ │ +│ │ │ +│ ▼ │ +│ Loop until all scenarios tested │ +└──────────────────────────────────────────────────────────────────┘ +``` + +### 2.1 Scenario Selector + +- Read fault scenarios from the GNS3-Skills repository +- Support filtering by: + - Protocol (OSPF, BGP, VxLAN, STP, VLAN, etc.) + - Severity (critical, high, medium, low) + - Difficulty (beginner, intermediate, advanced) +- Track which scenarios have been tested +- Randomize selection order to avoid bias +- Exclude previously tested scenarios + +### 2.2 Fault Injection + +- Use existing `manage_gns3_packet_filter` tool for network-level faults +- Use existing `execute_multiple_device_config_commands` for configuration faults +- Use existing `InjectionSkillsTool` to query and select appropriate faults +- Support combined faults (multiple simultaneous issues) +- Auto-recovery between scenarios (restore baseline state) + +### 2.3 AIOps Validation + +- Feed network state (after fault injection) to the AIOps module +- Record AIOps diagnosis output +- Compare AIOps results against expected fault definition: + - **Correct identification**: AIOps names the exact fault + - **Partial identification**: AIOps identifies related symptoms but not root cause + - **Missed**: AIOps fails to detect any issue + - **False positive**: AIOps reports a fault that doesn't exist + +### 2.4 Test Execution Flow + +``` +1. Reset network to clean baseline state +2. Select next untested fault scenario +3. Inject the fault into the GNS3 network +4. Wait for convergence (configurable delay) +5. Query AIOps module for diagnosis +6. Compare AIOps output with expected fault definition +7. Record result (pass/fail/partial) +8. Restore network to baseline +9. Repeat from step 2 until all scenarios completed +``` + +## Phase 3: Traffic Injection (Enhanced Realism) + +### Goal + +Add realistic network traffic to the GNS3 simulation so that AIOps has real telemetry data to analyze, rather than a static network. + +### Approaches + +#### 3.1 Traffic Generators in GNS3 + +- Deploy traffic generator appliances in GNS3 (e.g., TRex, Ostinato, Scapy on Linux nodes) +- Generate realistic traffic patterns: + - VoIP/RTP streams + - HTTP/HTTPS web traffic + - Database replication + - Routing protocol updates (OSPF hellos, BGP keepalives) + - ICMP monitoring traffic + +#### 3.2 tcpreplay with Captured Traffic + +- Capture real production traffic (sanitized) +- Use `tcpreplay` to replay traffic through the GNS3 network +- More realistic than synthetic traffic generators + +#### 3.3 Integration with Network Monitoring + +- Feed simulated device telemetry (SNMP, syslog, NetFlow) to the AIOps module +- Enable AIOps to analyze real-time telemetry during fault conditions +- Validate that AIOps can distinguish between traffic anomalies and actual faults + +## Success Metrics + +| Metric | Target | Measurement Method | +|--------|--------|-------------------| +| Fault detection rate | >95% | AIOps correctly identifies injected faults | +| False positive rate | <5% | AIOps reports fault when none exists | +| Time to detection | <30s | Duration from injection to AIOps alert | +| Coverage | >80% of defined scenarios | Percentage of scenarios tested | +| Accuracy improvement | Measurable per cycle | Compare pass rates across test cycles | + +## Technical Architecture + +``` +┌────────────────────────────────────────────────────────────────────┐ +│ Test Orchestrator │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ Test Runner (Python) │ │ +│ │ - Scenario selection & scheduling │ │ +│ │ - Fault injection coordination │ │ +│ │ - AIOps query & result collection │ │ +│ │ - Report generation │ │ +│ └──────────┬────────────────────────────────────────────┬───────┘ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌────────────────────┐ ┌────────────────────┐ │ +│ │ GNS3 Controller │ │ AIOps Module │ │ +│ │ (gns3-server) │ │ │ │ +│ │ - Network mgmt │ │ - Fault diagnosis │ │ +│ │ - Fault injection │ │ - Alert detection │ │ +│ │ - State queries │ │ - Root cause │ │ +│ └────────────────────┘ └────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌────────────────────┐ │ +│ │ GNS3 Network │ │ +│ │ Replica │ │ +│ │ - Devices │ │ +│ │ - Traffic │ │ +│ │ - Telemetry │ │ +│ └────────────────────┘ │ +└────────────────────────────────────────────────────────────────────┘ +``` + +## Reporting + +Each test cycle produces: + +- **Summary report**: Pass/fail rates, coverage, trends +- **Detailed per-scenario report**: Injection details, AIOps response, comparison +- **Regression tracker**: Which scenarios regressed since last cycle +- **Accuracy trend**: Improvement or degradation over time + +### Example Report Entry + +```yaml +test_cycle: 7 +date: "2026-06-01" +scenarios_planned: 20 +scenarios_completed: 18 +failed_injections: 1 +skipped: 1 +results: + - scenario: ospf_hello_dead_mismatch + protocol: ospf + severity: major + injection_method: device_config + target_device: R1 + aiops_detection: true + aiops_diagnosis: "OSPF Hello/Dead interval mismatch between R1 and R2" + detection_latency_ms: 12000 + match: exact + - scenario: packet_loss_heavy + protocol: performance + severity: high + injection_method: packet_filter + target_link: "R1 ↔ R2 (ethernet)" + aiops_detection: true + aiops_diagnosis: "High packet loss detected on link R1-R2" + detection_latency_ms: 45000 + match: partial +``` + +## Dependencies + +- [ ] GNS3 network replica ready and validated +- [ ] AI Copilot fault injection tools operational +- [ ] AIOps module query interface available +- [ ] Traffic generation tools deployed (for Phase 3) +- [ ] Test orchestrator framework (to be built) +- [ ] Result database and reporting system + +## Timeline + +| Phase | Duration | Deliverable | +|-------|----------|-------------| +| P1: Network Duplication | 2-4 weeks | GNS3 replica of production network | +| P2: Fault Injection Pipeline | 2-3 weeks | Automated test runner + first results | +| P3: Traffic Injection | 2-4 weeks | Realistic traffic simulation integrated | + +## Status + +- [ ] P1: Network architecture duplication + - [ ] Topology mapping documented + - [ ] Device configurations sanitized and adapted + - [ ] GNS3 replica deployed + - [ ] Baseline connectivity verified +- [ ] P2: Fault injection pipeline + - [ ] Scenario selection framework + - [ ] Automated fault injection + - [ ] AIOps validation interface + - [ ] Report generation + - [ ] Loop/retry mechanism +- [ ] P3: Traffic injection + - [ ] Traffic generator deployment + - [ ] Traffic pattern library + - [ ] AIOps telemetry integration +