Support packet filtering for VPCS

https://github.com/GNS3/gns3-gui/issues/765
This commit is contained in:
Julien Duponchelle committed 2017-07-04 15:12:26 +02:00
1 parent 8f72356bab
commit 08423eff96
25 files changed
+526 -269

No files matched your search

+121 -2
View File
@@ -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
}
+2
View File
@@ -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)
+31 -2
View File
@@ -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):
"""