mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 14:01:15 +02:00
Support packet filtering for VPCS
https://github.com/GNS3/gns3-gui/issues/765
This commit is contained in:
25 files changed
+526
-269
No files matched your search
@@ -26,6 +26,68 @@ import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
FILTERS = [
|
||||
{
|
||||
"type": "frequency_drop",
|
||||
"name": "Frequency drop",
|
||||
"description": "It will drop everything with a -1 frequency, drop every Nth packet with a positive frequency, or drop nothing",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Frequency",
|
||||
"minimum": -1,
|
||||
"maximum": 32767,
|
||||
"unit": "th packet"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "packet_loss",
|
||||
"name": "Packet loss",
|
||||
"description": "The percentage represents the chance for a packet to be lost",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Frequency",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"unit": "%"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "delay",
|
||||
"name": "Delay",
|
||||
"description": "Delay packets in milliseconds. You can add jitter in milliseconds (+/-) of the delay",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Delay",
|
||||
"minimum": 0,
|
||||
"maximum": 32767,
|
||||
"unit": "ms"
|
||||
},
|
||||
{
|
||||
"name": "Jitter",
|
||||
"minimum": 0,
|
||||
"maximum": 32767,
|
||||
"unit": "ms"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "corrupt",
|
||||
"name": "Corrupt",
|
||||
"description": "The percentage represents the chance for a packet to be corrupt",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Frequency",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"unit": "%"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
class Link:
|
||||
"""
|
||||
Base class for links.
|
||||
@@ -44,6 +106,32 @@ class Link:
|
||||
self._streaming_pcap = None
|
||||
self._created = False
|
||||
self._link_type = "ethernet"
|
||||
self._filters = {}
|
||||
|
||||
@property
|
||||
def filters(self):
|
||||
"""
|
||||
Get an array of filters
|
||||
"""
|
||||
return self._filters
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_filters(self, filters):
|
||||
"""
|
||||
Modify the filters list.
|
||||
|
||||
Filter with value 0 will be dropped because not active
|
||||
"""
|
||||
new_filters = {}
|
||||
for (filter, values) in filters.items():
|
||||
values = [int(v) for v in values]
|
||||
if len(values) != 0 and values[0] != 0:
|
||||
new_filters[filter] = values
|
||||
|
||||
if new_filters != self.filters:
|
||||
self._filters = new_filters
|
||||
if self._created:
|
||||
yield from self.update()
|
||||
|
||||
@property
|
||||
def created(self):
|
||||
@@ -127,6 +215,13 @@ class Link:
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self):
|
||||
"""
|
||||
Update a link
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
"""
|
||||
@@ -230,6 +325,28 @@ class Link:
|
||||
else:
|
||||
return None
|
||||
|
||||
def available_filters(self):
|
||||
"""
|
||||
Return the list of filters compatible with this link
|
||||
|
||||
:returns: Array of filters
|
||||
"""
|
||||
filter_node = self._get_filter_node()
|
||||
if filter_node:
|
||||
return FILTERS
|
||||
return []
|
||||
|
||||
def _get_filter_node(self):
|
||||
"""
|
||||
Return the node where the filter will run
|
||||
|
||||
:returns: None if no node support filtering else the node
|
||||
"""
|
||||
for node in self._nodes:
|
||||
if node["node"].node_type in ('vpcs', ):
|
||||
return node["node"]
|
||||
return None
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Link):
|
||||
return False
|
||||
@@ -253,7 +370,8 @@ class Link:
|
||||
if topology_dump:
|
||||
return {
|
||||
"nodes": res,
|
||||
"link_id": self._id
|
||||
"link_id": self._id,
|
||||
"filters": self._filters
|
||||
}
|
||||
return {
|
||||
"nodes": res,
|
||||
@@ -262,5 +380,6 @@ class Link:
|
||||
"capturing": self._capturing,
|
||||
"capture_file_name": self._capture_file_name,
|
||||
"capture_file_path": self.capture_file_path,
|
||||
"link_type": self._link_type
|
||||
"link_type": self._link_type,
|
||||
"filters": self._filters
|
||||
}
|
||||
@@ -665,6 +665,8 @@ class Project:
|
||||
for node_link in link_data["nodes"]:
|
||||
node = self.get_node(node_link["node_id"])
|
||||
yield from link.add_node(node, node_link["adapter_number"], node_link["port_number"], label=node_link.get("label"), dump=False)
|
||||
if "filters" in link_data:
|
||||
yield from link.update_filters(link_data["filters"])
|
||||
for drawing_data in topology.get("drawings", []):
|
||||
yield from self.add_drawing(dump=False, **drawing_data)
|
||||
|
||||
|
||||
@@ -62,12 +62,21 @@ class UDPLink(Link):
|
||||
response = yield from node2.compute.post("/projects/{}/ports/udp".format(self._project.id))
|
||||
self._node2_port = response.json["udp_port"]
|
||||
|
||||
node1_filters = {}
|
||||
node2_filters = {}
|
||||
filter_node = self._get_filter_node()
|
||||
if filter_node == node1:
|
||||
node1_filters = self._filters
|
||||
elif filter_node == node2:
|
||||
node2_filters = self._filters
|
||||
|
||||
# Create the tunnel on both side
|
||||
self._link_data.append({
|
||||
"lport": self._node1_port,
|
||||
"rhost": node2_host,
|
||||
"rport": self._node2_port,
|
||||
"type": "nio_udp"
|
||||
"type": "nio_udp",
|
||||
"filters": node1_filters
|
||||
})
|
||||
yield from node1.post("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), data=self._link_data[0], timeout=120)
|
||||
|
||||
@@ -75,7 +84,8 @@ class UDPLink(Link):
|
||||
"lport": self._node2_port,
|
||||
"rhost": node1_host,
|
||||
"rport": self._node1_port,
|
||||
"type": "nio_udp"
|
||||
"type": "nio_udp",
|
||||
"filters": node2_filters
|
||||
})
|
||||
try:
|
||||
yield from node2.post("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), data=self._link_data[1], timeout=120)
|
||||
@@ -85,6 +95,25 @@ class UDPLink(Link):
|
||||
raise e
|
||||
self._created = True
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self):
|
||||
if len(self._link_data) == 0:
|
||||
return
|
||||
node1 = self._nodes[0]["node"]
|
||||
node2 = self._nodes[1]["node"]
|
||||
filter_node = self._get_filter_node()
|
||||
|
||||
if node1 == filter_node:
|
||||
adapter_number1 = self._nodes[0]["adapter_number"]
|
||||
port_number1 = self._nodes[0]["port_number"]
|
||||
self._link_data[0]["filters"] = self._filters
|
||||
yield from node1.put("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), data=self._link_data[0], timeout=120)
|
||||
elif node2 == filter_node:
|
||||
adapter_number2 = self._nodes[1]["adapter_number"]
|
||||
port_number2 = self._nodes[1]["port_number"]
|
||||
self._link_data[1]["filters"] = self._filters
|
||||
yield from node2.put("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), data=self._link_data[1], timeout=221)
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
"""
|
||||
|
||||
Reference in new issue
Block a user