diff options
author | 2016-09-07 11:48:23 +0300 | |
---|---|---|
committer | 2016-09-07 14:08:35 +0300 | |
commit | dbe6b1886ee2b860395731a77da305a01095a843 (patch) | |
tree | 1b3141bf387b1435952d138360e41e28f994d53e /scripts/automation/trex_control_plane/stl/trex_stl_lib | |
parent | 323238656f07cbb4671e1493d8274ccb532588b1 (diff) |
support for DUAL mode on small PCAPs
Diffstat (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib')
-rwxr-xr-x | scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 78 | ||||
-rwxr-xr-x | scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py | 200 |
2 files changed, 261 insertions, 17 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() + |