pre test: Send grat ARP, and resolve gateway addresses
authorIdo Barnea <[email protected]>
Thu, 22 Sep 2016 12:15:29 +0000 (15:15 +0300)
committerIdo Barnea <[email protected]>
Wed, 5 Oct 2016 07:45:28 +0000 (10:45 +0300)
linux_dpdk/ws_main.py
src/bp_sim.h
src/main_dpdk.cpp
src/main_dpdk.h
src/platform_cfg.cpp
src/platform_cfg.h
src/pre_test.cpp [new file with mode: 0644]
src/pre_test.h [new file with mode: 0644]

index dc169f1..47c479c 100755 (executable)
@@ -117,6 +117,7 @@ main_src = SrcGroup(dir='src',
              'bp_sim.cpp',
              'latency.cpp',
              'platform_cfg.cpp',
+             'pre_test.cpp',
              'tuple_gen.cpp',
              'rx_check.cpp',
              'rx_check_header.cpp',
index 2790aa9..37c3e1e 100755 (executable)
@@ -757,7 +757,8 @@ public:
     uint16_t           m_vlan_port[2]; /* vlan value */
     uint16_t           m_src_ipv6[6];  /* Most signficant 96-bits */
     uint16_t           m_dst_ipv6[6];  /* Most signficant 96-bits */
-
+    uint32_t        m_def_gw[TREX_MAX_PORTS];
+    uint32_t        m_ip[TREX_MAX_PORTS];
     uint32_t        m_latency_rate; /* pkt/sec for each thread/port zero disable */
     uint32_t        m_latency_mask;
     uint32_t        m_latency_prev;
index e45e2ab..cc0fdb5 100644 (file)
@@ -73,6 +73,7 @@ extern "C" {
 #include "utl_term_io.h"
 #include "msg_manager.h"
 #include "platform_cfg.h"
+#include "pre_test.h"
 #include "latency.h"
 #include "debug.h"
 #include "test_pkt_gen.h"
@@ -1392,7 +1393,7 @@ void CPhyEthIF::configure_rx_duplicate_rules(){
 }
 
 
-void CPhyEthIF::configure_rx_drop_queue(){
+void CPhyEthIF::stop_rx_drop_queue() {
     // In debug mode, we want to see all packets. Don't want to disable any queue.
     if ( get_vm_one_queue_enable() || (CGlobalInfo::m_options.m_debug_pkt_proto != 0)) {
         return;
@@ -2833,6 +2834,7 @@ public:
     void ixgbe_configure_mg();
     void rx_sl_configure();
     bool is_all_links_are_up(bool dump=false);
+    void pre_test();
     int  reset_counters();
 
     /**
@@ -2973,6 +2975,53 @@ public:
 
 };
 
+// Before starting, send gratitues ARP on our addresses, and try to resolve dst MAC addresses.
+void CGlobalTRex::pre_test() {
+    CPretest pretest(m_max_ports);
+    bool resolve_needed = false;
+    uint8_t empty_mac[ETHER_ADDR_LEN] = {0,0,0,0,0,0};
+
+    for (int port_id = 0; port_id < m_max_ports; port_id++) {
+        CPhyEthIF *pif = &m_ports[port_id];
+        // Configure port to send all packets to software
+        CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, true);
+        if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) {
+            resolve_needed = true;
+        } else {
+            resolve_needed = false;
+        }
+        if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src, empty_mac, ETHER_ADDR_LEN)) {
+            rte_eth_macaddr_get(port_id,
+                                (struct ether_addr *)&CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src);
+        }
+        pretest.set_port_params(port_id, CGlobalInfo::m_options.m_ip[port_id]
+                                , CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src
+                                , CGlobalInfo::m_options.m_def_gw[port_id], resolve_needed);
+    }
+
+    pretest.send_grat_arp_all();
+    pretest.resolve_all();
+    if ( CGlobalInfo::m_options.preview.getVMode() > 0) {
+        pretest.dump(stdout);
+    }
+    uint8_t mac[ETHER_ADDR_LEN];
+    for (int port_id = 0; port_id < m_max_ports; port_id++) {
+        if (! memcmp(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) {
+            uint32_t ip = CGlobalInfo::m_options.m_def_gw[port_id];
+            if (! pretest.get_mac(port_id, ip, mac)) {
+                fprintf(stderr, "Failed resolving dest MAC for default gateway:%d.%d.%d.%d on port %d\n"
+                        , (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF, port_id);
+                exit(1);
+            }
+            memcpy(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, mac, ETHER_ADDR_LEN);
+        }
+
+        CPhyEthIF *pif = &m_ports[port_id];
+        // Configure port back to normal mode. Only relevant packets handled by software.
+        CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, false);
+    }
+}
+
 int  CGlobalTRex::reset_counters(){
     int i;
     for (i=0; i<m_max_ports; i++) {
@@ -3257,7 +3306,6 @@ int  CGlobalTRex::ixgbe_start(void){
         _if->stats_clear();
 
         _if->start();
-        _if->configure_rx_drop_queue();
         _if->configure_rx_duplicate_rules();
 
         if ( ! get_vm_one_queue_enable()  && ! CGlobalInfo::m_options.preview.get_is_disable_flow_control_setting()
@@ -4777,6 +4825,8 @@ int update_global_info_from_platform_file(){
         for (i=0; i<port_size; i++){
             cg->m_mac_info[i].copy_src(( char *)CGlobalInfo::m_options.m_mac_addr[i].u.m_mac.src)   ;
             cg->m_mac_info[i].copy_dest(( char *)CGlobalInfo::m_options.m_mac_addr[i].u.m_mac.dest)  ;
+            CGlobalInfo::m_options.m_def_gw[i] = cg->m_mac_info[i].get_def_gw();
+            CGlobalInfo::m_options.m_ip[i] = cg->m_mac_info[i].get_ip();
         }
     }
 
@@ -5143,6 +5193,14 @@ int main_test(int argc , char * argv[]){
         }
     }
 
+    g_trex.pre_test();
+    // after doing all needed ARP resolution, we need to flush queues, and stop our drop queue
+    g_trex.ixgbe_rx_queue_flush();
+    for (int i = 0; i < g_trex.m_max_ports; i++) {
+        CPhyEthIF *_if = &g_trex.m_ports[i];
+        _if->stop_rx_drop_queue();
+    }
+
     if ( CGlobalInfo::m_options.preview.getOnlyLatency() ){
         rte_eal_mp_remote_launch(latency_one_lcore, NULL, CALL_MASTER);
         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
index 3104ff5..bde1065 100644 (file)
@@ -81,7 +81,7 @@ class CPhyEthIF  {
                         uint16_t nb_tx_desc,
                         unsigned int socket_id,
                         const struct rte_eth_txconf *tx_conf);
-    void configure_rx_drop_queue();
+    void stop_rx_drop_queue();
     void configure_rx_duplicate_rules();
     void start();
     void stop();
index 64bbb71..a090a0c 100755 (executable)
@@ -23,6 +23,7 @@ limitations under the License.
 #include <iostream>
 #include <stdlib.h>
 #include "common/basic_utils.h"
+#include "utl_yaml.h"
 #include "platform_cfg.h"
 #include "utl_yaml.h"
 
@@ -165,6 +166,13 @@ void CMacYamlInfo::copy_src(char *p){
         }
 }
 
+uint32_t CMacYamlInfo::get_def_gw() {
+    return m_def_gw;
+}
+
+uint32_t CMacYamlInfo::get_ip() {
+    return m_ip;
+}
 
 void CMacYamlInfo::Dump(FILE *fd){
     if (m_dest_base.size() != 6) {
@@ -187,32 +195,54 @@ void operator >> (const YAML::Node& node, CMacYamlInfo & mac_info) {
     bool res;
     std::string mac_str;
 
-    const YAML::Node& dmac = node["dest_mac"];
-    if (dmac.Type() == YAML::NodeType::Sequence) { // [1,2,3,4,5,6]
-        ASSERT_MSG(dmac.size() == 6, "Array of dest MAC should have 6 elements.");
-        for(unsigned i=0;i<dmac.size();i++) {
-            dmac[i]  >> fi;
-            mac_info.m_dest_base.push_back(fi);
+    if (node.FindValue("dest_mac")) {
+        const YAML::Node& dmac = node["dest_mac"];
+        if (dmac.Type() == YAML::NodeType::Sequence) { // [1,2,3,4,5,6]
+            ASSERT_MSG(dmac.size() == 6, "Array of dest MAC should have 6 elements.");
+            for(unsigned i=0;i<dmac.size();i++) {
+                dmac[i]  >> fi;
+                mac_info.m_dest_base.push_back(fi);
+            }
+        }
+        else if (dmac.Type() == YAML::NodeType::Scalar) { // "12:34:56:78:9a:bc"
+            dmac >> mac_str;
+            res = mac2vect(mac_str, mac_info.m_dest_base);
+            ASSERT_MSG(res && mac_info.m_dest_base.size() == 6
+                       , "String of dest MAC should be in format '12:34:56:78:9a:bc'.");
+        }
+    } else {
+        for(unsigned i = 0; i < 6; i++) {
+            mac_info.m_dest_base.push_back(0);
         }
-    }
-    else if (dmac.Type() == YAML::NodeType::Scalar) { // "12:34:56:78:9a:bc"
-        dmac >> mac_str;
-        res = mac2vect(mac_str, mac_info.m_dest_base);
-        ASSERT_MSG(res && mac_info.m_dest_base.size() == 6, "String of dest MAC should be in format '12:34:56:78:9a:bc'.");
     }
 
-    const YAML::Node& smac = node["src_mac"];
-    if (smac.Type() == YAML::NodeType::Sequence) {
-        ASSERT_MSG(smac.size() == 6, "Array of src MAC should have 6 elements.");
-        for(unsigned i=0;i<smac.size();i++) {
-            smac[i]  >> fi;
-            mac_info.m_src_base.push_back(fi);
+    if (node.FindValue("src_mac")) {
+        const YAML::Node& smac = node["src_mac"];
+        if (smac.Type() == YAML::NodeType::Sequence) {
+            ASSERT_MSG(smac.size() == 6, "Array of src MAC should have 6 elements.");
+            for(unsigned i=0;i<smac.size();i++) {
+                smac[i]  >> fi;
+                mac_info.m_src_base.push_back(fi);
+            }
+        }
+        else if (smac.Type() == YAML::NodeType::Scalar) {
+            smac >> mac_str;
+            res = mac2vect(mac_str, mac_info.m_src_base);
+            ASSERT_MSG(res && mac_info.m_src_base.size() == 6
+                       , "String of src MAC should be in format '12:34:56:78:9a:bc'.");
+        }
+    } else {
+        for(unsigned i = 0; i < 6; i++) {
+            mac_info.m_src_base.push_back(0);
         }
     }
-    else if (smac.Type() == YAML::NodeType::Scalar) {
-        smac >> mac_str;
-        res = mac2vect(mac_str, mac_info.m_src_base);
-        ASSERT_MSG(res && mac_info.m_src_base.size() == 6, "String of src MAC should be in format '12:34:56:78:9a:bc'.");
+
+    if (! utl_yaml_read_ip_addr(node, "default_gw", mac_info.m_def_gw)) {
+        mac_info.m_def_gw = 0;
+    }
+
+    if (! utl_yaml_read_ip_addr(node, "ip", mac_info.m_ip)) {
+        mac_info.m_ip = 0;
     }
 }
 
index 1b56aad..cd01f47 100755 (executable)
@@ -99,10 +99,14 @@ const std::string * get_mbuf_names(void);
 struct CMacYamlInfo {
     std::vector     <uint8_t> m_dest_base;
     std::vector     <uint8_t> m_src_base;
+    uint32_t m_def_gw;
+    uint32_t m_ip;
     void Dump(FILE *fd);
 
     void copy_dest(char *p);
     void copy_src(char *p);
+    uint32_t get_def_gw();
+    uint32_t get_ip();
 
     void dump_mac_vector( std::vector<uint8_t> & v,FILE *fd){
         int i;
diff --git a/src/pre_test.cpp b/src/pre_test.cpp
new file mode 100644 (file)
index 0000000..9df2ed6
--- /dev/null
@@ -0,0 +1,339 @@
+/* 
+?????
+- read also from q 0
+- read periodically - not wait 1 sec
+flush q 0,1 at end
+close q 0 at end (not close it at start)
+test in trex-dan router for long time. remove static arp from router.
+test 10g, vm
+add also per profile
+
+
+remove stuff in ???
+make 10G work
+documentation
+
+ */
+
+/*
+  Ido Barnea
+  Cisco Systems, Inc.
+*/
+
+/*
+  Copyright (c) 2016-2016 Cisco Systems, Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+*/
+
+#include <rte_arp.h>
+#include <rte_ethdev.h>
+#include <arpa/inet.h>
+#include <common/Network/Packet/EthernetHeader.h>
+#include "common/basic_utils.h"
+#include "bp_sim.h"
+#include "main_dpdk.h"
+#include "pre_test.h"
+
+
+void CPretestPortInfo::set_params(uint32_t ip, const uint8_t *src_mac, uint32_t def_gw
+                                  , bool resolve_needed) {
+    m_ip = ip;
+    m_def_gw = def_gw;
+    memcpy(&m_src_mac, src_mac, sizeof(m_src_mac));
+    if (resolve_needed) {
+        m_state = CPretestPortInfo::RESOLVE_NEEDED;
+    } else {
+        m_state = CPretestPortInfo::RESOLVE_NOT_NEEDED;
+    }
+}
+
+void CPretestPortInfo::set_dst_mac(const uint8_t *dst_mac) {
+    memcpy(&m_dst_mac, dst_mac, sizeof(m_dst_mac));
+    m_state = CPretestPortInfo::RESOLVE_DONE;
+}
+
+void CPretestPortInfo::dump(FILE *fd) {
+    if (m_state == INIT_NEEDED) {
+        return;
+    }
+
+    uint32_t ip = htonl(m_ip);
+    
+    fprintf(fd, "  ip:%d.%d.%d.%d", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
+    ip = htonl(m_def_gw);
+    fprintf(fd, "  default gw:%d.%d.%d.%d\n", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
+
+    printf("  src MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", m_src_mac[0], m_src_mac[1], m_src_mac[2], m_src_mac[3]
+           , m_src_mac[4], m_src_mac[5]);
+    printf(  "  dst MAC: ");
+    if (m_state == RESOLVE_DONE) {
+        printf("%02x:%02x:%02x:%02x:%02x:%02x\n", m_dst_mac[0], m_dst_mac[1], m_dst_mac[2], m_dst_mac[3]
+               , m_dst_mac[4], m_dst_mac[5]);
+    } else {
+        printf("Not resolved\n");
+    }
+}
+
+// Create ARP request for our default gateway.
+// If is_grat is true - create gratuitous ARP.
+// pkt_size will contain the size of buffer returned.
+// Return pointer to buffer containing the packet.
+uint8_t *CPretestPortInfo::create_arp_req(uint16_t &pkt_size, uint8_t port, bool is_grat) {
+    pkt_size = 14 + sizeof (struct arp_hdr);
+    uint16_t l2_proto = htons(EthernetHeader::Protocol::ARP);
+    
+    uint8_t *pkt = (uint8_t *)malloc(pkt_size);
+    assert(pkt != NULL);   
+    uint8_t *p = pkt;
+    
+    // dst MAC
+    memset(p, 0xff, ETHER_ADDR_LEN);
+    p += ETHER_ADDR_LEN;
+    // src MAC
+    memcpy(p, m_src_mac, ETHER_ADDR_LEN);
+    p += ETHER_ADDR_LEN;
+    // l3 type
+    memcpy(p, &l2_proto, sizeof(l2_proto));
+    p += 2;
+
+    struct arp_hdr *arp = (struct arp_hdr *)p;
+    arp->arp_hrd = htons(ARP_HRD_ETHER); // Format of hardware address
+    arp->arp_pro = htons(EthernetHeader::Protocol::IP); // Format of protocol address
+    arp->arp_hln = ETHER_ADDR_LEN; // Length of hardware address
+    arp->arp_pln = 4; // Length of protocol address
+    arp->arp_op = htons(ARP_OP_REQUEST); // ARP opcode (command)
+
+    memcpy(&arp->arp_data.arp_sha, m_src_mac, ETHER_ADDR_LEN); // Sender MAC address
+    arp->arp_data.arp_sip = htonl(m_ip); // Sender IP address
+    uint8_t magic[5] = {0x1, 0x3, 0x5, 0x7, 0x9};
+    memcpy(&arp->arp_data.arp_tha, magic, 5); // Target MAC address
+    arp->arp_data.arp_tha.addr_bytes[5] = port;
+    if (is_grat)
+        arp->arp_data.arp_tip = htonl(m_ip); // gratuitous ARP is request for your own IP.
+    else
+        arp->arp_data.arp_tip = htonl(m_def_gw); // Target IP address
+    
+    return pkt;
+}
+
+/*
+  put in mac relevant dest MAC for port/ip pair.
+  return false if no relevant info exists, true otherwise.
+ */
+bool CPretest::get_mac(uint16_t port, uint32_t ip, uint8_t *mac) {
+    if (port >= TREX_MAX_PORTS) {
+        return false;
+    }
+
+    if (m_port_info[port].m_state != CPretestPortInfo::RESOLVE_DONE) {
+        return false;
+    }
+
+    memcpy(mac, &m_port_info[port].m_dst_mac, sizeof(m_port_info[port].m_dst_mac));
+        
+    return true;    
+}
+
+void CPretest::set_port_params(uint16_t port, uint32_t ip, const uint8_t *src_mac, uint32_t def_gw,
+                               bool resolve_needed) {
+    if (port >= m_max_ports)
+        return;
+
+    m_port_info[port].set_params(ip, src_mac, def_gw, resolve_needed);
+}
+
+
+int CPretest::handle_rx(int port_id, int queue_id) {
+    rte_mbuf_t * rx_pkts[32];
+    uint16_t cnt;
+    int i;
+
+    cnt = rte_eth_rx_burst(port_id, queue_id, rx_pkts, sizeof(rx_pkts)/sizeof(rx_pkts[0]));
+    for (i = 0; i < cnt; i++) {
+        rte_mbuf_t * m = rx_pkts[i];
+        int pkt_size = rte_pktmbuf_pkt_len(m);
+        uint8_t *p = rte_pktmbuf_mtod(m, uint8_t *);
+        struct arp_hdr *arp;
+        CPretestPortInfo *port = &m_port_info[port_id];
+        if (is_arp(p, pkt_size, arp)) {
+                if (arp->arp_op == htons(ARP_OP_REQUEST)) {
+                    // is this request for our IP?
+                    if (ntohl(arp->arp_data.arp_tip) == port->m_ip) {
+                        // If our request, do a shortcut, and write info directly to asking port
+                        uint8_t magic[5] = {0x1, 0x3, 0x5, 0x7, 0x9};
+                        if (! memcmp((uint8_t *)&arp->arp_data.arp_tha, magic, 5)) {
+                            uint8_t sent_port_id = arp->arp_data.arp_tha.addr_bytes[5];
+                            if ((sent_port_id < m_max_ports) &&
+                                (m_port_info[sent_port_id].m_def_gw == port->m_ip)) {
+                                memcpy(m_port_info[sent_port_id].m_dst_mac, port->m_src_mac, ETHER_ADDR_LEN);
+                                m_port_info[sent_port_id].m_state = CPretestPortInfo::RESOLVE_DONE;
+                            }
+                        }
+                    } else {
+                        // ARP request not to our IP. At the moment, we ignore this.
+                    }
+                } else {
+                    if (arp->arp_op == htons(ARP_OP_REPLY)) {
+                        // If this is response to our request, update our tables
+                        if (port->m_def_gw == ntohl(arp->arp_data.arp_sip)) {
+                            port->set_dst_mac((uint8_t *)&arp->arp_data.arp_sha);
+                        }
+                    }
+                }
+        }       
+
+        printf("port %d, queue:%d\n", port_id, queue_id);
+        utl_DumpBuffer(stdout, p, pkt_size, 0); //??? remove
+        rte_pktmbuf_free(m);
+    }
+    return 0;
+}
+
+/*
+  Try to resolve def_gw address on all ports marked as needed.
+  Return false if failed to resolve on one of the ports
+ */
+bool CPretest::resolve_all() {
+    uint16_t port;
+
+    // send ARP request on all ports
+    for (port = 0; port < m_max_ports; port++) {
+        if (m_port_info[port].m_state == CPretestPortInfo::RESOLVE_NEEDED) {
+            send_arp_req(port, false);
+        }
+    }
+
+    int max_tries = 1000;
+    int i;
+    for (i = 0; i < max_tries; i++) {
+        bool all_resolved = true;
+        for (port = 0; port < m_max_ports; port++) {
+            if (m_port_info[port].m_state == CPretestPortInfo::RESOLVE_NEEDED) {
+                // We need to stop reading packets only if all ports are resolved.
+                // If we are on loopback, We might get requests on port even after it is in RESOLVE_DONE state
+                all_resolved = false;
+            }
+            handle_rx(port, MAIN_DPDK_DATA_Q);
+            if (! CGlobalInfo::m_options.preview.get_vm_one_queue_enable())
+                handle_rx(port, MAIN_DPDK_RX_Q);
+        }
+        if (all_resolved) {
+            break;
+        } else {
+            delay(1);
+        }
+    }
+
+    if (i == max_tries) {
+        return false;
+    } else {
+        return true;
+    }
+}
+
+void CPretest::dump(FILE *fd) {
+    for (int port = 0; port < m_max_ports; port++) {
+        if (m_port_info[port].m_state != CPretestPortInfo::INIT_NEEDED) {
+            fprintf(fd, "port %d:\n", port);
+            m_port_info[port].dump(fd);
+        }
+    }
+}
+
+void CPretest::send_arp_req(uint16_t port, bool is_grat) {
+    uint16_t pkt_size;
+    uint8_t *pkt;
+    rte_mbuf_t *m[1];
+    char *p;
+    int num_sent;
+
+    pkt = m_port_info[port].create_arp_req(pkt_size, port, is_grat);
+    m[0] = CGlobalInfo::pktmbuf_alloc(0, pkt_size);
+    if ( unlikely(m[0] == 0) )  {
+        fprintf(stderr, "ERROR: Could not allocate mbuf for sending ARP to port:%d\n", port);
+        exit(1);
+    }
+    p = rte_pktmbuf_append(m[0], pkt_size);
+    memcpy(p, pkt, pkt_size);
+    free(pkt);
+
+    num_sent = rte_eth_tx_burst(port, 0, m, 1);
+    if (num_sent < 1) {
+        fprintf(stderr, "Failed sending ARP to port:%d\n", port);
+        exit(1);
+    }
+}
+
+/*
+  Send gratuitous ARP on all ports
+ */
+void CPretest::send_grat_arp_all() {
+    for (uint16_t port = 0; port < m_max_ports; port++) {
+        if (m_port_info[port].m_state == CPretestPortInfo::RESOLVE_NEEDED) {
+            send_arp_req(port, true);
+        }
+    }
+}
+
+bool CPretest::is_arp(uint8_t *p, uint16_t pkt_size, struct arp_hdr *&arp) {
+    EthernetHeader *m_ether = (EthernetHeader *)p;
+
+    if ((pkt_size < 60) ||
+        ((m_ether->getNextProtocol() != EthernetHeader::Protocol::ARP)
+         && (m_ether->getNextProtocol() != EthernetHeader::Protocol::VLAN)))
+        return false;
+
+    if (m_ether->getNextProtocol() == EthernetHeader::Protocol::ARP) {
+        arp = (struct arp_hdr *)(p + 14);
+    } else {
+        if (m_ether->getVlanProtocol() != EthernetHeader::Protocol::ARP) {
+            return false;
+        } else {
+            arp = (struct arp_hdr *)(p + 18);
+        }
+    }
+
+    return true;
+}
+
+// Should be run on setup with two interfaces connected by loopback.
+// Before running, should put ports on receive all mode. 
+void CPretest::test() {
+    uint8_t found_mac[ETHER_ADDR_LEN];
+    uint8_t mac0[ETHER_ADDR_LEN] = {0x90, 0xe2, 0xba, 0xae, 0x87, 0xd0};
+    uint8_t mac1[ETHER_ADDR_LEN] = {0x90, 0xe2, 0xba, 0xae, 0x87, 0xd1};
+    uint32_t ip0 = 0x0f000003;
+    uint32_t ip1 = 0x0f000001;
+    
+    set_port_params(0, ip0, mac0, ip1, true);
+    set_port_params(1, ip1, mac1, ip0, true);
+    dump(stdout);
+    resolve_all();
+    dump(stdout);
+    get_mac(0, ip1, found_mac);
+    if (memcmp(found_mac, mac1, ETHER_ADDR_LEN)) {
+        fprintf(stderr, "Test failed: Could not resolve def gw on port 0\n");
+        exit(1);
+    }
+
+    get_mac(1, ip0, found_mac);
+    if (memcmp(found_mac, mac0, ETHER_ADDR_LEN)) {
+        fprintf(stderr, "Test failed: Could not resolve def gw on port 1\n");
+        exit(1);
+    }
+
+    printf("Test passed\n");
+    exit(0);
+}
diff --git a/src/pre_test.h b/src/pre_test.h
new file mode 100644 (file)
index 0000000..0908532
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+  Ido Barnea
+  Cisco Systems, Inc.
+*/
+
+/*
+  Copyright (c) 2016-2016 Cisco Systems, Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+*/
+
+#ifndef __PRE_TEST_H__
+#define __PRE_TEST_H__
+
+#include <iostream>
+#include "trex_defs.h"
+
+class CPretestPortInfo {
+    friend class CPretest;
+    
+ private:
+    enum CPretestPortInfoStates {
+        INIT_NEEDED,
+        RESOLVE_NEEDED,
+        RESOLVE_DONE,
+        RESOLVE_NOT_NEEDED,
+    };
+
+    CPretestPortInfo() {
+        m_state = INIT_NEEDED;
+    }
+    void dump(FILE *fd);
+    uint8_t *create_arp_req(uint16_t &pkt_size, uint8_t port, bool is_grat);
+    void set_params(uint32_t ip, const uint8_t *src_mac, uint32_t def_gw, bool resolve_needed);
+    void set_dst_mac(const uint8_t *dst_mac);
+    
+ private:
+    uint32_t m_ip;
+    uint32_t m_def_gw;
+    uint8_t m_src_mac[6];
+    uint8_t m_dst_mac[6];
+    enum CPretestPortInfoStates m_state;
+};
+
+
+class CPretest {
+ public:
+    CPretest(uint16_t max_ports) {
+        m_max_ports = max_ports;
+    }
+    bool get_mac(uint16_t port, uint32_t ip, uint8_t *mac);
+    void set_port_params(uint16_t port, uint32_t ip, const uint8_t *src_mac, uint32_t def_gw,
+                        bool resolve_needed);
+    bool resolve_all();
+    void send_arp_req(uint16_t port, bool is_grat);
+    void send_grat_arp_all();
+    bool is_arp(uint8_t *p, uint16_t pkt_size, struct arp_hdr *&arp);
+    void dump(FILE *fd);
+    void test();
+    
+ private:
+    int handle_rx(int port, int queue_id);
+
+ private:
+    CPretestPortInfo m_port_info[TREX_MAX_PORTS];
+    uint16_t m_max_ports;
+};
+
+#endif