RX features phase 2 - ARP and ICMP self response 82/4282/1
authorimarom <[email protected]>
Tue, 6 Dec 2016 13:29:55 +0000 (15:29 +0200)
committerimarom <[email protected]>
Tue, 6 Dec 2016 13:31:28 +0000 (15:31 +0200)
Signed-off-by: imarom <[email protected]>
16 files changed:
scripts/automation/trex_control_plane/stl/console/trex_console.py
scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_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
scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
src/bp_sim.h
src/common/Network/Packet/Arp.h
src/common/Network/Packet/EthernetHeader.h
src/common/Network/Packet/IcmpHeader.h
src/common/Network/Packet/MacAddress.h
src/main_dpdk.cpp
src/stateless/cp/trex_exception.h
src/stateless/rx/trex_stateless_rx_core.cpp
src/stateless/rx/trex_stateless_rx_port_mngr.cpp
src/stateless/rx/trex_stateless_rx_port_mngr.h
src/trex_port_attr.h

index b33b044..5ea959a 100755 (executable)
@@ -488,7 +488,7 @@ class TRexConsole(TRexGeneralCmd):
     ############# update
     @verify_connected
     def do_update(self, line):
-        '''update speed of port(s)currently transmitting traffic\n'''
+        '''update speed of port(s) currently transmitting traffic\n'''
 
         self.stateless_client.update_line(line)
 
@@ -549,6 +549,13 @@ class TRexConsole(TRexGeneralCmd):
         '''Clear cached local statistics\n'''
         self.stateless_client.clear_stats_line(line)
 
+    @verify_connected
+    def do_service (self, line):
+        '''Sets port(s) service mode state'''
+        self.stateless_client.service_line(line)
+        
+    def help_service (self, line):
+        self.do_service("-h")
 
     def help_clear(self):
         self.do_clear("-h")
index cc20e08..fd41491 100755 (executable)
@@ -878,6 +878,15 @@ class STLClient(object):
 
         return rc
 
+    def __set_service_mode (self, port_id_list, enabled):
+        port_id_list = self.__ports(port_id_list)
+        rc = RC()
+
+        for port_id in port_id_list:
+            rc.add(self.ports[port_id].set_service_mode(enabled))
+
+        return rc
+        
 
     # connect to server
     def __connect(self):
@@ -1387,6 +1396,12 @@ class STLClient(object):
                 if port_obj.is_acquired() and port_obj.get_dst_addr()['ipv4'] is not None]
          
          
+    def get_service_enabled_ports(self):
+        return [port_id
+                for port_id, port_obj in self.ports.items()
+                if port_obj.is_acquired() and port_obj.is_service_mode_on()]
+
+        
     # get paused ports
     def get_paused_ports (self, owned = True):
         if owned:
@@ -1854,9 +1869,7 @@ class STLClient(object):
             raise STLError("pkt_size should be a value between 64 and 9216: '{0}'".format(pkt_size))
             
         validate_type('count', count, int)
-            
-        
-            
         self.logger.pre_cmd("Pinging {0} from port {1} with {2} bytes of data:".format(dst_ipv4,
                                                                                        src_port,
                                                                                        pkt_size))
@@ -2164,7 +2177,10 @@ class STLClient(object):
         unresolved_ports = [port_id for port_id in ports if not self.ports[port_id].is_resolved()]
         if unresolved_ports and not force:
             raise STLError("Port(s) {0} have unresolved destination addresses - please resolve them or specify 'force'".format(unresolved_ports))
-        
+     
+        if self.get_service_enabled_ports() and not force:
+            raise STLError("Port(s) {0} are under service mode - please disable service mode or specify 'force'".format(self.get_service_enabled_ports()))
+            
         
     @__api_check(True)
     def start (self,
@@ -2827,7 +2843,7 @@ class STLClient(object):
         validate_type('led_on', led_on, (bool, type(None)))
         validate_type('flow_ctrl', flow_ctrl, (int, type(None)))
         validate_choice('rxf', rxf, ['hw', 'all'])
-        
+    
         # common attributes for all ports
         cmn_attr_dict = {}
 
@@ -2880,7 +2896,35 @@ class STLClient(object):
                 self.resolve(ports = resolve_ports)
         
             
+    
+    @__api_check(True)
+    def set_service_mode (self, ports = None, enabled = True):
+        """
+            Set service mode for port(s)
+            In service mode ports will respond to ARP, PING and etc.
+
+            :parameters:
+                ports          - for which ports to configure service mode on/off
+                enabled        - True for activating service mode, False for disabling
+            :raises:
+                + :exe:'STLError'
+
+        """
+        # by default take all acquired ports
+        ports = ports if ports is not None else self.get_acquired_ports()
+        ports = self._validate_port_list(ports)
+        
+        if enabled:
+            self.logger.pre_cmd('Enabling service mode on port(s) {0}:'.format(ports))
+        else:
+            self.logger.pre_cmd('Disabling service mode on port(s) {0}:'.format(ports))
+            
+        rc = self.__set_service_mode(ports, enabled)
+        self.logger.post_cmd(rc)
         
+        if not rc:
+            raise STLError(rc)
+            
 
     @__api_check(True)
     def resolve (self, ports = None, retries = 0):
@@ -3078,7 +3122,7 @@ class STLClient(object):
             try:
                 rc = f(*args)
             except STLError as e:
-                client.logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold'))
+                client.logger.log("Action has failed with the following error:\n" + format_text(e.brief() + "\n", 'bold'))
                 return RC_ERR(e.brief())
 
             # if got true - print time
@@ -3697,18 +3741,12 @@ class STLClient(object):
                                          self.set_rx_sniffer_line.__doc__,
                                          parsing_opts.PORT_LIST_WITH_ALL,
                                          parsing_opts.OUTPUT_FILENAME,
-                                         parsing_opts.LIMIT,
-                                         parsing_opts.ALL_FILES)
+                                         parsing_opts.LIMIT)
 
         opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True)
         if not opts:
             return opts
 
-        rxf = 'all' if opts.all else None 
-
-        if rxf:
-            self.set_port_attr(opts.ports, rxf = rxf)
-            
         self.set_rx_sniffer(opts.ports, opts.output_filename, opts.limit)
 
         return RC_OK()
@@ -3840,10 +3878,37 @@ class STLClient(object):
             return "{0}(read-only)>".format(prefix)
 
         elif self.is_all_ports_acquired():
-            return "{0}>".format(prefix)
+            p = prefix
+            
+            if self.get_service_enabled_ports():
+                if self.get_service_enabled_ports() == self.get_acquired_ports():
+                    p += '(service)'
+                else:
+                    p += '(service: {0})'.format(self.get_service_enabled_ports())
+                
+            return "{0}>".format(p)
 
         else:
             return "{0} {1}>".format(prefix, self.get_acquired_ports())
             
             
 
+    @__console
+    def service_line (self, line):
+        '''Configures port for service mode.
+           In service mode ports will reply to ARP, PING
+           and etc.
+        '''
+
+        parser = parsing_opts.gen_parser(self,
+                                         "service",
+                                         self.service_line.__doc__,
+                                         parsing_opts.PORT_LIST_WITH_ALL,
+                                         parsing_opts.SERVICE_OFF)
+
+        opts = parser.parse_args(line.split())
+        if not opts:
+            return opts
+            
+        self.set_service_mode(ports = opts.ports, enabled = opts.enabled)
+        
index 389a942..4ac31fb 100644 (file)
@@ -489,6 +489,9 @@ class Port(object):
     @owned
     def set_rx_sniffer (self, pcap_filename, limit):
 
+        if not self.is_service_mode_on():
+            return self.err('port service mode must be enabled for performing RX capturing')
+            
         params = {"handler":        self.handler,
                   "port_id":        self.port_id,
                   "type":           "capture",
@@ -529,7 +532,8 @@ class Port(object):
         if rc.bad():
             return self.err(rc.err())
 
-        return self.ok()
+        # instead of updating manualy - let's sync with the server
+        return self.sync()
         
   
 
@@ -684,6 +688,9 @@ class Port(object):
             json_attr['ipv4'] = {'addr': kwargs.get('ipv4')}
         
         if kwargs.get('dest') is not None:
+            if not self.is_service_mode_on():
+                return self.err('setting destination requires port to be in service mode')
+                
             json_attr['dest'] = {'addr': kwargs.get('dest')}
             
 
@@ -698,6 +705,26 @@ class Port(object):
         # update the dictionary from the server explicitly
         return self.sync()
 
+    
+    @owned
+    def set_service_mode (self, enabled):
+        rc = self.set_attr(rx_filter_mode = 'all' if enabled else 'hw')
+        if not rc:
+            return rc
+            
+        if not enabled:
+            rc = self.remove_rx_queue()
+            if not rc:
+                return rc
+                
+            rc = self.remove_rx_sniffer()
+            if not rc:
+                return rc
+                
+        return self.ok()
+
+    def is_service_mode_on (self):
+        return self.get_rx_filter_mode() == 'all'
                 
     @writeable
     def push_remote (self, pcap_filename, ipg_usec, speedup, count, duration, is_dual, slave_handler):
@@ -877,6 +904,8 @@ class Port(object):
             
         return {'mac': src_mac, 'ipv4': src_ipv4}
         
+    def get_rx_filter_mode (self):
+        return self.__attr['rx_filter_mode']
         
     def get_dst_addr (self):
         dest = self.__attr['dest']
@@ -904,10 +933,16 @@ class Port(object):
         
     @writeable
     def arp_resolve (self, retries):
+        if not self.is_service_mode_on():
+            return self.err('port service mode must be enabled for performing ARP resolution')
+            
         return ARPResolver(self).resolve(retries)
 
     @writeable
     def ping (self, ping_ipv4, pkt_size):
+        if not self.is_service_mode_on():
+            return self.err('port service mode must be enabled for performing ping')
+            
         return PingResolver(self, ping_ipv4, pkt_size).resolve()
 
         
index 3754e60..e0fc172 100644 (file)
@@ -46,11 +46,6 @@ class Resolver(object):
 
             # 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
@@ -59,7 +54,6 @@ class Resolver(object):
             
         finally:
             # best effort restore
-            self.port.set_attr(rx_filter_mode = 'hw')
             self.port.remove_rx_queue()
             self.port.remove_all_streams()
                 
@@ -202,19 +196,13 @@ class PingResolver(Resolver):
         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)
+        base_pkt = Ether(dst = self.dst['mac'])/IP(src = self.src['ipv4'], dst = self.ping_ip)/ICMP(type = 8)
         pad = max(0, self.pkt_size - len(base_pkt))
         
         base_pkt = base_pkt / (pad * 'x')
@@ -222,6 +210,8 @@ class PingResolver(Resolver):
         #base_pkt.show2()
         s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
 
+        self.base_pkt = base_pkt
+        
         return [s1]
         
     # return None for more packets otherwise RC object
@@ -230,23 +220,32 @@ class PingResolver(Resolver):
         if not 'ICMP' in scapy_pkt:
             return None
         
-        #scapy_pkt.show2()    
         ip = scapy_pkt['IP']
-        
+        if ip.dst != self.src['ipv4']:
+            return None
+            
         icmp = scapy_pkt['ICMP']
         
         dt = pkt['ts'] - self.start_ts
         
+        # echo reply
         if icmp.type == 0:
-            # echo reply
+            # check seq
+            if icmp.seq != self.base_pkt['ICMP'].seq:
+                return None
             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:
+            # check seq
+            if icmp.payload.seq != self.base_pkt['ICMP'].seq:
+                return None
             return self.port.ok('Reply from {0}: Destination host unreachable'.format(icmp.src))
+            
         else:
-            scapy_pkt.show2()
-            return self.port.err('unknown ICMP reply')
+            # skip any other types
+            #scapy_pkt.show2()
+            return None
             
             
     
index 7ae22e8..66a17a0 100755 (executable)
@@ -60,6 +60,8 @@ PING_IPV4 = 40
 PING_COUNT = 41
 PKT_SIZE = 42
 
+SERVICE_OFF = 43
+
 GLOBAL_STATS = 50
 PORT_STATS = 51
 PORT_STATUS = 52
@@ -575,6 +577,12 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
                                        'default': None,
                                        'help': "Core mask - only cores responding to the bit mask will be active"}),
 
+              SERVICE_OFF: ArgumentPack(['--off'],
+                                        {'action': 'store_false',
+                                         'dest': 'enabled',
+                                         'default': True,
+                                         'help': 'Deactivates services on port(s)'}),
+                
               # advanced options
               PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST,
                                                         ALL_PORTS],
index 914a26d..454f5f1 100755 (executable)
@@ -643,7 +643,6 @@ public:
     CMacAddrCfg (){
         memset(u.m_data,0,sizeof(u.m_data));
         u.m_mac.dest[3]=1;
-        u.m_mac.src[3]=1;
     }
     union {
         mac_align_t m_mac;
index a16605b..e23ff13 100644 (file)
@@ -20,8 +20,7 @@ limitations under the License.
 #pragma pack(push, 1)
 class ArpHdr {
  public:
-    enum arp_hdr_enum_e {
-        ARP_HDR_HRD_ETHER = 1,
+    enum arp_hdr_op_e {
         ARP_HDR_OP_REQUEST = 1, /* request to resolve address */
         ARP_HDR_OP_REPLY = 2, /* response to previous request */
         ARP_HDR_OP_REVREQUEST = 3, /* request proto addr given hardware */
@@ -30,6 +29,46 @@ class ArpHdr {
         ARP_HDR_OP_INVREPLY = 6, /* response identifying peer */
     };
 
+    enum arp_hdr_hrd_e {
+        ARP_HDR_HRD_ETHER = 1,
+    };
+    
+    enum arp_hdr_proto_e {
+        ARP_HDR_PROTO_IPV4 = 0x800,
+    };
+    
+    void setOp(uint16_t op) {
+        m_arp_op = PKT_HTONS(op);
+    }
+    
+    uint16_t getOp() const {
+        return PKT_NTOHS(m_arp_op);
+    }
+    
+    uint16_t getHrdType() const {
+        return PKT_NTOHS(m_arp_hrd);
+    }
+    
+    uint16_t getProtocolType() const {
+        return PKT_NTOHS(m_arp_pro);
+    }
+    
+    uint32_t getSip() const {
+        return PKT_NTOHL(m_arp_sip); 
+    }
+    
+    void setSip(uint32_t sip) {
+        m_arp_sip = PKT_HTONL(sip);
+    }
+    
+    uint32_t getTip() const {
+        return PKT_NTOHL(m_arp_tip); 
+    }
+    
+    void setTip(uint32_t tip) {
+        m_arp_tip = PKT_HTONL(tip);
+    }
+    
  public:
        uint16_t m_arp_hrd;    /* format of hardware address */
        uint16_t m_arp_pro;    /* format of protocol address */
index c9dcdbe..002d6c2 100755 (executable)
@@ -62,8 +62,10 @@ public:
        inline EthernetHeader(uint8_t* packet);
 
     inline  uint8_t*  getPointer          (){return (uint8_t*)this;}
-    static inline  uint32_t  getSize             (){return (uint32_t)sizeof(EthernetHeader);}
-
+    inline  uint32_t  getSize () {
+        return ( (getNextProtocol() == Protocol::VLAN) ? 18 : 14);
+    }
+    
     // Get dest MAC pointer
     MacAddress *getDestMacP()             { return &myDestination; }
 
index 99d8932..4bc102e 100644 (file)
@@ -24,6 +24,11 @@ class ICMPHeader
 {
 
 public:
+    enum {
+        TYPE_ECHO_REPLY   = 0,
+        TYPE_ECHO_REQUEST = 8,
+    };
+    
     ICMPHeader()
        {
                setCode(0);
index 5dc4a9e..9bd3eae 100755 (executable)
@@ -137,6 +137,11 @@ public:
         return true;
     }
 
+    MacAddress& operator = (const uint8_t *rhs) {
+        memcpy(data, rhs, ETHER_ADDR_LEN);
+        return (*this);
+    }
+     
     uint8_t*   GetBuffer()
     {
         return data;
index f92d18c..a01d57a 100644 (file)
@@ -3618,9 +3618,7 @@ int  CGlobalTRex::ixgbe_start(void){
 
     if (! get_is_stateless()) {
         ixgbe_configure_mg();
-    } else {
-        rx_sl_configure();
-    }
+    } 
 
 
     /* core 0 - control
@@ -3759,6 +3757,8 @@ bool CGlobalTRex::Create(){
         cfg.m_publisher          = &m_zmq_publisher;
 
         m_trex_stateless = new TrexStateless(cfg);
+        
+        rx_sl_configure();
     }
 
     return (true);
index c12732e..ffc2f73 100644 (file)
@@ -55,7 +55,10 @@ class TrexException : public std::runtime_error
         T_FLOW_STAT_NO_FREE_HW_ID,
         T_FLOW_STAT_RX_CORE_START_FAIL,
         T_FLOW_STAT_BAD_HW_ID,
-        T_INVALID
+        
+        T_RX_PKT_PARSE_ERR,
+        
+        T_INVALID,
     };
 
  TrexException() : std::runtime_error(""), m_type(T_INVALID) {
index f2061bf..9898e8b 100644 (file)
@@ -84,11 +84,13 @@ void CRxCoreStateless::create(const CRxSlCfg &cfg) {
 
     /* create per port manager */
     for (int i = 0; i < m_max_ports; i++) {
+        const TRexPortAttr *port_attr = get_stateless_obj()->get_platform_api()->getPortAttrObj(i);
         m_rx_port_mngr[i].create(cfg.m_ports[i],
                                  m_rfc2544,
                                  &m_err_cntrs,
                                  &m_cpu_dp_u,
-                                 cfg.m_num_crc_fix_bytes);
+                                 cfg.m_num_crc_fix_bytes,
+                                 port_attr);
     }
 }
 
index afc6827..af31270 100644 (file)
@@ -22,6 +22,7 @@
 #include "trex_stateless_rx_port_mngr.h"
 #include "common/captureFile.h"
 #include "trex_stateless_rx_core.h"
+#include "common/Network/Packet/Arp.h"
 
 /**************************************
  * latency RX feature
@@ -400,6 +401,259 @@ RXPacketRecorder::to_json() const {
     return output;
 }
 
+
+/**************************************
+ * RX feature server (ARP, ICMP) and etc.
+ * 
+ *************************************/
+
+class RXPktParser {
+public:
+    RXPktParser(const rte_mbuf_t *m) {
+
+        m_mbuf = m;
+        
+        /* start point */
+        m_current   = rte_pktmbuf_mtod(m, uint8_t *);;
+        m_size_left = rte_pktmbuf_pkt_len(m);
+
+        m_ether    = NULL;
+        m_arp      = NULL;
+        m_ipv4     = NULL;
+        m_icmp     = NULL;
+        m_vlan_tag = 0;
+        
+        /* ethernet */
+        m_ether = (EthernetHeader *)parse_bytes(14);
+        
+        uint16_t next_proto;
+        if (m_ether->getNextProtocol() == EthernetHeader::Protocol::VLAN) {
+            parse_bytes(4);
+            m_vlan_tag = m_ether->getVlanTag();
+            next_proto = m_ether->getVlanProtocol();
+        } else {
+            next_proto = m_ether->getNextProtocol();
+        }
+        
+        /**
+         * support only for ARP or IPv4 based protocols
+         */
+        switch (next_proto) {
+        case EthernetHeader::Protocol::ARP:
+            parse_arp();
+            return;
+            
+        case EthernetHeader::Protocol::IP:
+            parse_ipv4();
+            return;
+            
+        default:
+            return;
+        }
+        
+    }
+    
+    const rte_mbuf_t *m_mbuf;
+    EthernetHeader   *m_ether;
+    ArpHdr           *m_arp;
+    IPHeader         *m_ipv4;
+    ICMPHeader       *m_icmp;
+    uint16_t          m_vlan_tag;
+    
+protected:
+    
+    const uint8_t *parse_bytes(uint32_t size) {
+        if (m_size_left < size) {
+            parse_err();
+        }
+        
+        const uint8_t *p = m_current;
+        m_current    += size;
+        m_size_left  -= size;
+        
+        return p;
+    }
+    
+    void parse_arp() {
+        m_arp = (ArpHdr *)parse_bytes(sizeof(ArpHdr));
+    }
+    
+    void parse_ipv4() {
+        m_ipv4 = (IPHeader *)parse_bytes(IPHeader::DefaultSize);
+        
+        /* advance over IP options if exists */
+        parse_bytes(m_ipv4->getOptionLen());
+        
+        switch (m_ipv4->getNextProtocol()) {
+        case IPHeader::Protocol::ICMP:
+            parse_icmp();
+            return;
+            
+        default:
+            return;
+        }
+    }
+    
+    void parse_icmp() {
+        m_icmp = (ICMPHeader *)parse_bytes(sizeof(ICMPHeader));
+    }
+    
+    void parse_err() {
+        throw TrexException(TrexException::T_RX_PKT_PARSE_ERR);
+    }
+    
+    const uint8_t *m_current;
+    uint16_t       m_size_left;
+};
+
+RXServer::RXServer() {
+    m_port_attr = NULL;
+    m_io        = NULL;
+    m_port_id   = 255;
+}
+
+void
+RXServer::create(const TRexPortAttr *port_attr, CPortLatencyHWBase *io) {
+    m_port_attr = port_attr;
+    m_io = io;
+    m_port_id = port_attr->get_port_id();
+}
+
+
+void
+RXServer::handle_pkt(const rte_mbuf_t *m) {
+
+    RXPktParser parser(m);
+    
+    if (parser.m_icmp) {
+        handle_icmp(parser);
+    } else if (parser.m_arp) {
+        handle_arp(parser);
+    } else {
+        return;
+    }
+
+}
+void
+RXServer::handle_icmp(RXPktParser &parser) {
+    
+    /* maybe not for us... */
+    if (parser.m_ipv4->getDestIp() != m_port_attr->get_src_ipv4()) {
+        return;
+    }
+    
+    /* we handle only echo request */
+    if (parser.m_icmp->getType() != ICMPHeader::TYPE_ECHO_REQUEST) {
+        return;
+    }
+    
+    /* duplicate the MBUF */
+    rte_mbuf_t *response = duplicate_mbuf(parser.m_mbuf);
+    if (!response) {
+        return;
+    }
+    
+    /* reparse the cloned packet */
+    RXPktParser response_parser(response);
+    
+    /* swap MAC */
+    MacAddress tmp = response_parser.m_ether->mySource;
+    response_parser.m_ether->mySource = response_parser.m_ether->myDestination;
+    response_parser.m_ether->myDestination = tmp;
+    
+    /* swap IP */
+    std::swap(response_parser.m_ipv4->mySource, response_parser.m_ipv4->myDestination);
+    
+    /* new packet - new TTL */
+    response_parser.m_ipv4->setTimeToLive(128);
+    response_parser.m_ipv4->updateCheckSum();
+    
+    /* update type and fix checksum */
+    response_parser.m_icmp->setType(ICMPHeader::TYPE_ECHO_REPLY);
+    response_parser.m_icmp->updateCheckSum(response_parser.m_ipv4->getTotalLength() - response_parser.m_ipv4->getHeaderLength());
+    
+    /* send */
+    m_io->tx(response);
+}
+
+void
+RXServer::handle_arp(RXPktParser &parser) {
+    
+    /* only ethernet format supported */
+    if (parser.m_arp->getHrdType() != ArpHdr::ARP_HDR_HRD_ETHER) {
+        return;
+    }
+    
+    /* IPV4 only */    
+    if (parser.m_arp->getProtocolType() != ArpHdr::ARP_HDR_PROTO_IPV4) {
+        return;
+    }
+
+    /* support only for ARP request */
+    if (parser.m_arp->getOp() != ArpHdr::ARP_HDR_OP_REQUEST) {
+        return;
+    }
+    
+    /* are we the target ? if not - go home */
+    if (parser.m_arp->getTip() != m_port_attr->get_src_ipv4()) {
+        return;
+    }
+    
+    /* duplicate the MBUF */
+    rte_mbuf_t *response = duplicate_mbuf(parser.m_mbuf);
+    if (!response) {
+        return;
+    }
+    /* reparse the cloned packet */
+    RXPktParser response_parser(response);
+    
+    /* reply */
+    response_parser.m_arp->setOp(ArpHdr::ARP_HDR_OP_REPLY);
+    
+    /* fix the MAC addresses */
+    response_parser.m_ether->mySource = m_port_attr->get_src_mac();
+    response_parser.m_ether->myDestination = parser.m_ether->mySource;
+    
+    /* fill up the fields */
+    
+    /* src */
+    response_parser.m_arp->m_arp_sha = m_port_attr->get_src_mac();
+    response_parser.m_arp->setSip(m_port_attr->get_src_ipv4());
+    
+    /* dst */
+    response_parser.m_arp->m_arp_tha = parser.m_arp->m_arp_sha;
+    response_parser.m_arp->m_arp_tip = parser.m_arp->m_arp_sip;
+    
+    /* send */
+    m_io->tx(response);
+    
+}
+
+rte_mbuf_t *
+RXServer::duplicate_mbuf(const rte_mbuf_t *m) {
+    /* RX packets should always be one segment */
+    assert(m->nb_segs == 1);
+    
+    /* allocate */
+    rte_mbuf_t *clone_mbuf = CGlobalInfo::pktmbuf_alloc_by_port(m_port_id, rte_pktmbuf_pkt_len(m));
+    if (!clone_mbuf) {
+        return NULL;
+    }
+    
+    /* append data */
+    uint8_t *dest = (uint8_t *)rte_pktmbuf_append(clone_mbuf, rte_pktmbuf_pkt_len(m));
+    if (!dest) {
+        return NULL;
+    }
+    
+    /* copy data */
+    const uint8_t *src = rte_pktmbuf_mtod(m, const uint8_t *);
+    memcpy(dest, src, rte_pktmbuf_pkt_len(m));
+    
+    return clone_mbuf;
+}
+
 /**************************************
  * Port manager 
  * 
@@ -417,13 +671,18 @@ RXPortManager::create(CPortLatencyHWBase *io,
                       CRFC2544Info *rfc2544,
                       CRxCoreErrCntrs *err_cntrs,
                       CCpuUtlDp *cpu_util,
-                      uint8_t crc_bytes_num) {
+                      uint8_t crc_bytes_num,
+                      const TRexPortAttr *port_attr) {
     m_io = io;
     m_cpu_dp_u = cpu_util;
     m_num_crc_fix_bytes = crc_bytes_num;
     
     /* init features */
     m_latency.create(rfc2544, err_cntrs);
+    m_server.create(port_attr, io);
+    
+    /* by default, server feature is always on */
+    set_feature(SERVER);
 }
     
 void RXPortManager::handle_pkt(const rte_mbuf_t *m) {
@@ -441,6 +700,10 @@ void RXPortManager::handle_pkt(const rte_mbuf_t *m) {
     if (is_feature_set(QUEUE)) {
         m_queue.handle_pkt(m);
     }
+    
+    if (is_feature_set(SERVER)) {
+        m_server.handle_pkt(m);
+    }
 }
 
 
@@ -513,3 +776,4 @@ RXPortManager::to_json() const {
  
     return output;
 }
+
index c049cb5..12b601e 100644 (file)
@@ -255,6 +255,28 @@ private:
 };
 
 
+/**************************************
+ * RX server (ping, ARP and etc.)
+ * 
+ *************************************/
+class RXPktParser;
+class RXServer {
+public:
+    
+    RXServer();
+    void create(const TRexPortAttr *port_attr, CPortLatencyHWBase *io);
+    void handle_pkt(const rte_mbuf_t *m);
+    
+private:
+    void handle_icmp(RXPktParser &parser);
+    void handle_arp(RXPktParser &parser);
+    rte_mbuf_t *duplicate_mbuf(const rte_mbuf_t *m);
+    
+    const TRexPortAttr  *m_port_attr;
+    CPortLatencyHWBase  *m_io;
+    uint8_t              m_port_id;
+};
+
 /************************ manager ***************************/
 
 /**
@@ -268,7 +290,8 @@ public:
         NO_FEATURES  = 0x0,
         LATENCY      = 0x1,
         RECORDER     = 0x2,
-        QUEUE        = 0x4
+        QUEUE        = 0x4,
+        SERVER       = 0x8
     };
 
     RXPortManager();
@@ -277,7 +300,8 @@ public:
                 CRFC2544Info *rfc2544,
                 CRxCoreErrCntrs *err_cntrs,
                 CCpuUtlDp *cpu_util,
-                uint8_t crc_bytes_num);
+                uint8_t crc_bytes_num,
+                const TRexPortAttr *port_attr);
 
     void clear_stats() {
         m_latency.reset_stats();
@@ -403,6 +427,8 @@ private:
     RXLatency                    m_latency;
     RXPacketRecorder             m_recorder;
     RXQueue                      m_queue;
+    RXServer                     m_server;
+    
     // compensate for the fact that hardware send us packets without Ethernet CRC, and we report with it
     uint8_t m_num_crc_fix_bytes;
     
index 3cb9bef..ae39743 100755 (executable)
@@ -134,7 +134,7 @@ public:
     virtual void get_description(std::string &description) { description = intf_info_st.description; }
     virtual void get_supported_speeds(supp_speeds_t &supp_speeds) = 0;
 
-    uint32_t get_src_ipv4() {return m_src_ipv4;}
+    uint32_t get_src_ipv4() const {return m_src_ipv4;}
     DestAttr & get_dest() {return m_dest;}
     
     const uint8_t *get_src_mac() const;
@@ -161,6 +161,9 @@ public:
     /* dump object status to JSON */
     void to_json(Json::Value &output);
     
+    uint8_t get_port_id() const {
+        return m_port_id;
+    }
     
 protected: