self code review
authorimarom <[email protected]>
Mon, 28 Nov 2016 14:47:41 +0000 (16:47 +0200)
committerimarom <[email protected]>
Mon, 28 Nov 2016 14:47:41 +0000 (16:47 +0200)
Signed-off-by: imarom <[email protected]>
scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py [new file with mode: 0644]
src/main_dpdk.cpp
src/rpc-server/commands/trex_rpc_cmd_general.cpp
src/trex_port_attr.cpp
src/trex_port_attr.h

index 546298c..c72244a 100755 (executable)
@@ -12,7 +12,7 @@ from .trex_stl_types import *
 from .trex_stl_async_client import CTRexAsyncClient
 
 from .utils import parsing_opts, text_tables, common
-from .utils.common import list_intersect, list_difference, is_sub_list, PassiveTimer, is_valid_ipv4, is_valid_mac, list_remove_dup
+from .utils.common import *
 from .utils.text_opts import *
 from functools import wraps
 
@@ -1793,61 +1793,15 @@ class STLClient(object):
         if not rc:
             raise STLError(rc)
 
-    def test (self):
-        self.resolve()
-        return
-
-        #rc = self.ports[0].resolve()
-        #if not rc:
-        #    raise STLError(rc)
-        #return
-        
-        self.reset(ports = [0])
-        
-        attr = self.ports[0].get_ts_attr()
-        src_ipv4 = attr['src_ipv4']
-        src_mac  = attr['src_mac']
-        dest     = attr['dest']
-        print(src_ipv4, src_mac, dest)
-        #self.set_port_attr(ports = [0, 1], ipv4 = ['5.5.5.5', '6.6.6.6'])
-        return
-        
-        self.set_rx_queue(ports = [0], size = 1000, rxf = 'all')
-
-        #base_pkt = Ether()/ARP()/('x'*50)
-        base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(psrc = '1.1.1.2',pdst = '1.1.1.1', hwsrc = 'a0:36:9f:20:e6:ce')
-        #base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ICMP()
-        
-        print('Sending ARP request on port 0:\n')
-        base_pkt.show2()
-        
-        # send some traffic
-        x = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
-        
-        self.add_streams(streams = [x], ports = [0])
-        self.start(ports = [0], mult = '100%')
-        self.wait_on_traffic(ports = [0])
-        time.sleep(1)
-        
-        pkts = self.get_rx_queue_pkts(ports = [0])
-        
-        print('got back on port 0:\n')
-        for pkt in pkts[0]:
-            Ether(pkt).show2()
-        
-        self.remove_rx_queue(ports = [1])
-        self.set_port_attr(ports = [1], rxf = 'hw')
-        #for pkt in pkts[1]:
-        #    Ether(pkt).show2()
             
 
     @__api_check(True)
-    def ping(self):
+    def ping_rpc_server(self):
         """
-            Pings the server
+            Pings the RPC server
 
             :parameters:
-                 none
+                 None
 
             :raises:
                 + :exc:`STLError`
@@ -1865,9 +1819,9 @@ class STLClient(object):
         
 
     @__api_check(True)
-    def ip_ping (self, src_port, dst_ipv4, pkt_size = 64, count = 5):
+    def ping_ip (self, src_port, dst_ipv4, pkt_size = 64, count = 5):
         """
-            Pings an IP address
+            Pings an IP address through a port
 
             :parameters:
                  src_port - on which port_id to send the ICMP PING request
@@ -1888,11 +1842,12 @@ class STLClient(object):
         with self.logger.supress(level = LoggerApi.VERBOSE_REGULAR_SYNC):
             self.logger.log('')
             for i in range(count):
-                rc = self.ports[src_port].ping(ping_ipv4 = dst_ipv4, pkt_size = pkt_size, retries = 0)
-                if rc:
-                    self.logger.log(rc.data())
-                else:
+                rc = self.ports[src_port].ping(ping_ipv4 = dst_ipv4, pkt_size = pkt_size)
+                if not rc:
                     raise STLError(rc)
+                    
+                self.logger.log(rc.data())
+                
                 if i != (count - 1):
                     time.sleep(1)
         
@@ -2003,18 +1958,33 @@ class STLClient(object):
         ports = ports if ports is not None else self.get_all_ports()
         ports = self._validate_port_list(ports)
 
-        # force take the port and ignore any streams on it
-        self.acquire(ports, force = True, sync_streams = False)
-        self.stop(ports, rx_delay_ms = 0)
-        self.remove_all_streams(ports)
-        self.clear_stats(ports)
-        self.set_port_attr(ports,
-                           promiscuous = False,
-                           link_up = True if restart else None,
-                           rxf = 'hw')
-        self.remove_rx_sniffer(ports)
-        self.remove_rx_queue(ports)
+        
+        if restart:
+            self.logger.pre_cmd("Hard resetting ports {0}:".format(ports))
+        else:
+            self.logger.pre_cmd("Resetting ports {0}:".format(ports))
+        
+        
+        try:
+            with self.logger.supress():
+            # force take the port and ignore any streams on it
+                self.acquire(ports, force = True, sync_streams = False)
+                self.stop(ports, rx_delay_ms = 0)
+                self.remove_all_streams(ports)
+                self.clear_stats(ports)
+                self.set_port_attr(ports,
+                                   promiscuous = False,
+                                   link_up = True if restart else None,
+                                   rxf = 'hw')
+                self.remove_rx_sniffer(ports)
+                self.remove_rx_queue(ports)
+                
+        except STLError as e:
+            self.logger.post_cmd(False)
+            raise e
+                
 
+        self.logger.post_cmd(RC_OK())
         
 
     @__api_check(True)
@@ -2159,11 +2129,11 @@ class STLClient(object):
         
         # verify link status
         ports_link_down = [port_id for port_id in ports if not self.ports[port_id].is_up()]
-        if not force and ports_link_down:
+        if ports_link_down and not force:
             raise STLError("Port(s) %s - link DOWN - check the connection or specify 'force'" % ports_link_down)
         
         # verify ports are stopped or force stop them
-        active_ports = list(set(self.get_active_ports()).intersection(ports))
+        active_ports = [port_id for port_id in ports if self.ports[port_id].is_active()]
         if active_ports and not force:
             raise STLError("Port(s) {0} are active - please stop them or specify 'force'".format(active_ports))
             
@@ -2228,6 +2198,7 @@ class STLClient(object):
         validate_type('core_mask', core_mask, (int, list))
 
       
+        # some sanity checks before attempting start
         self.__pre_start_check(ports, force)
         
         #########################
@@ -2285,7 +2256,7 @@ class STLClient(object):
                 return
 
         ports = self._validate_port_list(ports)
-
+        
         self.logger.pre_cmd("Stopping traffic on port(s) {0}:".format(ports))
         rc = self.__stop(ports)
         self.logger.post_cmd(rc)
@@ -2844,7 +2815,7 @@ class STLClient(object):
         cmn_attr_dict['led_status']      = led_on
         cmn_attr_dict['flow_ctrl_mode']  = flow_ctrl
         cmn_attr_dict['rx_filter_mode']  = rxf
-    
+        
         # each port starts with a set of the common attributes
         attr_dict = [dict(cmn_attr_dict) for _ in ports]
     
@@ -2908,37 +2879,37 @@ class STLClient(object):
             if not ports:
                 raise STLError('No ports configured with destination as IPv4')
             
-        active_ports = list(set(self.get_active_ports()).intersection(ports))
+        ports = self._validate_port_list(ports)
+            
+        active_ports = list_intersect(ports, self.get_active_ports())
         if active_ports:
             raise STLError('Port(s) {0} are active, please stop them before resolving'.format(active_ports))
                      
-        ports = self._validate_port_list(ports)
         
-        self.logger.pre_cmd("Resolving destination on port(s) {0}:".format(ports))
+        self.logger.pre_cmd('Resolving destination on port(s) {0}:'.format(ports))
         with self.logger.supress():
             rc = self.__resolve(ports, retries)
             
         self.logger.post_cmd(rc)
         
-        if rc:
-            self.logger.log(rc)
-            
         if not rc:
             raise STLError(rc)
-      
+
+        # print the ARP transaction
+        self.logger.log(rc)
+        self.logger.log('')
             
             
         
     @__api_check(True)
-    def set_rx_sniffer (self, ports = None, base_filename = 'rx_capture', limit = 1000, rxf = None):
+    def set_rx_sniffer (self, ports = None, base_filename = 'rx.pcap', limit = 1000):
         """
-            Sets RX sniffer for port(s) written to a PCAP file
+            Sets RX sniffer for port(s) written to a PCAP file
 
             :parameters:
                 ports          - for which ports to apply a unique sniffer (each port gets a unique file)
-                base_filename  - filename will be appended with '-<port_number>'
+                base_filename  - filename will be appended with '-<port_number>', e.g. rx.pcap --> rx-0.pcap, rx-1.pcap etc.
                 limit          - limit how many packets will be written
-                rxf            - RX filter mode to use: 'hw' or 'all'
             :raises:
                 + :exe:'STLError'
 
@@ -2952,10 +2923,6 @@ class STLClient(object):
         if limit <= 0:
             raise STLError("'limit' must be a positive value")
 
-        # change RX filter mode if asked
-        if rxf:
-            self.set_port_attr(ports, rxf = rxf)
-            
         self.logger.pre_cmd("Setting RX sniffers on port(s) {0}:".format(ports))
         rc = self.__set_rx_sniffer(ports, base_filename, limit)
         self.logger.post_cmd(rc)
@@ -2987,7 +2954,7 @@ class STLClient(object):
 
     
     @__api_check(True)
-    def set_rx_queue (self, ports = None, size = 1000, rxf = None):
+    def set_rx_queue (self, ports = None, size = 1000):
         """
             Sets RX queue for port(s)
             The queue is cyclic and will hold last 'size' packets
@@ -2995,7 +2962,6 @@ class STLClient(object):
             :parameters:
                 ports          - for which ports to apply a queue
                 size           - size of the queue
-                rxf            - which RX filter to use on those ports: 'hw' or 'all'
             :raises:
                 + :exe:'STLError'
 
@@ -3008,10 +2974,6 @@ class STLClient(object):
         if size <= 0:
             raise STLError("'size' must be a positive value")
 
-        # change RX filter mode if asked
-        if rxf:
-            self.set_port_attr(ports, rxf = rxf)
-            
         self.logger.pre_cmd("Setting RX queue on port(s) {0}:".format(ports))
         rc = self.__set_rx_queue(ports, size)
         self.logger.post_cmd(rc)
@@ -3112,7 +3074,7 @@ class STLClient(object):
         
         # no parameters - so ping server
         if not line:
-            self.ping()
+            self.ping_rpc_server()
             return True
             
         parser = parsing_opts.gen_parser(self,
@@ -3128,7 +3090,7 @@ class STLClient(object):
             return opts
             
         # IP ping
-        self.ip_ping(opts.source_port, opts.ping_ipv4, opts.pkt_size, opts.count)
+        self.ping_ip(opts.source_port, opts.ping_ipv4, opts.pkt_size, opts.count)
 
         
     @__console
@@ -3722,7 +3684,10 @@ class STLClient(object):
 
         rxf = 'all' if opts.all else None 
 
-        self.set_rx_sniffer(opts.ports, opts.output_filename, opts.limit, rxf)
+        if rxf:
+            self.set_port_attr(opts.ports, rxf = rxf)
+            
+        self.set_rx_sniffer(opts.ports, opts.output_filename, opts.limit)
 
 
     @__console
index 1461fce..4ebfa0b 100644 (file)
@@ -172,6 +172,10 @@ class JsonRpcClient(object):
                     self.disconnect()
                     return RC_ERR("*** [RPC] - Failed to send message to server")
 
+            except KeyboardInterrupt as e:
+                # must restore the socket to a sane state
+                self.reconnect()
+                raise e
 
         tries = 0
         while True:
@@ -184,6 +188,10 @@ class JsonRpcClient(object):
                     self.disconnect()
                     return RC_ERR("*** [RPC] - Failed to get server response from {0}".format(self.transport))
 
+            except KeyboardInterrupt as e:
+                # must restore the socket to a sane state
+                self.reconnect()
+                raise e
 
         return response
        
@@ -267,12 +275,6 @@ class JsonRpcClient(object):
         # connect using current values
         return self.connect()
 
-        if not self.connected:
-            return RC_ERR("Not connected to server")
-
-        # reconnect
-        return self.connect(self.server, self.port)
-
 
     def is_connected(self):
         return self.connected
index d225c31..d009253 100644 (file)
@@ -4,17 +4,14 @@ from collections import namedtuple, OrderedDict
 from .trex_stl_packet_builder_scapy import STLPktBuilder
 from .trex_stl_streams import STLStream, STLTXSingleBurst
 from .trex_stl_types import *
+from .trex_stl_rx_features import ARPResolver, PingResolver
 from . import trex_stl_stats
 from .utils.constants import FLOW_CTRL_DICT_REVERSED
 
-from scapy.layers.l2 import Ether, ARP
-from scapy.layers.inet import IP, ICMP
-
 import base64
 import copy
 from datetime import datetime, timedelta
 import threading
-import time
 
 StreamOnPort = namedtuple('StreamOnPort', ['compiled_stream', 'metadata'])
 
@@ -70,7 +67,6 @@ class Port(object):
         self.profile = None
         self.session_id = session_id
         self.status = {}
-        self.__attr = {}
 
         self.port_stats = trex_stl_stats.CPortStats(self)
 
@@ -81,6 +77,7 @@ class Port(object):
         self.owner = ''
         self.last_factor_type = None
         
+        self.__attr = {}
         self.attr_lock = threading.Lock()
         
     # decorator to verify port is up
@@ -100,9 +97,6 @@ class Port(object):
         def func_wrapper(*args, **kwargs):
             port = args[0]
 
-            if not port.is_up():
-                return port.err("{0} - port is down".format(func.__name__))
-
             if not port.is_acquired():
                 return port.err("{0} - port is not owned".format(func.__name__))
 
@@ -116,9 +110,6 @@ class Port(object):
         def func_wrapper(*args, **kwargs):
             port = args[0]
 
-            if not port.is_up():
-                return port.err("{0} - port is down".format(func.__name__))
-
             if not port.is_acquired():
                 return port.err("{0} - port is not owned".format(func.__name__))
 
@@ -511,33 +502,36 @@ class Port(object):
 
         return self.ok()
 
+      
     @owned
-    def set_arp_resolution (self, ipv4, mac):
-
+    def remove_rx_sniffer (self):
         params = {"handler":        self.handler,
                   "port_id":        self.port_id,
-                  "ipv4":           ipv4,
-                  "mac":            mac}
+                  "type":           "capture",
+                  "enabled":        False}
 
-        rc = self.transmit("set_arp_resolution", params)
+        rc = self.transmit("set_rx_feature", params)
         if rc.bad():
             return self.err(rc.err())
 
         return self.ok()
-        
-    
+     
+           
     @owned
-    def remove_rx_sniffer (self):
+    def set_arp_resolution (self, ipv4, mac):
+
         params = {"handler":        self.handler,
                   "port_id":        self.port_id,
-                  "type":           "capture",
-                  "enabled":        False}
+                  "ipv4":           ipv4,
+                  "mac":            mac}
 
-        rc = self.transmit("set_rx_feature", params)
+        rc = self.transmit("set_arp_resolution", params)
         if rc.bad():
             return self.err(rc.err())
 
         return self.ok()
+        
+  
 
 
     @owned
@@ -579,7 +573,7 @@ class Port(object):
 
         pkts = rc.data()['pkts']
         
-        # decode the packets
+        # decode the packets from base64 to binary
         for i in range(len(pkts)):
             pkts[i]['binary'] = base64.b64decode(pkts[i]['binary'])
             
@@ -735,7 +729,7 @@ class Port(object):
         dest = self.__attr['dest']
         
         if dest['type'] != 'mac':
-            return self.set_attr(dest = dest['addr'])
+            return self.set_attr(dest = dest['ipv4'])
         else:
             return self.ok()
         
@@ -851,42 +845,32 @@ class Port(object):
             info['src_ipv4'] = 'Not Configured'
 
         # dest
-        dest = attr.get('dest', {})
-        info['dest'] = dest.get('addr', 'N/A')
-        
+        dest = attr['dest']
         if dest['type'] == 'mac':
-            info['arp'] = '-'
-        else:
-            info['arp']  = dest.get('arp', 'N/A')
-        
-
-        if info['dest'] is None:
-            info['dest'] = 'Not Configured'
-
+            info['dest'] = dest['mac']
+            info['arp']  = '-'
             
-        if info['arp'] is None:
-            info['arp'] = 'unresolved'
-     
+        elif dest['type'] == 'ipv4':
+            info['dest'] = dest['ipv4']
+            info['arp']  = dest['arp']
+            
+        elif dest['type'] == 'ipv4_u':
+            info['dest'] = dest['ipv4']
+            info['arp']  = 'unresolved'
             
             
         # RX info
         rx_info = self.status['rx_info']
 
         # RX sniffer
-        if 'sniffer' in rx_info:
-            sniffer = rx_info['sniffer']
-            info['rx_sniffer'] = '{0}\n[{1} / {2}]'.format(sniffer['pcap_filename'], sniffer['count'], sniffer['limit']) if sniffer['is_active'] else 'off'
-        else:
-            info['rx_sniffer'] = 'N/A'
-
+        sniffer = rx_info['sniffer']
+        info['rx_sniffer'] = '{0}\n[{1} / {2}]'.format(sniffer['pcap_filename'], sniffer['count'], sniffer['limit']) if sniffer['is_active'] else 'off'
+        
 
         # RX queue
-        if 'queue' in rx_info:
-            queue = rx_info['queue']
-            info['rx_queue'] = '[{0} / {1}]'.format(queue['count'], queue['size']) if queue['is_active'] else 'off'
-        else:
-            info['rx_queue'] = 'off'
-
+        queue = rx_info['queue']
+        info['rx_queue'] = '[{0} / {1}]'.format(queue['count'], queue['size']) if queue['is_active'] else 'off'
+        
 
         return info
 
@@ -895,7 +879,7 @@ class Port(object):
         return self.STATES_MAP.get(self.state, "Unknown")
 
     def get_src_addr (self):
-        src_mac = self.__attr['src_mac']
+        src_mac  = self.__attr['src_mac']
         src_ipv4 = self.__attr['src_ipv4']
             
         return {'mac': src_mac, 'ipv4': src_ipv4}
@@ -904,36 +888,34 @@ class Port(object):
     def get_dst_addr (self):
         dest = self.__attr['dest']
         
-        dst_ipv4 = None
-        dst_mac  = None
-        
         if dest['type'] == 'mac':
-            dst_mac = dest['addr']
+            return {'ipv4': None, 'mac': dest['mac']}
+            
         elif dest['type'] == 'ipv4':
-            dst_ipv4 = dest['addr']
-            dst_mac  = dest['arp']
+            return {'ipv4': dest['ipv4'], 'mac': dest['arp']}
+            
+        elif dest['type'] == 'ipv4_u':
+            return {'ipv4': dest['ipv4'], 'mac': None}
+            
         else:
             assert(0)
-                                       
-            
-        return {'ipv4': dst_ipv4, 'mac' : dst_mac}
-  
     
-    # return True if the port is resolved (either has MAC as dest of ARP resolution)
+        
+    # return True if the port is resolved
     def is_resolved (self):
-        return (self.get_dst_addr()['mac'] != None)
+        return (self.get_dst_addr()['mac'] is not None)
     
     # return True if the port is valid for resolve (has an IPv4 address as dest)
     def is_resolvable (self):
-        return (self.get_dst_addr()['ipv4'] != None)
+        return (self.get_dst_addr()['ipv4'] is not None)
         
     @writeable
     def arp_resolve (self, retries):
         return ARPResolver(self).resolve(retries)
 
     @writeable
-    def ping (self, ping_ipv4, pkt_size, retries):
-        return PingResolver(self, ping_ipv4, pkt_size).resolve(retries)
+    def ping (self, ping_ipv4, pkt_size):
+        return PingResolver(self, ping_ipv4, pkt_size).resolve()
 
         
     ################# stats handler ######################
@@ -1075,248 +1057,4 @@ class Port(object):
     def async_event_released (self):
         self.owner = ''
 
-# a generic abstract class for resolving using the server
-class Resolver(object):
-    def __init__ (self, port, queue_size = 100):
-        self.port = port
-     
-    # code to execute before sending any request - return RC object
-    def pre_send (self):
-        raise NotImplementedError()
-        
-    # return a list of streams for request
-    def generate_request (self):
-        raise NotImplementedError()
-        
-    # return None for more packets otherwise RC object
-    def on_pkt_rx (self, pkt):
-        raise NotImplementedError()
-    
-    # return value in case of timeout
-    def on_timeout_err (self, retries):
-        raise NotImplementedError()
-    
-    ##################### API ######################
-    def resolve (self, retries = 0):
-
-        # first cleanup
-        rc = self.port.remove_all_streams()
-        if not rc:
-            return rc
-
-        # call the specific class implementation
-        rc = self.pre_send()
-        if not rc:
-            return rc
-
-        # start the iteration
-        try:
-
-            # add the stream(s)
-            self.port.add_streams(self.generate_request())
-
-            rc = self.port.set_attr(rx_filter_mode = 'all')
-            if not rc:
-                return rc
-                
-            rc = self.port.set_rx_queue(size = 100)
-            if not rc:
-                return rc
-            
-            return self.resolve_wrapper(retries)
-            
-        finally:
-            # best effort restore
-            self.port.set_attr(rx_filter_mode = 'hw')
-            self.port.remove_rx_queue()
-            self.port.remove_all_streams()
-                
-    
-    # main resolve function
-    def resolve_wrapper (self, retries):
-            
-        # retry for 'retries'
-        index = 0
-        while True:
-            rc = self.resolve_iteration()
-            if rc is not None:
-                return rc
-            
-            if index >= retries:
-                return self.on_timeout_err(retries)
-                
-            index += 1
-            time.sleep(0.1)
-            
-            
-
-    def resolve_iteration (self):
-        
-        mult = {'op': 'abs', 'type' : 'percentage', 'value': 100}
-        rc = self.port.start(mul = mult, force = False, duration = -1, mask = 0xffffffff)
-        if not rc:
-            return rc
-
-        # save the start timestamp
-        self.start_ts = rc.data()['ts']
-        
-        # block until traffic finishes
-        while self.port.is_active():
-            time.sleep(0.01)
-
-        return self.wait_for_rx_response()
-        
-             
-    def wait_for_rx_response (self):
-
-        # we try to fetch response for 5 times
-        polling = 5
-        
-        while polling > 0:
-            # fetch the queue
-            rx_pkts = self.port.get_rx_queue_pkts()
-            
-            # for each packet - examine it
-            for pkt in rx_pkts:
-                rc = self.on_pkt_rx(pkt)
-                if rc is not None:
-                    return rc
-                
-            if polling == 0:
-                return None
-                
-            polling -= 1
-            time.sleep(0.1)
-          
-        
-        
-        
-class ARPResolver(Resolver):
-    def __init__ (self, port_id):
-        super(ARPResolver, self).__init__(port_id)
-        
-    # before resolve
-    def pre_send (self):
-        dst = self.port.get_dst_addr()
-        src = self.port.get_src_addr()
-        
-        if dst['ipv4'] is None:
-            return self.port.err("Port has a non-IPv4 destination: '{0}'".format(dst['mac']))
-            
-        if src['ipv4'] is None:
-            return self.port.err('Port must have an IPv4 source address configured')
-
-        # invalidate the current ARP resolution (if exists)
-        return self.port.invalidate_arp()
-        
-
-    # return a list of streams for request
-    def generate_request (self):
-                
-        dst       = self.port.get_dst_addr()
-        src       = self.port.get_src_addr()
-        
-        base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(psrc = src['ipv4'], pdst = dst['ipv4'], hwsrc = src['mac'])
-        s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
-
-        return [s1]
-
-
-    # return None in case more packets are needed else the status rc
-    def on_pkt_rx (self, pkt):
-        scapy_pkt = Ether(pkt['binary'])
-        if not 'ARP' in scapy_pkt:
-            return None
-
-        arp = scapy_pkt['ARP']
-        dst = self.port.get_dst_addr()
-
-        # check this is the right ARP (ARP reply with the address)
-        if (arp.op != 2) or (arp.psrc != dst['ipv4']):
-            return None
-
-        
-        rc = self.port.set_arp_resolution(arp.psrc, arp.hwsrc)
-        if not rc:
-            return rc
-            
-        return self.port.ok('Port {0} - Recieved ARP reply from: {1}, hw: {2}'.format(self.port.port_id, arp.psrc, arp.hwsrc))
-        
 
-    def on_timeout_err (self, retries):
-        return self.port.err('failed to receive ARP response ({0} retries)'.format(retries))
-
-
-        
-    #################### ping resolver ####################
-           
-class PingResolver(Resolver):
-    def __init__ (self, port, ping_ip, pkt_size):
-        super(PingResolver, self).__init__(port)
-        self.ping_ip = ping_ip
-        self.pkt_size = pkt_size
-                
-    def pre_send (self):
-            
-        src = self.port.get_src_addr()
-        dst = self.port.get_dst_addr()
-        if src['ipv4'] is None:
-            return self.port.err('Ping - port does not have an IPv4 address configured')
-            
-        if dst['mac'] is None:
-            return self.port.err('Ping - port has an unresolved destination, cannot determine next hop MAC address')
-        
-        if self.ping_ip == src['ipv4']:
-            return self.port.err('Ping - cannot ping own IP')
-            
-        return self.port.ok()
-            
-        
-    # return a list of streams for request
-    def generate_request (self):
-                    
-        src = self.port.get_src_addr()
-        dst = self.port.get_dst_addr()
-              
-        base_pkt = Ether(dst = dst['mac'])/IP(src = src['ipv4'], dst = self.ping_ip)/ICMP(type = 8)
-        pad = max(0, self.pkt_size - len(base_pkt))
-        
-        base_pkt = base_pkt / (pad * 'x')
-        
-        #base_pkt.show2()
-        s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
-
-        return [s1]
-        
-    # return None for more packets otherwise RC object
-    def on_pkt_rx (self, pkt):
-        scapy_pkt = Ether(pkt['binary'])
-        if not 'ICMP' in scapy_pkt:
-            return None
-        
-        #scapy_pkt.show2()    
-        ip = scapy_pkt['IP']
-        
-        icmp = scapy_pkt['ICMP']
-        
-        dt = pkt['ts'] - self.start_ts
-        
-        if icmp.type == 0:
-            # echo reply
-            return self.port.ok('Reply from {0}: bytes={1}, time={2:.2f}ms, TTL={3}'.format(ip.src, len(pkt['binary']), dt * 1000, ip.ttl))
-            
-        # unreachable
-        elif icmp.type == 3:
-            return self.port.ok('Reply from {0}: Destination host unreachable'.format(icmp.src))
-        else:
-            scapy_pkt.show2()
-            return self.port.err('unknown ICMP reply')
-            
-            
-    
-    # return the str of a timeout err
-    def on_timeout_err (self, retries):
-        return self.port.ok('Request timed out.')
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py
new file mode 100644 (file)
index 0000000..f9f6a49
--- /dev/null
@@ -0,0 +1,255 @@
+
+from .trex_stl_streams import STLStream, STLTXSingleBurst
+from .trex_stl_packet_builder_scapy import STLPktBuilder
+
+from scapy.layers.l2 import Ether, ARP
+from scapy.layers.inet import IP, ICMP
+
+import time
+# a generic abstract class for resolving using the server
+class Resolver(object):
+    def __init__ (self, port, queue_size = 100):
+        self.port = port
+     
+    # code to execute before sending any request - return RC object
+    def pre_send (self):
+        raise NotImplementedError()
+        
+    # return a list of streams for request
+    def generate_request (self):
+        raise NotImplementedError()
+        
+    # return None for more packets otherwise RC object
+    def on_pkt_rx (self, pkt):
+        raise NotImplementedError()
+    
+    # return value in case of timeout
+    def on_timeout_err (self, retries):
+        raise NotImplementedError()
+    
+    ##################### API ######################
+    def resolve (self, retries = 0):
+
+        # first cleanup
+        rc = self.port.remove_all_streams()
+        if not rc:
+            return rc
+
+        # call the specific class implementation
+        rc = self.pre_send()
+        if not rc:
+            return rc
+
+        # start the iteration
+        try:
+
+            # add the stream(s)
+            self.port.add_streams(self.generate_request())
+
+            rc = self.port.set_attr(rx_filter_mode = 'all')
+            if not rc:
+                return rc
+                
+            rc = self.port.set_rx_queue(size = 100)
+            if not rc:
+                return rc
+            
+            return self.resolve_wrapper(retries)
+            
+        finally:
+            # best effort restore
+            self.port.set_attr(rx_filter_mode = 'hw')
+            self.port.remove_rx_queue()
+            self.port.remove_all_streams()
+                
+    
+    # main resolve function
+    def resolve_wrapper (self, retries):
+            
+        # retry for 'retries'
+        index = 0
+        while True:
+            rc = self.resolve_iteration()
+            if rc is not None:
+                return rc
+            
+            if index >= retries:
+                return self.on_timeout_err(retries)
+                
+            index += 1
+            time.sleep(0.1)
+            
+            
+
+    def resolve_iteration (self):
+        
+        mult = {'op': 'abs', 'type' : 'percentage', 'value': 100}
+        rc = self.port.start(mul = mult, force = False, duration = -1, mask = 0xffffffff)
+        if not rc:
+            return rc
+
+        # save the start timestamp
+        self.start_ts = rc.data()['ts']
+        
+        # block until traffic finishes
+        while self.port.is_active():
+            time.sleep(0.01)
+
+        return self.wait_for_rx_response()
+        
+             
+    def wait_for_rx_response (self):
+
+        # we try to fetch response for 5 times
+        polling = 5
+        
+        while polling > 0:
+            
+            # fetch the queue
+            rx_pkts = self.port.get_rx_queue_pkts()
+            
+            # might be an error
+            if not rx_pkts:
+                return rx_pkts
+                
+            # for each packet - examine it
+            for pkt in rx_pkts:
+                rc = self.on_pkt_rx(pkt)
+                if rc is not None:
+                    return rc
+                
+            if polling == 0:
+                return None
+                
+            polling -= 1
+            time.sleep(0.1)
+          
+        
+        
+        
+class ARPResolver(Resolver):
+    def __init__ (self, port_id):
+        super(ARPResolver, self).__init__(port_id)
+        
+    # before resolve
+    def pre_send (self):
+        self.dst = self.port.get_dst_addr()
+        self.src = self.port.get_src_addr()
+        
+        if self.dst['ipv4'] is None:
+            return self.port.err("Port has a non-IPv4 destination: '{0}'".format(dst['mac']))
+            
+        if self.src['ipv4'] is None:
+            return self.port.err('Port must have an IPv4 source address configured')
+
+        # invalidate the current ARP resolution (if exists)
+        return self.port.invalidate_arp()
+        
+
+    # return a list of streams for request
+    def generate_request (self):
+                
+        base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(psrc = self.src['ipv4'], pdst = self.dst['ipv4'], hwsrc = self.src['mac'])
+        s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
+
+        return [s1]
+
+
+    # return None in case more packets are needed else the status rc
+    def on_pkt_rx (self, pkt):
+        scapy_pkt = Ether(pkt['binary'])
+        if not 'ARP' in scapy_pkt:
+            return None
+
+        arp = scapy_pkt['ARP']
+
+        # check this is the right ARP (ARP reply with the address)
+        if (arp.op != 2) or (arp.psrc != self.dst['ipv4']):
+            return None
+
+        
+        rc = self.port.set_arp_resolution(arp.psrc, arp.hwsrc)
+        if not rc:
+            return rc
+            
+        return self.port.ok('Port {0} - Recieved ARP reply from: {1}, hw: {2}'.format(self.port.port_id, arp.psrc, arp.hwsrc))
+        
+
+    def on_timeout_err (self, retries):
+        return self.port.err('failed to receive ARP response ({0} retries)'.format(retries))
+
+
+        
+    #################### ping resolver ####################
+           
+class PingResolver(Resolver):
+    def __init__ (self, port, ping_ip, pkt_size):
+        super(PingResolver, self).__init__(port)
+        self.ping_ip = ping_ip
+        self.pkt_size = pkt_size
+                
+    def pre_send (self):
+            
+        self.src = self.port.get_src_addr()
+        self.dst = self.port.get_dst_addr()
+        
+        if self.src['ipv4'] is None:
+            return self.port.err('Ping - port does not have an IPv4 address configured')
+            
+        if self.dst['mac'] is None:
+            return self.port.err('Ping - port has an unresolved destination, cannot determine next hop MAC address')
+        
+        if self.ping_ip == self.src['ipv4']:
+            return self.port.err('Ping - cannot ping own IP')
+            
+        return self.port.ok()
+            
+        
+    # return a list of streams for request
+    def generate_request (self):
+                    
+        src = self.port.get_src_addr()
+        dst = self.port.get_dst_addr()
+              
+        base_pkt = Ether(dst = dst['mac'])/IP(src = src['ipv4'], dst = self.ping_ip)/ICMP(type = 8)
+        pad = max(0, self.pkt_size - len(base_pkt))
+        
+        base_pkt = base_pkt / (pad * 'x')
+        
+        #base_pkt.show2()
+        s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
+
+        return [s1]
+        
+    # return None for more packets otherwise RC object
+    def on_pkt_rx (self, pkt):
+        scapy_pkt = Ether(pkt['binary'])
+        if not 'ICMP' in scapy_pkt:
+            return None
+        
+        #scapy_pkt.show2()    
+        ip = scapy_pkt['IP']
+        
+        icmp = scapy_pkt['ICMP']
+        
+        dt = pkt['ts'] - self.start_ts
+        
+        if icmp.type == 0:
+            # echo reply
+            return self.port.ok('Reply from {0}: bytes={1}, time={2:.2f}ms, TTL={3}'.format(ip.src, len(pkt['binary']), dt * 1000, ip.ttl))
+            
+        # unreachable
+        elif icmp.type == 3:
+            return self.port.ok('Reply from {0}: Destination host unreachable'.format(icmp.src))
+        else:
+            scapy_pkt.show2()
+            return self.port.err('unknown ICMP reply')
+            
+            
+    
+    # return the str of a timeout err
+    def on_timeout_err (self, retries):
+        return self.port.ok('Request timed out.')
index 2aab7f5..1341def 100644 (file)
@@ -3345,9 +3345,9 @@ void CGlobalTRex::pre_test() {
            uint32_t dg = CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw();
            const uint8_t *dst_mac = CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest;
            if (dg) {
-               m_ports[port_id].get_port_attr()->get_dest().set_dest_ipv4(dg, dst_mac);
+               m_ports[port_id].get_port_attr()->get_dest().set_dest(dg, dst_mac);
            } else {
-               m_ports[port_id].get_port_attr()->get_dest().set_dest_mac(dst_mac);
+               m_ports[port_id].get_port_attr()->get_dest().set_dest(dst_mac);
            }
         
         }
@@ -4932,7 +4932,7 @@ bool CPhyEthIF::Create(uint8_t portid) {
     /* for now set as unresolved IPv4 destination */
     uint32_t dest_ipv4 = CGlobalInfo::m_options.m_ip_cfg[m_port_id].get_def_gw();
     if (dest_ipv4) {
-        m_port_attr->get_dest().set_dest_ipv4(dest_ipv4);
+        m_port_attr->get_dest().set_dest(dest_ipv4);
     }
     
     return true;
index 11dd99b..ccdcbd8 100644 (file)
@@ -385,10 +385,10 @@ TrexRpcCmdSetPortAttr::parse_dest(const Json::Value &msg, uint8_t port_id, Json:
     uint8_t  mac[6];
     
     if (utl_ipv4_to_uint32(addr.c_str(), ipv4_addr)) {
-        port_attr->get_dest().set_dest_ipv4(ipv4_addr);
+        port_attr->get_dest().set_dest(ipv4_addr);
         
     } else if (utl_str_to_macaddr(addr, mac)) {
-        port_attr->get_dest().set_dest_mac(mac);
+        port_attr->get_dest().set_dest(mac);
         
     } else {
         std::stringstream ss;
@@ -850,7 +850,7 @@ TrexRpcCmdSetARPRes::_run(const Json::Value &params, Json::Value &result) {
         generate_parse_err(result, ss.str());
     }   
     
-    port->getPortAttrObj()->get_dest().set_dest_ipv4(ipv4_addr, mac);
+    port->getPortAttrObj()->get_dest().set_dest(ipv4_addr, mac);
     
     return (TREX_RPC_CMD_OK);
     
index 20b441e..b215a24 100644 (file)
@@ -17,13 +17,77 @@ limitations under the License.
 #include "trex_port_attr.h"
 #include "bp_sim.h"
 
-const uint8_t DestAttr::g_dummy_mac[6] = {0x0,0x0,0x0,0x1,0x0,0x0};
-
-
 DestAttr::DestAttr(uint8_t port_id) {
     m_port_id = port_id;
-
+    
     m_mac = CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest;
+    m_type = DEST_TYPE_MAC;
+    
+    /* save the default */
+    memcpy(m_default_mac, m_mac, 6);
+}
+
+/**
+ * set dest as an IPv4 unresolved
+ */
+void 
+DestAttr::set_dest(uint32_t ipv4) {
+    assert(ipv4 != 0);
+
+    m_ipv4 = ipv4;
+    memset(m_mac, 0, 6); // just to be on the safe side
+    m_type = DEST_TYPE_IPV4_UNRESOLVED;
+}
+
+/**
+ * set dest as a resolved IPv4
+ */
+void
+DestAttr::set_dest(uint32_t ipv4, const uint8_t *mac) {
+    assert(ipv4 != 0);
+
+    m_ipv4 = ipv4;
+    memcpy(m_mac, mac, 6);
+    m_type = DEST_TYPE_IPV4;
+}
+
+/**
+ * dest dest as MAC
+ * 
+ */
+void
+DestAttr::set_dest(const uint8_t *mac) {
+
+    m_ipv4 = 0;
+    memcpy(m_mac, mac, 6);
+    m_type = DEST_TYPE_MAC;
+}
+
+void
+DestAttr::to_json(Json::Value &output) const {
+    switch (m_type) {
+    
+    case DEST_TYPE_IPV4:
+        output["type"] = "ipv4";
+        output["ipv4"] = utl_uint32_to_ipv4(m_ipv4);
+        output["arp"]  = utl_macaddr_to_str(m_mac);
+        break;
+        
+    case DEST_TYPE_IPV4_UNRESOLVED:
+        output["type"] = "ipv4_u";
+        output["ipv4"] = utl_uint32_to_ipv4(m_ipv4);
+        break;
+
+    case DEST_TYPE_MAC:
+        output["type"] = "mac";
+        output["mac"]  = utl_macaddr_to_str(m_mac);
+        break;
+
+    default:
+        assert(0);
+    }
+
 }
 
 const uint8_t *
index 95050d1..3cb9bef 100755 (executable)
@@ -31,79 +31,50 @@ limitations under the License.
  * 
  */
 class DestAttr {
-private:
-    static const uint8_t g_dummy_mac[6];
+    
 public:
 
     DestAttr(uint8_t port_id);
     
+    /**
+     * dest can be either MAC IPv4, or IPv4 unresolved
+     */
     enum dest_type_e {
-        DEST_TYPE_IPV4   = 1,
-        DEST_TYPE_MAC    = 2
+        DEST_TYPE_IPV4 = 1,
+        DEST_TYPE_IPV4_UNRESOLVED,
+        DEST_TYPE_MAC,
     };
     
     /**
      * set dest as an IPv4 unresolved
      */
-    void set_dest_ipv4(uint32_t ipv4) {
-        assert(ipv4 != 0);
-        
-        m_src_ipv4 = ipv4;
-        memset(m_mac, 0, 6);
-        m_type = DEST_TYPE_IPV4;
-    }
+    void set_dest(uint32_t ipv4);
     
     /**
-     * set dest as a resolved IPv4
+     * set dest as a resolved IPv4 
      */
-    void set_dest_ipv4(uint32_t ipv4, const uint8_t *mac) {
-        assert(ipv4 != 0);
-        
-        m_src_ipv4 = ipv4;
-        memcpy(m_mac, mac, 6);
-        m_type = DEST_TYPE_IPV4;
-        
-    }
-
+    void set_dest(uint32_t ipv4, const uint8_t *mac);
+    
     /**
-     * dest dest as MAC
-     * 
+     * set dest as a plain MAC
      */
-    void set_dest_mac(const uint8_t *mac) {
-
-        m_src_ipv4 = 0;
-        memcpy(m_mac, mac, 6);
-        m_type = DEST_TYPE_MAC;
-    }
-    
+    void set_dest(const uint8_t *mac);
     
+   
+    /**
+     * return true if destination is resolved
+     */
     bool is_resolved() const {
-        if (m_type == DEST_TYPE_MAC) {
-            return true;
-        }
-        
-        for (int i = 0; i < 6; i++) {
-            if (m_mac[i] != 0) {
-                return true;
-            }
-        }
-        
-        /* all zeroes - non resolved */
-        return false;
+        return (m_type != DEST_TYPE_IPV4_UNRESOLVED);
     }
     
     /**
-     * get the dest mac
-     * if no MAC is configured and dest was not resolved 
-     * will return a dummy 
+     * get the dest mac 
+     * if the dest is not resolved 
+     * it will return the default MAC 
      */
     const uint8_t *get_dest_mac() {
-        
-        if (is_resolved()) {
-            return m_mac;
-        } else {
-            return g_dummy_mac;
-        }
+        return m_mac;
     }
     
     /**
@@ -113,39 +84,20 @@ public:
     void on_link_down() {
         if (m_type == DEST_TYPE_IPV4) {
             /* reset the IPv4 dest with no resolution */
-            set_dest_ipv4(m_src_ipv4);
+            set_dest(m_ipv4);
         }
     }
     
-    void to_json(Json::Value &output) {
-        switch (m_type) {
-
-        case DEST_TYPE_IPV4:
-            output["type"] = "ipv4";
-            output["addr"] = utl_uint32_to_ipv4(m_src_ipv4);
-            if (is_resolved()) {
-                output["arp"] = utl_macaddr_to_str(m_mac);
-            } else {
-                output["arp"] = Json::nullValue;
-            }
-            break;
-            
-        case DEST_TYPE_MAC:
-            output["type"]      = "mac";
-            output["addr"]      = utl_macaddr_to_str(m_mac);
-            break;
-            
-        default:
-            assert(0);
-        }
-        
-    }
+    void to_json(Json::Value &output) const;
     
 private:
-    uint32_t          m_src_ipv4;
+    uint32_t          m_ipv4;
     uint8_t          *m_mac;
     dest_type_e       m_type;
     uint8_t           m_port_id;
+    
+private:
+    uint8_t m_default_mac[6];
 };