From 86f12012768d4cc2788b344b7046b6282a9791a2 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 12 Jul 2017 12:08:02 +0200 Subject: [PATCH] Support packet filtering for dynamips Fix #1109 --- gns3server/compute/base_node.py | 2 +- gns3server/compute/dynamips/nios/nio_udp.py | 42 +++++++++++++------ gns3server/compute/dynamips/nodes/router.py | 2 +- gns3server/compute/vpcs/vpcs_vm.py | 2 +- gns3server/controller/link.py | 2 +- .../api/compute/dynamips_vm_handler.py | 29 +++++++++++++ tests/compute/test_base_node.py | 2 +- 7 files changed, 64 insertions(+), 17 deletions(-) diff --git a/gns3server/compute/base_node.py b/gns3server/compute/base_node.py index 14c6b42d..56444f43 100644 --- a/gns3server/compute/base_node.py +++ b/gns3server/compute/base_node.py @@ -599,7 +599,7 @@ class BaseNode: yield from self._ubridge_send("bridge delete {name}".format(name=name)) @asyncio.coroutine - def _update_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio): + def update_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio): yield from self._ubridge_apply_filters(bridge_name, destination_nio.filters) @asyncio.coroutine diff --git a/gns3server/compute/dynamips/nios/nio_udp.py b/gns3server/compute/dynamips/nios/nio_udp.py index 522b3c5a..60ddafa0 100644 --- a/gns3server/compute/dynamips/nios/nio_udp.py +++ b/gns3server/compute/dynamips/nios/nio_udp.py @@ -54,6 +54,14 @@ class NIOUDP(NIO): self._node = node super().__init__(name, node.hypervisor) + @property + def filters(self): + return self._filters + + @filters.setter + def filters(self, val): + self._filters = val + @asyncio.coroutine def create(self): if not self._hypervisor: @@ -67,7 +75,7 @@ class NIOUDP(NIO): return self._local_tunnel_lport = self._node.manager.port_manager.get_free_udp_port(self._node.project) self._local_tunnel_rport = self._node.manager.port_manager.get_free_udp_port(self._node.project) - name = 'DYNAMIPS-{}-{}'.format(self._local_tunnel_lport, self._local_tunnel_rport) + self._bridge_name = 'DYNAMIPS-{}-{}'.format(self._local_tunnel_lport, self._local_tunnel_rport) yield from self._hypervisor.send("nio create_udp {name} {lport} {rhost} {rport}".format(name=self._name, lport=self._local_tunnel_lport, rhost='127.0.0.1', @@ -77,23 +85,33 @@ class NIOUDP(NIO): lport=self._lport, rhost=self._rhost, rport=self._rport)) + + self._source_nio = nio_udp.NIOUDP(self._local_tunnel_rport, + '127.0.0.1', + self._local_tunnel_lport, + {}) + self._destination_nio = nio_udp.NIOUDP(self._lport, + self._rhost, + self._rport, + self._filters) yield from self._node.add_ubridge_udp_connection( - name, - nio_udp.NIOUDP(self._local_tunnel_rport, - '127.0.0.1', - self._local_tunnel_lport, - self._filters), - nio_udp.NIOUDP(self._lport, - self._rhost, - self._rport, - self._filters) + self._bridge_name, + self._source_nio, + self._destination_nio ) + @asyncio.coroutine + def update(self): + self._destination_nio.filters = self._filters + yield from self._node.update_ubridge_udp_connection( + self._bridge_name, + self._source_nio, + self._destination_nio) + @asyncio.coroutine def close(self): if self._local_tunnel_lport: - name = 'DYNAMIPS-{}-{}'.format(self._local_tunnel_lport, self._local_tunnel_rport) - yield from self._node.ubridge_delete_bridge(name) + yield from self._node.ubridge_delete_bridge(self._bridge_name) self._node.manager.port_manager.release_udp_port(self._local_tunnel_lport, self ._node.project) if self._local_tunnel_rport: self._node.manager.port_manager.release_udp_port(self._local_tunnel_rport, self._node.project) diff --git a/gns3server/compute/dynamips/nodes/router.py b/gns3server/compute/dynamips/nodes/router.py index e9a0fa47..7ea6c15f 100644 --- a/gns3server/compute/dynamips/nodes/router.py +++ b/gns3server/compute/dynamips/nodes/router.py @@ -1290,7 +1290,7 @@ class Router(BaseNode): :param port_number: port number :param nio: NIO instance to add to the slot/port """ - pass + yield from nio.update() @asyncio.coroutine def slot_remove_nio_binding(self, slot_number, port_number): diff --git a/gns3server/compute/vpcs/vpcs_vm.py b/gns3server/compute/vpcs/vpcs_vm.py index f37f397c..f45d8985 100644 --- a/gns3server/compute/vpcs/vpcs_vm.py +++ b/gns3server/compute/vpcs/vpcs_vm.py @@ -397,7 +397,7 @@ class VPCSVM(BaseNode): raise VPCSError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=self._ethernet_adapter, port_number=port_number)) if self.ubridge: - yield from self._update_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio) + yield from self.update_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio) elif self.is_running(): raise VPCSError("Sorry, adding a link to a started VPCS instance is not supported without using uBridge.") diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index 78bc033c..2e1e2956 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -365,7 +365,7 @@ class Link: :returns: None if no node support filtering else the node """ for node in self._nodes: - if node["node"].node_type in ('vpcs', ): + if node["node"].node_type in ('vpcs', 'dynamips'): return node["node"] return None diff --git a/gns3server/handlers/api/compute/dynamips_vm_handler.py b/gns3server/handlers/api/compute/dynamips_vm_handler.py index dd0352de..6626a0d8 100644 --- a/gns3server/handlers/api/compute/dynamips_vm_handler.py +++ b/gns3server/handlers/api/compute/dynamips_vm_handler.py @@ -268,6 +268,35 @@ class DynamipsVMHandler: response.set_status(201) response.json(nio) + @Route.put( + r"/projects/{project_id}/dynamips/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID", + "adapter_number": "Network adapter where the nio is located", + "port_number": "Port from where the nio should be updated" + }, + status_codes={ + 201: "NIO updated", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + input=NIO_SCHEMA, + output=NIO_SCHEMA, + description="Update a NIO from a Dynamips instance") + def update_nio(request, response): + + dynamips_manager = Dynamips.instance() + vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + slot_number = int(request.match_info["adapter_number"]) + port_number = int(request.match_info["port_number"]) + nio = vm.slots[slot_number].get_nio(port_number) + if "filters" in request.json and nio: + nio.filters = request.json["filters"] + yield from vm.slot_update_nio_binding(slot_number, port_number, nio) + response.set_status(201) + response.json(request.json) + @Route.delete( r"/projects/{project_id}/dynamips/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ diff --git a/tests/compute/test_base_node.py b/tests/compute/test_base_node.py index e942c698..635c9966 100644 --- a/tests/compute/test_base_node.py +++ b/tests/compute/test_base_node.py @@ -133,7 +133,7 @@ def test_update_ubridge_udp_connection(node, async_run): snio = NIOUDP(1245, "localhost", 1246, []) dnio = NIOUDP(1245, "localhost", 1244, filters) with asyncio_patch("gns3server.compute.base_node.BaseNode._ubridge_apply_filters") as mock: - async_run(node._update_ubridge_udp_connection('VPCS-10', snio, dnio)) + async_run(node.update_ubridge_udp_connection('VPCS-10', snio, dnio)) mock.assert_called_with("VPCS-10", filters)