From 7cecfd8ac5537e2128af95660c19f8bb4955a8a0 Mon Sep 17 00:00:00 2001
From: Ido Barnea <ibarnea@cisco.com>
Date: Mon, 8 Aug 2016 10:35:40 +0300
Subject: IPv6 x710 flow stats work. Still not supported: Flow stats for
 UDP/TCP IPv6 packets with extension header.

---
 src/flow_stat.cpp                    |  64 +++++++++---------
 src/flow_stat.h                      |  21 +++---
 src/flow_stat_parser.cpp             |  14 ++++
 src/flow_stat_parser.h               |   1 +
 src/internal_api/trex_platform_api.h |  52 ++++++++-------
 src/main_dpdk.cpp                    | 122 +++++++++++++++++++++--------------
 src/main_dpdk.h                      |   6 +-
 src/stateless/cp/trex_exception.h    |   1 +
 8 files changed, 166 insertions(+), 115 deletions(-)

(limited to 'src')

diff --git a/src/flow_stat.cpp b/src/flow_stat.cpp
index a9451e17..58ff65a4 100644
--- a/src/flow_stat.cpp
+++ b/src/flow_stat.cpp
@@ -87,13 +87,15 @@ inline std::string methodName(const std::string& prettyFunction)
 #endif
 
 /************** class CFlowStatUserIdInfo ***************/
-CFlowStatUserIdInfo::CFlowStatUserIdInfo(uint8_t proto) {
+CFlowStatUserIdInfo::CFlowStatUserIdInfo(uint16_t l3_proto, uint8_t l4_proto, uint8_t ipv6_next_h) {
     memset(m_rx_cntr, 0, sizeof(m_rx_cntr));
     memset(m_rx_cntr_base, 0, sizeof(m_rx_cntr));
     memset(m_tx_cntr, 0, sizeof(m_tx_cntr));
     memset(m_tx_cntr_base, 0, sizeof(m_tx_cntr));
     m_hw_id = UINT16_MAX;
-    m_proto = proto;
+    m_l3_proto = l3_proto;
+    m_l4_proto = l4_proto;
+    m_ipv6_next_h = ipv6_next_h;
     m_ref_count = 1;
     m_trans_ref_count = 0;
     m_was_sent = false;
@@ -105,7 +107,7 @@ CFlowStatUserIdInfo::CFlowStatUserIdInfo(uint8_t proto) {
 }
 
 std::ostream& operator<<(std::ostream& os, const CFlowStatUserIdInfo& cf) {
-    os << "hw_id:" << cf.m_hw_id << " proto:" << (uint16_t) cf.m_proto << " ref("
+    os << "hw_id:" << cf.m_hw_id << " l3 proto:" << (uint16_t) cf.m_l3_proto << " ref("
        << (uint16_t) cf.m_ref_count << "," << (uint16_t) cf.m_trans_ref_count << ")";
     os << " rx count (";
     os << cf.m_rx_cntr[0];
@@ -141,7 +143,7 @@ void CFlowStatUserIdInfo::add_stream(uint8_t proto) {
     std::cout << __METHOD_NAME__ << " proto:" << (uint16_t)proto << std::endl;
 #endif
 
-    if (proto != m_proto)
+    if (proto != m_l4_proto)
         throw TrexFStatEx("Can't use same pg_id for streams with different l4 protocol",
                                     TrexException::T_FLOW_STAT_PG_ID_DIFF_L4);
 
@@ -221,7 +223,7 @@ CFlowStatUserIdMap::find_user_id(uint32_t user_id) {
 }
 
 CFlowStatUserIdInfo *
-CFlowStatUserIdMap::add_user_id(uint32_t user_id, uint8_t proto) {
+CFlowStatUserIdMap::add_user_id(uint32_t user_id, uint16_t l3_proto, uint8_t l4_proto, uint8_t ipv6_next_h) {
 #ifdef __DEBUG_FUNC_ENTRY__
     std::cout << __METHOD_NAME__ << " user id:" << user_id << " proto:" << (uint16_t)proto
               << std::endl;
@@ -229,10 +231,10 @@ CFlowStatUserIdMap::add_user_id(uint32_t user_id, uint8_t proto) {
 
     CFlowStatUserIdInfo *new_id;
 
-    if (proto == PAYLOAD_RULE_PROTO) {
-        new_id = new CFlowStatUserIdInfoPayload(proto);
+    if (l4_proto == PAYLOAD_RULE_PROTO) {
+        new_id = new CFlowStatUserIdInfoPayload(l3_proto, l4_proto, ipv6_next_h);
     } else {
-        new_id = new CFlowStatUserIdInfo(proto);
+        new_id = new CFlowStatUserIdInfo(l3_proto, l4_proto, ipv6_next_h);
     }
     if (new_id != NULL) {
         std::pair<flow_stat_user_id_map_it_t, bool> ret;
@@ -249,9 +251,10 @@ CFlowStatUserIdMap::add_user_id(uint32_t user_id, uint8_t proto) {
     }
 }
 
-void CFlowStatUserIdMap::add_stream(uint32_t user_id, uint8_t proto) {
+void CFlowStatUserIdMap::add_stream(uint32_t user_id, uint16_t l3_proto, uint8_t l4_proto, uint8_t ipv6_next_h) {
 #ifdef __DEBUG_FUNC_ENTRY__
-    std::cout << __METHOD_NAME__ << " user id:" << user_id << " proto:" << (uint16_t)proto
+    std::cout << __METHOD_NAME__ << " user id:" << user_id << " l3 proto:" << (uint16_t)l3_proto
+              << " l4 proto:" << (uint16_t)l4_proto << " IPv6 next header:" << (uint16_t)ipv6_next_h
               << std::endl;
 #endif
 
@@ -259,9 +262,10 @@ void CFlowStatUserIdMap::add_stream(uint32_t user_id, uint8_t proto) {
 
     c_user_id = find_user_id(user_id);
     if (! c_user_id) {
-        c_user_id = add_user_id(user_id, proto); // throws exception on error
+        // throws exception on error
+        c_user_id = add_user_id(user_id, l3_proto, l4_proto, ipv6_next_h);
     } else {
-        c_user_id->add_stream(proto);
+        c_user_id->add_stream(l4_proto);
     }
 }
 
@@ -360,17 +364,6 @@ bool CFlowStatUserIdMap::is_started(uint32_t user_id) {
     return c_user_id->is_started();
 }
 
-uint8_t CFlowStatUserIdMap::l4_proto(uint32_t user_id) {
-    CFlowStatUserIdInfo *c_user_id;
-
-    c_user_id = find_user_id(user_id);
-    if (! c_user_id) {
-        return 0;
-    }
-
-    return c_user_id->get_proto();
-}
-
 uint16_t CFlowStatUserIdMap::unmap(uint32_t user_id) {
 #ifdef __DEBUG_FUNC_ENTRY__
     std::cout << __METHOD_NAME__ << " user id:" << user_id << std::endl;
@@ -557,7 +550,7 @@ int CFlowStatRuleMgr::add_stream(TrexStream * stream) {
     return add_stream_internal(stream, true);
 }
 
-/* 
+/*
  * Helper function for adding/verifying streams
  * stream - stream to act on
  * do_action - if false, just verify. Do not change any state, or add to database.
@@ -589,14 +582,20 @@ int CFlowStatRuleMgr::add_stream_internal(TrexStream * stream, bool do_action) {
 
     switch(rule_type) {
     case TrexPlatformApi::IF_STAT_IPV4_ID:
+        uint16_t l3_proto;
+        if (m_parser->get_l3_proto(l3_proto) < 0) {
+            throw TrexFStatEx("Failed determining l3 proto for packet", TrexException::T_FLOW_STAT_FAILED_FIND_L3);
+        }
         uint8_t l4_proto;
         if (m_parser->get_l4_proto(l4_proto) < 0) {
             throw TrexFStatEx("Failed determining l4 proto for packet", TrexException::T_FLOW_STAT_FAILED_FIND_L4);
         }
 
+
         // throws exception if there is error
         if (do_action) {
-            m_user_id_map.add_stream(stream->m_rx_check.m_pg_id, l4_proto);
+            uint8_t ipv6_next_h = l4_proto; //??? just for now
+            m_user_id_map.add_stream(stream->m_rx_check.m_pg_id, l3_proto, l4_proto, ipv6_next_h);
         }
         break;
     case TrexPlatformApi::IF_STAT_PAYLOAD:
@@ -610,7 +609,7 @@ int CFlowStatRuleMgr::add_stream_internal(TrexStream * stream, bool do_action) {
                               , TrexException::T_FLOW_STAT_PAYLOAD_TOO_SHORT);
         }
         if (do_action) {
-            m_user_id_map.add_stream(stream->m_rx_check.m_pg_id, PAYLOAD_RULE_PROTO);
+            m_user_id_map.add_stream(stream->m_rx_check.m_pg_id, 0, PAYLOAD_RULE_PROTO, 0);
         }
         break;
     default:
@@ -759,7 +758,10 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) {
                     m_max_hw_id = hw_id;
                 }
                 m_hw_id_map.map(hw_id, user_id);
-                add_hw_rule(hw_id, m_user_id_map.l4_proto(user_id));
+                CFlowStatUserIdInfo *uid_info = m_user_id_map.find_user_id(user_id);
+                if (uid_info != NULL) {
+                    add_hw_rule(hw_id, uid_info->get_l3_proto(), uid_info->get_l4_proto(), uid_info->get_ipv6_next_h());
+                }
             } else {
                 if (hw_id > m_max_hw_id_payload) {
                     m_max_hw_id_payload = hw_id;
@@ -821,9 +823,9 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) {
     return 0;
 }
 
-int CFlowStatRuleMgr::add_hw_rule(uint16_t hw_id, uint8_t proto) {
+int CFlowStatRuleMgr::add_hw_rule(uint16_t hw_id, uint16_t l3_proto, uint8_t l4_proto, uint8_t ipv6_next_h) {
     for (int port = 0; port < m_num_ports; port++) {
-        m_api->add_rx_flow_stat_rule(port, FLOW_STAT_RULE_TYPE_IPV4_ID, proto, hw_id);
+        m_api->add_rx_flow_stat_rule(port, l3_proto, l4_proto, ipv6_next_h, hw_id);
     }
 
     return 0;
@@ -862,7 +864,6 @@ int CFlowStatRuleMgr::stop_stream(TrexStream * stream) {
     if (m_user_id_map.stop_stream(stream->m_rx_check.m_pg_id) == 0) {
         // last stream associated with the entry stopped transmittig.
         // remove user_id <--> hw_id mapping
-        uint8_t proto = m_user_id_map.l4_proto(stream->m_rx_check.m_pg_id);
         uint16_t hw_id = m_user_id_map.get_hw_id(stream->m_rx_check.m_pg_id);
         if (hw_id >= MAX_FLOW_STATS) {
             throw TrexFStatEx("Internal error in stop_stream. Got bad hw_id" + std::to_string(hw_id)
@@ -881,7 +882,8 @@ int CFlowStatRuleMgr::stop_stream(TrexStream * stream) {
             rfc2544_info_t rfc2544_info;
             for (uint8_t port = 0; port < m_num_ports; port++) {
                 if (rule_type == TrexPlatformApi::IF_STAT_IPV4_ID) {
-                    m_api->del_rx_flow_stat_rule(port, FLOW_STAT_RULE_TYPE_IPV4_ID, proto, hw_id);
+                    m_api->del_rx_flow_stat_rule(port, p_user_id->get_l3_proto(), p_user_id->get_l4_proto()
+                                                 , p_user_id->get_ipv6_next_h(), hw_id);
                 }
                 m_api->get_flow_stats(port, &rx_cntr, (void *)&tx_cntr, hw_id, hw_id, true, rule_type);
                 // when stopping, always send counters for stopped stream one last time
diff --git a/src/flow_stat.h b/src/flow_stat.h
index 05b94f04..dee1acc8 100644
--- a/src/flow_stat.h
+++ b/src/flow_stat.h
@@ -268,7 +268,7 @@ class CFlowStatParser;
 
 class CFlowStatUserIdInfo {
  public:
-    CFlowStatUserIdInfo(uint8_t proto);
+    CFlowStatUserIdInfo(uint16_t l3_proto, uint8_t l4_proto, uint8_t ipv6_next_h);
     virtual ~CFlowStatUserIdInfo() {};
     friend std::ostream& operator<<(std::ostream& os, const CFlowStatUserIdInfo& cf);
     void set_rx_cntr(uint8_t port, rx_per_flow_t val) {m_rx_cntr[port] = val;}
@@ -279,7 +279,9 @@ class CFlowStatUserIdInfo {
     uint16_t get_hw_id() {return m_hw_id;}
     virtual void reset_hw_id();
     bool is_hw_id() {return (m_hw_id != UINT16_MAX);}
-    uint64_t get_proto() {return m_proto;}
+    uint16_t get_l3_proto() {return m_l3_proto;}
+    uint8_t get_l4_proto() {return m_l4_proto;}
+    uint8_t get_ipv6_next_h() {return m_ipv6_next_h;}
     uint8_t get_ref_count() {return m_ref_count;}
     virtual void add_stream(uint8_t proto);
     int del_stream() {m_ref_count--; return m_ref_count;}
@@ -309,7 +311,9 @@ class CFlowStatUserIdInfo {
     tx_per_flow_t m_tx_cntr[TREX_MAX_PORTS]; // How many packets transmitted with this user id since stream start
     // How many packets transmitted with this user id, since stream creation, before stream start.
     tx_per_flow_t m_tx_cntr_base[TREX_MAX_PORTS];
-    uint8_t m_proto;      // protocol (UDP, TCP, other), associated with this user id.
+    uint16_t m_l3_proto;      // L3 protocol (IPv4, IPv6), associated with this user id.
+    uint8_t m_l4_proto;      // L4 protocol (UDP, TCP, other), associated with this user id.
+    uint8_t m_ipv6_next_h;   // In case of IPv6, what is the type of the first extension header
     uint8_t m_ref_count;  // How many streams with this user id exists
     uint8_t m_trans_ref_count;  // How many streams with this user id currently transmit
     bool m_was_sent; // Did we send this info to clients once?
@@ -320,7 +324,9 @@ typedef std::map<uint32_t, class CFlowStatUserIdInfo *>::iterator flow_stat_user
 
 class CFlowStatUserIdInfoPayload : public CFlowStatUserIdInfo {
  public:
-    CFlowStatUserIdInfoPayload(uint8_t proto) : CFlowStatUserIdInfo(proto){m_rfc2544_support = true; clear();};
+    CFlowStatUserIdInfoPayload(uint16_t l3_proto, uint8_t l4_proto, uint8_t ipv6_next_h)
+                               : CFlowStatUserIdInfo(l3_proto, l4_proto, ipv6_next_h)
+        {m_rfc2544_support = true; clear();}
     virtual void add_stream(uint8_t proto);
 
     void clear() {
@@ -408,14 +414,13 @@ class CFlowStatUserIdMap {
     bool is_empty() {return (m_map.empty() == true);};
     uint16_t get_hw_id(uint32_t user_id);
     CFlowStatUserIdInfo * find_user_id(uint32_t user_id);
-    CFlowStatUserIdInfo * add_user_id(uint32_t user_id, uint8_t proto);
-    void add_stream(uint32_t user_id, uint8_t proto);
+    CFlowStatUserIdInfo * add_user_id(uint32_t user_id, uint16_t l3_proto, uint8_t l4_proto, uint8_t ipv6_next_h);
+    void add_stream(uint32_t user_id, uint16_t l3_proto, uint8_t l4_proto, uint8_t ipv6_next_h);
     int del_stream(uint32_t user_id);
     int start_stream(uint32_t user_id, uint16_t hw_id);
     int start_stream(uint32_t user_id);
     int stop_stream(uint32_t user_id);
     bool is_started(uint32_t user_id);
-    uint8_t l4_proto(uint32_t user_id);
     uint16_t unmap(uint32_t user_id);
     flow_stat_user_id_map_it_t begin() {return m_map.begin();}
     flow_stat_user_id_map_it_t end() {return m_map.end();}
@@ -464,7 +469,7 @@ class CFlowStatRuleMgr {
     void create();
     int compile_stream(const TrexStream * stream, CFlowStatParser *parser);
     int add_stream_internal(TrexStream * stream, bool do_action);
-    int add_hw_rule(uint16_t hw_id, uint8_t proto);
+    int add_hw_rule(uint16_t hw_id, uint16_t l3_proto, uint8_t l4_proto, uint8_t ipv6_next_h);
     void send_start_stop_msg_to_rx(bool is_start);
 
  private:
diff --git a/src/flow_stat_parser.cpp b/src/flow_stat_parser.cpp
index 602ef310..deae7062 100644
--- a/src/flow_stat_parser.cpp
+++ b/src/flow_stat_parser.cpp
@@ -119,6 +119,20 @@ int CFlowStatParser::set_ip_id(uint32_t new_id) {
     return -1;
 }
 
+int CFlowStatParser::get_l3_proto(uint16_t &proto) {
+    if (m_ipv4) {
+        proto = EthernetHeader::Protocol::IP;
+        return 0;
+    }
+
+    if (m_ipv6) {
+        proto = EthernetHeader::Protocol::IPv6;
+        return 0;
+    }
+
+    return -1;
+}
+
 int CFlowStatParser::get_l4_proto(uint8_t &proto) {
     if (m_ipv4) {
         proto = m_ipv4->getProtocol();
diff --git a/src/flow_stat_parser.h b/src/flow_stat_parser.h
index d44c8a7f..8ec5a229 100644
--- a/src/flow_stat_parser.h
+++ b/src/flow_stat_parser.h
@@ -37,6 +37,7 @@ class CFlowStatParser {
     virtual bool is_stat_supported() {return m_stat_supported == true;}
     virtual int get_ip_id(uint32_t &ip_id);
     virtual int set_ip_id(uint32_t ip_id);
+    virtual int get_l3_proto(uint16_t &proto);
     virtual int get_l4_proto(uint8_t &proto);
     virtual int get_payload_len(uint8_t *p, uint16_t len, uint16_t &payload_len);
     virtual uint16_t get_pkt_size();
diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h
index c9247130..7a2a0c8a 100644
--- a/src/internal_api/trex_platform_api.h
+++ b/src/internal_api/trex_platform_api.h
@@ -32,7 +32,7 @@ limitations under the License.
 
 /**
  * Global stats
- * 
+ *
  * @author imarom (06-Oct-15)
  */
 
@@ -48,23 +48,23 @@ public:
         double m_rx_cpu_util;
         double m_tx_bps;
         double m_rx_bps;
-       
+
         double m_tx_pps;
         double m_rx_pps;
-       
+
         uint64_t m_total_tx_pkts;
         uint64_t m_total_rx_pkts;
-       
+
         uint64_t m_total_tx_bytes;
         uint64_t m_total_rx_bytes;
-       
+
         uint64_t m_tx_rx_errors;
     } m_stats;
 };
 
 /**
  * Per Interface stats
- * 
+ *
  * @author imarom (26-Oct-15)
  */
 class TrexPlatformInterfaceStats {
@@ -80,16 +80,16 @@ public:
 
         double m_tx_bps;
         double m_rx_bps;
-       
+
         double m_tx_pps;
         double m_rx_pps;
-       
+
         uint64_t m_total_tx_pkts;
         uint64_t m_total_rx_pkts;
-       
+
         uint64_t m_total_tx_bytes;
         uint64_t m_total_rx_bytes;
-       
+
         uint64_t m_tx_rx_errors;
     } m_stats;
 };
@@ -97,8 +97,8 @@ public:
 
 /**
  * low level API interface
- * can be implemented by DPDK or mock 
- *  
+ * can be implemented by DPDK or mock
+ *
  * @author imarom (25-Oct-15)
  */
 
@@ -110,7 +110,7 @@ public:
         IF_STAT_IPV6_FLOW_LABEL = 4,
         IF_STAT_RX_BYTES_COUNT = 8, // Card support counting rx bytes
     };
-    
+
     enum driver_speed_e {
         SPEED_INVALID,
         SPEED_1G,
@@ -126,7 +126,7 @@ public:
 
     /**
      * interface static info
-     * 
+     *
      */
     struct intf_info_st {
         std::string     driver_name;
@@ -152,8 +152,10 @@ public:
     virtual int get_rx_err_cntrs(void *rx_err_cntrs) const = 0;
     virtual int reset_hw_flow_stats(uint8_t port_id) const = 0;
     virtual void get_port_num(uint8_t &port_num) const = 0;
-    virtual int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const = 0;
-    virtual int del_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const = 0;
+    virtual int add_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto
+                                      , uint8_t ipv6_next_h, uint16_t id) const = 0;
+    virtual int del_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto
+                                      , uint8_t ipv6_next_h, uint16_t id) const = 0;
     virtual void set_promiscuous(uint8_t port_id, bool enabled) const = 0;
     virtual bool get_promiscuous(uint8_t port_id) const = 0;
     virtual void flush_dp_messages() const = 0;
@@ -169,7 +171,7 @@ public:
 
 /**
  * DPDK implementation of the platform API
- * 
+ *
  * @author imarom (26-Oct-15)
  */
 class TrexDpdkPlatformApi : public TrexPlatformApi {
@@ -189,8 +191,10 @@ public:
     int get_rx_err_cntrs(void *rx_err_cntrs) const;
     int reset_hw_flow_stats(uint8_t port_id) const;
     void get_port_num(uint8_t &port_num) const;
-    int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const;
-    int del_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const;
+    virtual int add_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto
+                                      , uint8_t ipv6_next_h, uint16_t id) const;
+    virtual int del_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto
+                                      , uint8_t ipv6_next_h, uint16_t id) const;
     void set_promiscuous(uint8_t port_id, bool enabled) const;
     bool get_promiscuous(uint8_t port_id) const;
     void flush_dp_messages() const;
@@ -204,7 +208,7 @@ public:
 
 /**
  * for simulation
- * 
+ *
  * @author imarom (25-Feb-16)
  */
 class SimPlatformApi : public TrexPlatformApi {
@@ -250,12 +254,12 @@ public:
     virtual int get_rx_err_cntrs(void *rx_err_cntrs) const {return 0;};
     virtual int reset_hw_flow_stats(uint8_t port_id) const {return 0;};
     virtual void get_port_num(uint8_t &port_num) const {port_num = 2;};
-    virtual int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const {return 0;}
-    virtual int del_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const {return 0;}
-
+    virtual int add_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto
+                                      , uint8_t ipv6_next_h, uint16_t id) const {return 0;};
+    virtual int del_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto
+                                      , uint8_t ipv6_next_h, uint16_t id) const {return 0;};
     void set_promiscuous(uint8_t port_id, bool enabled) const {
     }
-
     bool get_promiscuous(uint8_t port_id) const {
         return false;
     }
diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp
index f5261f8d..e5884f93 100644
--- a/src/main_dpdk.cpp
+++ b/src/main_dpdk.cpp
@@ -136,7 +136,8 @@ public:
         return(false);
     }
     virtual int configure_rx_filter_rules(CPhyEthIF * _if)=0;
-    virtual int add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint8_t type, uint16_t proto, uint16_t id) {return -1;};
+    virtual int add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint16_t l3, uint8_t l4
+                                          , uint8_t ipv6_next_h, uint16_t id) {return -1;};
     virtual bool is_hardware_support_drop_queue(){
         return(false);
     }
@@ -316,11 +317,10 @@ public:
 
     virtual void update_global_config_fdir(port_cfg_t * cfg){
     }
-
     virtual void update_configuration(port_cfg_t * cfg);
-
     virtual int configure_rx_filter_rules(CPhyEthIF * _if);
-    virtual int add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint8_t type, uint16_t proto, uint16_t id);
+    virtual int add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint16_t l3_proto
+                                          , uint8_t l4_proto, uint8_t ipv6_next_h, uint16_t id);
     virtual bool is_hardware_filter_is_supported(){
         return (true);
     }
@@ -1339,17 +1339,17 @@ void CPhyEthIF::configure(uint16_t nb_rx_queue,
     rte_eth_dev_info_get(m_port_id, &m_dev_info);
 
     if (CGlobalInfo::m_options.preview.getChecksumOffloadEnable()) {
-		/* check if the device supports TCP and UDP checksum offloading */
-		if ((m_dev_info.tx_offload_capa & DEV_TX_OFFLOAD_UDP_CKSUM) == 0) {
-			rte_exit(EXIT_FAILURE, "Device does not support UDP checksum offload: "
-					 "port=%u\n",
-					 m_port_id);
-		}
-		if ((m_dev_info.tx_offload_capa & DEV_TX_OFFLOAD_TCP_CKSUM) == 0) {
-			rte_exit(EXIT_FAILURE, "Device does not support TCP checksum offload: "
-					 "port=%u\n",
-					 m_port_id);
-		}
+        /* check if the device supports TCP and UDP checksum offloading */
+        if ((m_dev_info.tx_offload_capa & DEV_TX_OFFLOAD_UDP_CKSUM) == 0) {
+            rte_exit(EXIT_FAILURE, "Device does not support UDP checksum offload: "
+                     "port=%u\n",
+                     m_port_id);
+        }
+        if ((m_dev_info.tx_offload_capa & DEV_TX_OFFLOAD_TCP_CKSUM) == 0) {
+            rte_exit(EXIT_FAILURE, "Device does not support TCP checksum offload: "
+                     "port=%u\n",
+                     m_port_id);
+        }
     }
 }
 
@@ -2176,7 +2176,7 @@ void CCoreEthIF::apply_client_cfg(const ClientCfg *cfg, rte_mbuf_t *m, pkt_dir_t
     /* VLAN */
     if (cfg_dir.has_vlan()) {
         add_vlan(m, cfg_dir.get_vlan());
-    }   
+    }
 }
 
 
@@ -2188,11 +2188,11 @@ void CCoreEthIF::add_vlan(rte_mbuf_t *m, uint16_t vlan_id) {
 
 /**
  * slow path features goes here (avoid multiple IFs)
- * 
+ *
  */
 void CCoreEthIF::handle_slowpath_features(CGenNode *node, rte_mbuf_t *m, uint8_t *p, pkt_dir_t dir) {
 
-  
+
     /* MAC ovverride */
     if ( unlikely( CGlobalInfo::m_options.preview.get_mac_ip_overide_enable() ) ) {
         /* client side */
@@ -2231,7 +2231,7 @@ int CCoreEthIF::send_node(CGenNode * node) {
     dir         = node->cur_interface_dir();
     single_port = node->get_is_all_flow_from_same_dir() ;
 
-   
+
     if ( unlikely( CGlobalInfo::m_options.preview.get_vlan_mode_enable() ) ){
         /* which vlan to choose 0 or 1*/
         uint8_t vlan_port = (node->m_src_ip &1);
@@ -2259,7 +2259,7 @@ int CCoreEthIF::send_node(CGenNode * node) {
     /* update mac addr dest/src 12 bytes */
     uint8_t *p   = rte_pktmbuf_mtod(m, uint8_t*);
     uint8_t p_id = lp_port->m_port->get_port_id();
-    
+
     memcpy(p,CGlobalInfo::m_options.get_dst_src_mac_addr(p_id),12);
 
      /* when slowpath features are on */
@@ -2267,7 +2267,7 @@ int CCoreEthIF::send_node(CGenNode * node) {
         handle_slowpath_features(node, m, p, dir);
     }
 
-   
+
     if ( unlikely( node->is_rx_check_enabled() ) ) {
         lp_stats->m_tx_rx_check_pkt++;
         lp->do_generate_new_mbuf_rxcheck(m, node, single_port);
@@ -2829,9 +2829,9 @@ public:
     int  reset_counters();
 
     /**
-     * mark for shutdown 
-     * on the next check - the control plane will 
-     * call shutdown() 
+     * mark for shutdown
+     * on the next check - the control plane will
+     * call shutdown()
      */
     void mark_for_shutdown(shutdown_rc_e rc) {
 
@@ -2858,7 +2858,7 @@ private:
 
     /**
      * shutdown sequence
-     * 
+     *
      */
     void shutdown();
 
@@ -4114,14 +4114,14 @@ CGlobalTRex::handle_fast_path() {
 
 /**
  * shutdown sequence
- * 
+ *
  */
 void CGlobalTRex::shutdown() {
     std::stringstream ss;
     ss << " *** TRex is shutting down - cause: '";
 
     switch (m_mark_for_shutdown) {
-    
+
     case SHUTDOWN_TEST_ENDED:
         ss << "test has ended'";
         break;
@@ -4133,7 +4133,7 @@ void CGlobalTRex::shutdown() {
     case SHUTDOWN_SIGINT:
         ss << "received signal SIGINT'";
         break;
-            
+
     case SHUTDOWN_SIGTERM:
         ss << "received signal SIGTERM'";
         break;
@@ -4220,7 +4220,7 @@ int CGlobalTRex::run_in_master() {
 
     /* shutdown everything gracefully */
     shutdown();
-   
+
     return (0);
 }
 
@@ -5635,22 +5635,42 @@ extern "C" int rte_eth_fdir_stats_reset(uint8_t port_id, uint32_t *stats, uint32
 // type - rule type. Currently we only support rules in IP ID.
 // proto - Packet protocol: UDP or TCP
 // id - Counter id in HW. We assume it is in the range 0..MAX_FLOW_STATS
-int CTRexExtendedDriverBase40G::add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint8_t type, uint16_t proto, uint16_t id) {
+int CTRexExtendedDriverBase40G::add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint16_t l3_proto
+                                                          , uint8_t l4_proto, uint8_t ipv6_next_h, uint16_t id) {
     uint32_t rule_id = (port_id % m_if_per_card) * MAX_FLOW_STATS + id;
     uint16_t rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_OTHER;
+    uint8_t next_proto;
 
-    switch(proto) {
-    case IPPROTO_TCP:
-        rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_TCP;
-        break;
-    case IPPROTO_UDP:
-        rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_UDP;
-        break;
-    default:
-        rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_OTHER;
-        break;
+    if (l3_proto == EthernetHeader::Protocol::IP) {
+        next_proto = l4_proto;
+        switch(l4_proto) {
+        case IPPROTO_TCP:
+            rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_TCP;
+            break;
+        case IPPROTO_UDP:
+            rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_UDP;
+            break;
+        default:
+            rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_OTHER;
+            break;
+        }
+    } else {
+        // IPv6
+        next_proto = ipv6_next_h;
+        switch(l4_proto) {
+        case IPPROTO_TCP:
+            rte_type = RTE_ETH_FLOW_NONFRAG_IPV6_TCP;
+            break;
+        case IPPROTO_UDP:
+            rte_type = RTE_ETH_FLOW_NONFRAG_IPV6_UDP;
+            break;
+        default:
+            rte_type = RTE_ETH_FLOW_NONFRAG_IPV6_OTHER;
+            break;
+        }
     }
-    add_del_rules(op, port_id, rte_type, 0, IP_ID_RESERVE_BASE + id, proto, MAIN_DPDK_DATA_Q, rule_id);
+
+    add_del_rules(op, port_id, rte_type, 0, IP_ID_RESERVE_BASE + id, next_proto, MAIN_DPDK_DATA_Q, rule_id);
     return 0;
 }
 
@@ -5909,10 +5929,10 @@ struct rte_mbuf *  rte_mbuf_convert_to_one_seg(struct rte_mbuf *m){
 
 /**
  * handle a signal for termination
- * 
+ *
  * @author imarom (7/27/2016)
- * 
- * @param signum 
+ *
+ * @param signum
  */
 static void trex_termination_handler(int signum) {
     std::stringstream ss;
@@ -5932,7 +5952,7 @@ static void trex_termination_handler(int signum) {
     default:
         assert(0);
     }
-    
+
 }
 
 /***********************************************************
@@ -6061,14 +6081,16 @@ int TrexDpdkPlatformApi::reset_hw_flow_stats(uint8_t port_id) const {
     return g_trex.m_ports[port_id].reset_hw_flow_stats();
 }
 
-int TrexDpdkPlatformApi::add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const {
+int TrexDpdkPlatformApi::add_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto
+                                               , uint8_t ipv6_next_h, uint16_t id) const {
     return CTRexExtendedDriverDb::Ins()->get_drv()
-        ->add_del_rx_flow_stat_rule(port_id, RTE_ETH_FILTER_ADD, type, proto, id);
+        ->add_del_rx_flow_stat_rule(port_id, RTE_ETH_FILTER_ADD, l3_type, l4_proto, ipv6_next_h, id);
 }
 
-int TrexDpdkPlatformApi::del_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const {
+int TrexDpdkPlatformApi::del_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto
+                                               , uint8_t ipv6_next_h, uint16_t id) const {
     return CTRexExtendedDriverDb::Ins()->get_drv()
-        ->add_del_rx_flow_stat_rule(port_id, RTE_ETH_FILTER_DELETE, type, proto, id);
+        ->add_del_rx_flow_stat_rule(port_id, RTE_ETH_FILTER_DELETE, l3_type, l4_proto, ipv6_next_h, id);
 }
 
 void TrexDpdkPlatformApi::set_promiscuous(uint8_t port_id, bool enabled) const {
@@ -6107,7 +6129,7 @@ CFlowStatParser *TrexDpdkPlatformApi::get_flow_stat_parser() const {
 
 /**
  * marks the control plane for a total server shutdown
- * 
+ *
  * @author imarom (7/27/2016)
  */
 void TrexDpdkPlatformApi::mark_for_shutdown() const {
diff --git a/src/main_dpdk.h b/src/main_dpdk.h
index c2169eea..f8c35732 100644
--- a/src/main_dpdk.h
+++ b/src/main_dpdk.h
@@ -125,8 +125,10 @@ class CPhyEthIF  {
     }
     void flush_dp_rx_queue(void);
     void flush_rx_queue(void);
-    int add_rx_flow_stat_rule(uint8_t type, uint16_t proto, uint16_t id);
-    int del_rx_flow_stat_rule(uint8_t type, uint16_t proto, uint16_t id);
+    int add_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto
+                          , uint8_t ipv6_next_h, uint16_t id) const;
+    int del_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto
+                          , uint8_t ipv6_next_h, uint16_t id) const;
     inline uint16_t  tx_burst(uint16_t queue_id, struct rte_mbuf **tx_pkts, uint16_t nb_pkts) {
         return rte_eth_tx_burst(m_port_id, queue_id, tx_pkts, nb_pkts);
     }
diff --git a/src/stateless/cp/trex_exception.h b/src/stateless/cp/trex_exception.h
index ff3179f9..3984e7e3 100644
--- a/src/stateless/cp/trex_exception.h
+++ b/src/stateless/cp/trex_exception.h
@@ -44,6 +44,7 @@ class TrexException : public std::runtime_error
         T_FLOW_STAT_UNSUPP_PKT_FORMAT,
         T_FLOW_STAT_BAD_RULE_TYPE,
         T_FLOW_STAT_BAD_RULE_TYPE_FOR_IF,
+        T_FLOW_STAT_FAILED_FIND_L3,
         T_FLOW_STAT_FAILED_FIND_L4,
         T_FLOW_STAT_PAYLOAD_TOO_SHORT,
         T_FLOW_STAT_NO_STREAMS_EXIST,
-- 
cgit