summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorimarom <imarom@cisco.com>2016-09-07 11:48:23 +0300
committerimarom <imarom@cisco.com>2016-09-07 14:08:35 +0300
commitdbe6b1886ee2b860395731a77da305a01095a843 (patch)
tree1b3141bf387b1435952d138360e41e28f994d53e /scripts
parent323238656f07cbb4671e1493d8274ccb532588b1 (diff)
support for DUAL mode on small PCAPs
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py78
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py200
-rw-r--r--scripts/external_libs/scapy-2.3.1/python2/scapy/utils.py3
-rw-r--r--scripts/external_libs/scapy-2.3.1/python3/scapy/utils.py3
4 files changed, 265 insertions, 19 deletions
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
index 12a69c56..fc49bf77 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
@@ -2330,25 +2330,81 @@ class STLClient(object):
validate_type('vm', vm, (list, type(None)))
validate_type('is_dual', is_dual, bool)
- if is_dual:
- raise STLError("push: dual mode is not implemented yet for non remote injection")
# no support for > 1MB PCAP - use push remote
if not force and os.path.getsize(pcap_filename) > (1024 * 1024):
raise STLError("PCAP size of {:} is too big for local push - consider using remote push or provide 'force'".format(format_num(os.path.getsize(pcap_filename), suffix = 'B')))
- self.remove_all_streams(ports = ports)
+ if is_dual:
+ for port in ports:
+ master = port
+ slave = port ^ 0x1
+
+ if slave in ports:
+ raise STLError("dual mode: cannot provide adjacent ports ({0}, {1}) in a batch".format(master, slave))
+
+ if not slave in self.get_acquired_ports():
+ raise STLError("dual mode: adjacent port {0} must be owned during dual mode".format(slave))
+
+ # regular push
+ if not is_dual:
+
+ # create the profile from the PCAP
+ try:
+ self.logger.pre_cmd("Converting '{0}' to streams:".format(pcap_filename))
+ profile = STLProfile.load_pcap(pcap_filename,
+ ipg_usec,
+ speedup,
+ count,
+ vm = vm,
+ packet_hook = packet_hook)
+ self.logger.post_cmd(RC_OK)
+ except STLError as e:
+ self.logger.post_cmd(RC_ERR(e))
+ raise
+
+
+ self.remove_all_streams(ports = ports)
+ id_list = self.add_streams(profile.get_streams(), ports)
+
+ return self.start(ports = ports, duration = duration)
+
+ else:
+
+ # create a dual profile
+ split_mode = 'MAC'
+
+ try:
+ self.logger.pre_cmd("Analyzing '{0}' for dual ports based on {1}:".format(pcap_filename, split_mode))
+ profile_a, profile_b = STLProfile.load_pcap(pcap_filename,
+ ipg_usec,
+ speedup,
+ count,
+ vm = vm,
+ packet_hook = packet_hook,
+ split_mode = split_mode)
+
+ self.logger.post_cmd(RC_OK())
+
+ except STLError as e:
+ self.logger.post_cmd(RC_ERR(e))
+ raise
+
+ all_ports = ports + [p ^ 0x1 for p in ports]
+
+ self.remove_all_streams(ports = all_ports)
+
+ for port in ports:
+ master = port
+ slave = port ^ 0x1
+
+ self.add_streams(profile_a.get_streams(), master)
+ self.add_streams(profile_b.get_streams(), slave)
+
+ return self.start(ports = all_ports, duration = duration)
- profile = STLProfile.load_pcap(pcap_filename,
- ipg_usec,
- speedup,
- count,
- vm = vm,
- packet_hook = packet_hook)
- id_list = self.add_streams(profile.get_streams(), ports)
- return self.start(ports = ports, duration = duration)
@__api_check(True)
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
index 752f14b5..50751111 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
@@ -16,6 +16,7 @@ import traceback
import copy
import imp
+
# base class for TX mode
class STLTXMode(object):
""" mode rate speed """
@@ -969,7 +970,13 @@ class STLProfile(object):
# loop_count = 0 means loop forever
@staticmethod
- def load_pcap (pcap_file, ipg_usec = None, speedup = 1.0, loop_count = 1, vm = None, packet_hook = None):
+ def load_pcap (pcap_file,
+ ipg_usec = None,
+ speedup = 1.0,
+ loop_count = 1,
+ vm = None,
+ packet_hook = None,
+ split_mode = None):
""" Convert a pcap file with a number of packets to a list of connected streams.
packet1->packet2->packet3 etc
@@ -994,6 +1001,11 @@ class STLProfile(object):
packet_hook : Callable or function
will be applied to every packet
+ is_split : str
+ should this PCAP be split to two profiles based on IPs / MACs
+ used for dual mode
+ can be 'MAC' or 'IP'
+
:return: STLProfile
"""
@@ -1009,13 +1021,50 @@ class STLProfile(object):
if loop_count < 0:
raise STLError("'loop_count' cannot be negative")
- streams = []
- last_ts_usec = 0
-
+
try:
- pkts = RawPcapReader(pcap_file).read_all()
+
+ if split_mode is None:
+ pkts = PCAPReader(pcap_file).read_all()
+ return STLProfile.__pkts_to_streams(pkts,
+ ipg_usec,
+ speedup,
+ loop_count,
+ vm,
+ packet_hook)
+ else:
+ pkts_a, pkts_b = PCAPReader(pcap_file).read_all(split_mode = split_mode)
+
+ profile_a = STLProfile.__pkts_to_streams(pkts_a,
+ ipg_usec,
+ speedup,
+ loop_count,
+ vm,
+ packet_hook,
+ start_delay_usec = 10000)
+
+ profile_b = STLProfile.__pkts_to_streams(pkts_b,
+ ipg_usec,
+ speedup,
+ loop_count,
+ vm,
+ packet_hook,
+ start_delay_usec = 10000)
+
+ return profile_a, profile_b
+
+
except Scapy_Exception as e:
- raise STLError("failed to open PCAP file '{0}'".format(pcap_file))
+ raise STLError("failed to open PCAP file {0}: '{1}'".format(pcap_file, str(e)))
+
+
+ @staticmethod
+ def __pkts_to_streams (pkts, ipg_usec, speedup, loop_count, vm, packet_hook, start_delay_usec = 0):
+
+ streams = []
+
+ # 10 ms delay before starting the PCAP
+ last_ts_usec = -(start_delay_usec)
if packet_hook:
pkts = [(packet_hook(cap), meta) for (cap, meta) in pkts]
@@ -1144,4 +1193,143 @@ def register():
def __len__ (self):
return len(self.streams)
+
+class PCAPReader(object):
+ def __init__ (self, pcap_file):
+ self.pcap_file = pcap_file
+
+ def read_all (self, split_mode = None):
+ if split_mode is None:
+ return RawPcapReader(self.pcap_file).read_all()
+
+ # we need to split
+ self.pcap = rdpcap(self.pcap_file)
+ self.graph = Graph()
+
+ self.pkt_groups = [ [], [] ]
+
+ if split_mode == 'MAC':
+ self.generate_mac_groups()
+ elif split_mode == 'IP':
+ self.generate_ip_groups()
+ else:
+ raise STLError('unknown split mode for PCAP')
+
+ return self.pkt_groups
+
+
+ # generate two groups based on MACs
+ def generate_mac_groups (self):
+ for i, pkt in enumerate(self.pcap):
+ if not isinstance(pkt, (Ether, Dot3) ):
+ raise STLError("Packet #{0} has an unknown L2 format: {1}".format(i, type(pkt)))
+ mac_src = pkt.fields['src']
+ mac_dst = pkt.fields['dst']
+ self.graph.add(mac_src, mac_dst)
+
+ # split the graph to two groups
+ mac_groups = self.graph.split()
+
+ for pkt in self.pcap:
+ mac_src = pkt.fields['src']
+ group = 1 if mac_src in mac_groups[1] else 0
+
+ time, raw = pkt.time, bytes(pkt)
+ self.pkt_groups[group].append((raw, (time, 0)))
+
+
+ # generate two groups based on IPs
+ def generate_ip_groups (self):
+ for pkt in self.pcap:
+ if not isinstance(pkt, (Ether, Dot3) ):
+ raise STLError("Packet #{0} has an unknown L2 format: {1}".format(i, type(pkt)))
+ # skip non IP packets
+ if not isinstance(pkt.payload, IP):
+ continue
+ ip_src = pkt.payload.fields['src']
+ ip_dst = pkt.payload.fields['dst']
+ self.graph.add(ip_src, ip_dst)
+
+ # split the graph to two groups
+ ip_groups = self.graph.split()
+
+ for pkt in self.pcap:
+ # default group - 0
+ group = 0
+
+ # if the packet is IP and IP SRC is in group 1 - move to group 1
+ if isinstance(pkt.payload, IP) and pkt.payload.fields['src'] in ip_groups[1]:
+ group = 1
+
+ time, raw = pkt.time, bytes(pkt)
+ self.pkt_groups[group].append((raw, (time, 0)))
+
+
+
+# a simple graph object - used to split to two groups
+class Graph(object):
+ def __init__ (self):
+ self.db = OrderedDict()
+ self.debug = False
+
+ def log (self, msg):
+ if self.debug:
+ print(msg)
+
+ # add a connection v1 --> v2
+ def add (self, v1, v2):
+ # init value for v1
+ if not v1 in self.db:
+ self.db[v1] = set()
+
+ # init value for v2
+ if not v2 in self.db:
+ self.db[v2] = set()
+
+ # ignore self to self edges
+ if v1 == v2:
+ return
+
+ # undirected - add two ways
+ self.db[v1].add(v2)
+ self.db[v2].add(v1)
+
+
+ # create a 2-color of the graph if possible
+ def split (self):
+ color_a = set()
+ color_b = set()
+
+ # start with all
+ nodes = list(self.db.keys())
+
+ # process one by one
+ while len(nodes) > 0:
+ node = nodes.pop(0)
+
+ friends = self.db[node]
+
+ # node has never been seen - move to color_a
+ if not node in color_a and not node in color_b:
+ self.log("<NEW> {0} --> A".format(node))
+ color_a.add(node)
+
+ # node color
+ node_color, other_color = (color_a, color_b) if node in color_a else (color_b, color_a)
+
+ # check that the coloring is possible
+ bad_friends = friends.intersection(node_color)
+ if bad_friends:
+ raise STLError("ERROR: failed to split PCAP file - {0} and {1} are in the same group".format(node, bad_friends))
+
+ # add all the friends to the other color
+ for friend in friends:
+ self.log("<FRIEND> {0} --> {1}".format(friend, 'A' if other_color is color_a else 'B'))
+ other_color.add(friend)
+
+
+ return color_a, color_b
+
+
default_STLStream = STLStream()
+
diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/utils.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/utils.py
index c5ac2520..b1efdf37 100644
--- a/scripts/external_libs/scapy-2.3.1/python2/scapy/utils.py
+++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/utils.py
@@ -573,7 +573,8 @@ class PcapReader(RawPcapReader):
try:
self.LLcls = conf.l2types[self.linktype]
except KeyError:
- warning("PcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype))
+ raise Scapy_Exception("Scapy PcapReader: unknown LL type [%i]/[%#x]" % (self.linktype,self.linktype))
+ #warning("PcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype))
self.LLcls = conf.raw_layer
def read_packet(self, size=MTU):
rp = RawPcapReader.read_packet(self,size)
diff --git a/scripts/external_libs/scapy-2.3.1/python3/scapy/utils.py b/scripts/external_libs/scapy-2.3.1/python3/scapy/utils.py
index 252109bb..9f15f2a9 100644
--- a/scripts/external_libs/scapy-2.3.1/python3/scapy/utils.py
+++ b/scripts/external_libs/scapy-2.3.1/python3/scapy/utils.py
@@ -750,7 +750,8 @@ class _RawPcapOldReader:
try:
self.LLcls = conf.l2types[self.linktype]
except KeyError:
- warning("RawPcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype))
+ raise Scapy_Exception("Scapy PcapReader: unknown LL type [%i]/[%#x]" % (self.linktype,self.linktype))
+ #warning("RawPcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype))
self.LLcls = conf.raw_layer
def __iter__(self):