aboutsummaryrefslogtreecommitdiffstats
path: root/resources/libraries
diff options
context:
space:
mode:
authorselias <samelias@cisco.com>2016-08-11 10:02:33 +0200
committerJan Gelety <jgelety@cisco.com>2016-08-25 21:34:48 +0000
commitf2711a847cd8de8dadce1049747e78f96bcae031 (patch)
treee6f0fb27e22c6d32fb4c0241e54a253eaad0ecbe /resources/libraries
parent53ce7d17816de6516f3c21bd01835b8c1c474c79 (diff)
CSIT-233 IPv4 IPFIX - baseline tests
- add scapy classes for parsing IPFIX packets - add vat scripts and keywords for settings up IPFIX - add IPv4 IPFIX test suite Change-Id: I80ab76ca361c7920a01a46ad720b1c04acd0d147 Signed-off-by: selias <samelias@cisco.com>
Diffstat (limited to 'resources/libraries')
-rw-r--r--resources/libraries/python/Classify.py38
-rw-r--r--resources/libraries/python/IPFIXSetup.py129
-rw-r--r--resources/libraries/python/IPFIXUtil.py102
-rw-r--r--resources/libraries/python/PacketVerifier.py22
-rw-r--r--resources/libraries/robot/ipfix.robot67
5 files changed, 350 insertions, 8 deletions
diff --git a/resources/libraries/python/Classify.py b/resources/libraries/python/Classify.py
index dfa5c3377d..8dbe3fb25f 100644
--- a/resources/libraries/python/Classify.py
+++ b/resources/libraries/python/Classify.py
@@ -37,6 +37,7 @@ class Classify(object):
:rtype: tuple(int, int, int)
:raises RuntimeError: If VPP can't create table.
"""
+
output = VatExecutor.cmd_from_template(node, "classify_add_table.vat",
ip_version=ip_version,
direction=direction)
@@ -205,6 +206,43 @@ class Classify(object):
hex_value=hex_value)
@staticmethod
+ def vpp_configures_classify_session_generic(node, session_type, table_index,
+ skip_n, match_n, match,
+ match2=''):
+ """Configuration of classify session.
+
+ :param node: VPP node to setup classify session.
+ :param session_type: Session type - hit-next, l2-hit-next, acl-hit-next
+ or policer-hit-next, and their respective parameters.
+ :param table_index: Classify table index.
+ :param skip_n: Number of skip vectors based on mask.
+ :param match_n: Number of match vectors based on mask.
+ :param match: Match value - l2, l3, l4 or hex, and their
+ respective parameters.
+ :param match2: Additional match values, to avoid using overly long
+ variables in RobotFramework.
+ :type node: dict
+ :type session_type: str
+ :type table_index: int
+ :type skip_n: int
+ :type match_n: int
+ :type match: str
+ :type match2: str
+ """
+
+ match = ' '.join((match, match2))
+
+ with VatTerminal(node) as vat:
+ vat.vat_terminal_exec_cmd_from_template(
+ "classify_add_session_generic.vat",
+ type=session_type,
+ table_index=table_index,
+ skip_n=skip_n,
+ match_n=match_n,
+ match=match,
+ )
+
+ @staticmethod
def compute_classify_hex_mask(ip_version, protocol, direction):
"""Compute classify hex mask for TCP or UDP packet matching.
diff --git a/resources/libraries/python/IPFIXSetup.py b/resources/libraries/python/IPFIXSetup.py
new file mode 100644
index 0000000000..f0f35f3bc6
--- /dev/null
+++ b/resources/libraries/python/IPFIXSetup.py
@@ -0,0 +1,129 @@
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""IPFIX setup library"""
+
+from resources.libraries.python.topology import Topology
+from resources.libraries.python.VatExecutor import VatTerminal
+
+
+class IPFIXSetup(object):
+ """Class contains methods for seting up IPFIX reporting on DUTs."""
+
+ def __init__(self):
+ """Initializer."""
+ pass
+
+ @staticmethod
+ def setup_ipfix_exporter(node, collector, source, fib=None, mtu=None,
+ interval=None):
+ """Setup an IPFIX exporter on node to export collected flow data.
+
+ :param node: DUT node.
+ :param collector: IP address of flow data collector.
+ :param source: IP address of local interface to send flow data from.
+ :param fib: fib table ID.
+ :param mtu: Maximum transfer unit of path to collector.
+ :param interval: Frequency of sending template packets, in seconds.
+ :type node: dict
+ :type collector: str
+ :type source: str
+ :type fib: int
+ :type mtu: int
+ :type interval: int
+ """
+
+ fib = "vrf_id {0}".format(fib) if fib else ''
+ mtu = "path_mtu {0}".format(mtu) if mtu else ''
+ interval = "template_interval {0}".format(interval) if interval else ''
+
+ with VatTerminal(node, json_param=False) as vat:
+ vat.vat_terminal_exec_cmd_from_template('ipfix_exporter_set.vat',
+ collector=collector,
+ source=source,
+ fib=fib,
+ mtu=mtu,
+ interval=interval)
+
+ @staticmethod
+ def assign_interface_to_flow_table(node, interface, table_id,
+ ip_version='ip4'):
+ """Assigns a VPP interface to the specified classify table for IPFIX
+ flow data collection.
+
+ :param node: DUT node.
+ :param interface: An interface on the DUT node.
+ :param table_id: ID of a classify table.
+ :param ip_version: Version of IP protocol. Valid options are ip4, ip6.
+ :type node: dict
+ :type interface: str or int
+ :type table_id: int
+ :type ip_version: str
+ """
+
+ if isinstance(interface, basestring):
+ sw_if_index = Topology.get_interface_sw_index(node, interface)
+ elif isinstance(interface, int):
+ sw_if_index = interface
+ else:
+ raise TypeError
+
+ table = "{0}-table {1}".format(ip_version, table_id)
+
+ with VatTerminal(node, json_param=False) as vat:
+ vat.vat_terminal_exec_cmd_from_template(
+ "ipfix_interface_enable.vat",
+ interface=sw_if_index,
+ table=table,
+ delete='')
+
+ @staticmethod
+ def set_ipfix_stream(node, domain=None, src_port=None):
+ """Set an IPFIX export stream. Can be used to break up IPFIX reports
+ into separate reporting domains.
+
+ :param node: DUT node.
+ :param domain: Desired index number of exporting domain.
+ :param src_port: Source port to use when sending IPFIX packets. Default
+ is the standard IPFIX port 4739.
+ :type node: dict
+ :type domain: int
+ :type src_port: int
+ """
+
+ domain = "domain {0}".format(domain) if domain else ''
+ src_port = "src_port {0}".format(src_port) if src_port else ''
+
+ with VatTerminal(node, json_param=False) as vat:
+ vat.vat_terminal_exec_cmd_from_template("ipfix_stream_set.vat",
+ domain=domain,
+ src_port=src_port)
+
+ @staticmethod
+ def assign_classify_table_to_exporter(node, table_id, ip_version='ip4'):
+ """Assign a classify table to an IPFIX exporter. Classified packets will
+ be included in the IPFIX flow report.
+
+ :param node: DUT node.
+ :param table_id: ID of a classify table.
+ :param ip_version: Version of IP protocol. Valid options are ip4, ip6.
+ :type node: dict
+ :type table_id: int
+ :type ip_version: str
+ """
+
+ with VatTerminal(node, json_param=False) as vat:
+ vat.vat_terminal_exec_cmd_from_template("ipfix_table_add.vat",
+ table=table_id,
+ ip_version=ip_version,
+ add_del='add')
diff --git a/resources/libraries/python/IPFIXUtil.py b/resources/libraries/python/IPFIXUtil.py
new file mode 100644
index 0000000000..f3247a8982
--- /dev/null
+++ b/resources/libraries/python/IPFIXUtil.py
@@ -0,0 +1,102 @@
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""IPFIX utilities library. Provides classes that allow scapy to work
+with IPFIX packets.
+
+ Note:
+ Template and data sets in one packet are not supported.
+ Option template sets (Set_ID = 3) are not supported.
+ """
+
+
+from scapy.all import Packet, bind_layers
+from scapy.fields import *
+from scapy.layers.inet import UDP
+from scapy.contrib.ppi_geotag import UTCTimeField
+
+
+class IPFIXHandler(object):
+ """Class for handling IPFIX packets. To use, create instance of class before
+ dissecting IPFIX packets with scapy, then run update_template every time
+ an IPFIX template packet is received."""
+
+ template_elements = {
+ 4: ByteField("Protocol_ID", 0x00),
+ 7: ShortField("src_port", 0),
+ 8: IPField("IPv4_src", ""),
+ 11: ShortField("dst_port", 0),
+ 12: IPField("IPv4_dst", ""),
+ 86: LongField("packetTotalCount", 0),
+ 180: ShortField("udp_src_port", 0),
+ 181: ShortField("udp_dst_port", 0),
+ 182: ShortField("tcp_src_port", 0),
+ 183: ShortField("tcp_dst_port", 0),
+ }
+
+ def __init__(self):
+ """Initializer, registers IPFIX header and template layers with scapy.
+ """
+ bind_layers(UDP, IPFIXHeader, dport=4739)
+ bind_layers(IPFIXHeader, IPFIXTemplate, Set_ID=2)
+
+ def update_template(self, packet):
+ """Updates IPFIXData class with new data template. Registers IPFIX data
+ layer with scapy using the new template.
+
+ :param packet: Packet containing an IPFIX template.
+ :type packet: scapy.Ether
+ """
+ template_list = packet['IPFIX template'].Template
+ template_id = packet['IPFIX template'].Template_ID
+
+ IPFIXData.fields_desc = []
+ for item in template_list[::2]:
+ try:
+ IPFIXData.fields_desc.append(self.template_elements[item])
+ except KeyError:
+ raise KeyError(
+ "Unknown IPFIX template element with ID {0}".format(item))
+ bind_layers(IPFIXHeader, IPFIXData, Set_ID=template_id)
+ # if the packet doesn't end here, assume it contains more data sets
+ bind_layers(IPFIXData, IPFIXData)
+
+
+class IPFIXHeader(Packet):
+ """Class for IPFIX header."""
+ name = "IPFIX header"
+ fields_desc = [StrFixedLenField("Version", 0x000a, length=2),
+ ShortField("Message Length", 0),
+ UTCTimeField("Timestamp(UTC)", ""),
+ IntField("Sequence Number", 0),
+ IntField("Observation Domain ID", 0),
+ ShortField("Set_ID", 0),
+ ShortField("Set_Length", 0)
+ ]
+
+
+class IPFIXTemplate(Packet):
+ """Class for IPFIX template layer."""
+ name = "IPFIX template"
+ fields_desc = [ShortField("Template_ID", 256),
+ ShortField("nFields", 2),
+ FieldListField("Template", [], ShortField("type_len", ""),
+ count_from=lambda p: p.nFields*2)
+ ]
+
+
+class IPFIXData(Packet):
+ """Class for IPFIX data layer. Needs to be updated with
+ a template before use."""
+ name = "IPFIX flow data"
+ fields_desc = []
diff --git a/resources/libraries/python/PacketVerifier.py b/resources/libraries/python/PacketVerifier.py
index 78c3670135..59ea2db5a3 100644
--- a/resources/libraries/python/PacketVerifier.py
+++ b/resources/libraries/python/PacketVerifier.py
@@ -204,7 +204,7 @@ class RxQueue(PacketVerifier):
def __init__(self, interface_name):
PacketVerifier.__init__(self, interface_name)
- def recv(self, timeout=3, ignore=None):
+ def recv(self, timeout=3, ignore=None, verbose=True):
"""Read next received packet.
Returns scapy's Ether() object created from next packet in the queue.
@@ -212,9 +212,11 @@ class RxQueue(PacketVerifier):
arrives in given timeout queue.Empty exception will be risen.
:param timeout: How many seconds to wait for next packet.
- :param ignore: Packet list that should be ignored.
+ :param ignore: List of packets that should be ignored.
+ :param verbose: Used to suppress detailed logging of received packets.
:type timeout: int
:type ignore: list
+ :type verbose: bool
:return: Ether() initialized object from packet data.
:rtype: scapy.Ether
@@ -226,8 +228,9 @@ class RxQueue(PacketVerifier):
pkt = self._sock.recv(0x7fff)
pkt_pad = auto_pad(pkt)
print 'Received packet on {0} of len {1}'.format(self._ifname, len(pkt))
- Ether(pkt).show2()
- print
+ if verbose:
+ Ether(pkt).show2()
+ print
if ignore is not None:
for i, ig_pkt in enumerate(ignore):
@@ -238,7 +241,7 @@ class RxQueue(PacketVerifier):
# Found the packet in ignore list, get another one
# TODO: subtract timeout - time_spent in here
ignore.remove(ig_pkt)
- return self.recv(timeout, ignore)
+ return self.recv(timeout, ignore, verbose)
return Ether(pkt)
@@ -254,16 +257,19 @@ class TxQueue(PacketVerifier):
def __init__(self, interface_name):
PacketVerifier.__init__(self, interface_name)
- def send(self, pkt):
+ def send(self, pkt, verbose=True):
"""Send packet out of the bound interface.
:param pkt: Packet to send.
+ :param verbose: Used to supress detailed logging of sent packets.
:type pkt: string or scapy Packet derivative.
+ :type verbose: bool
"""
print 'Sending packet out of {0} of len {1}'.format(self._ifname,
len(pkt))
- Ether(str(pkt)).show2()
- print
+ if verbose:
+ Ether(str(pkt)).show2()
+ print
pkt = auto_pad(str(pkt))
self._sock.send(pkt)
diff --git a/resources/libraries/robot/ipfix.robot b/resources/libraries/robot/ipfix.robot
new file mode 100644
index 0000000000..a5adacfc0f
--- /dev/null
+++ b/resources/libraries/robot/ipfix.robot
@@ -0,0 +1,67 @@
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Traffic keywords"""
+
+*** Settings ***
+| Library | resources.libraries.python.TrafficScriptExecutor
+| Library | resources.libraries.python.InterfaceUtil
+| Resource | resources/libraries/robot/default.robot
+| Documentation | Traffic keywords
+
+*** Keywords ***
+| Send packets and verify IPFIX
+| | [Documentation] | Send simple TCP or UDP packets from source interface\
+| | ... | to destination interface. Listen for IPFIX flow report on source\
+| | ... | interface and verify received report against number of packets sent.
+| | ...
+| | ... | *Arguments:*
+| | ...
+| | ... | - tg_node - TG node. Type: dictionary
+| | ... | - dst_node - Destination node. Type: dictionary
+| | ... | - src_int - Source interface. Type: string
+| | ... | - dst_int - Destination interface. Type: string
+| | ... | - src_ip - Source IP address. Type: string
+| | ... | - dst_ip - Destination IP address. Type: string
+| | ... | - protocol - TCP or UDP (Optional, default is TCP). Type: string
+| | ... | - port - Source and destination ports to use
+| | ... | (Optional, default is port 20). Type: integer
+| | ... | - count - Number of packets to send
+| | ... | (Optional, default is one packet). Type: integer
+| | ... | - timeout - Timeout value in seconds (Optional, default is 10 sec).
+| | ... | Should be at least twice the configured IPFIX flow report interval.
+| | ... | Type: integer
+| | ...
+| | ... | *Return:*
+| | ...
+| | ... | - No value returned
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Send packets and verify IPFIX \| ${nodes['TG']} | ${nodes['DUT1']}\
+| | ... | \| eth1 \| GigabitEthernet0/8/0 \| 16.0.0.1 \| 192.168.0.2 \| UDP \
+| | ... | \| ${20} \| ${5} \| ${10} \|
+| | ... |
+| | [Arguments] | ${tg_node} | ${dst_node} | ${src_int} | ${dst_int} |
+| | ... | ${src_ip} | ${dst_ip} | ${protocol}=tcp | ${port}=20 | ${count}=1
+| | ... | ${timeout}=${10}
+| | ${src_mac}= | Get Interface Mac | ${tg_node} | ${src_int}
+| | ${dst_mac}= | Get Interface Mac | ${dst_node} | ${dst_int}
+| | ${src_int_name}= | Get interface name | ${tg_node} | ${src_int}
+| | ${dst_int_name}= | Get interface name | ${dst_node} | ${dst_int}
+| | ${args}= | Traffic Script Gen Arg | ${dst_int_name} | ${src_int_name}
+| | ... | ${src_mac} | ${dst_mac} | ${src_ip} | ${dst_ip}
+| | ${args}= | Set Variable
+| | ... | ${args} --protocol ${protocol} --port ${port} --count ${count}
+| | Run Traffic Script On Node | ipfix_check.py | ${tg_node} | ${args}
+| | ... | ${timeout}