From 7b9d10888594ca9fe1114309e53c0dea9089085b Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Mon, 11 Jul 2016 16:58:21 +0300 Subject: NAT seq num randomization fully working --- src/bp_gtest.cpp | 14 ++ src/bp_sim.cpp | 21 ++- src/bp_sim.h | 2 + src/global_io_mode.cpp | 31 +++-- src/global_io_mode.h | 16 ++- src/latency.cpp | 10 +- src/latency.h | 1 + src/main_dpdk.cpp | 42 +++++- src/nat_check.cpp | 37 +---- src/nat_check.h | 19 +-- src/nat_check_flow_table.cpp | 314 +++++++++++++++++++++++++++++++++++++++++++ src/nat_check_flow_table.h | 111 +++++++++++++++ src/trex_defs.h | 3 + 13 files changed, 541 insertions(+), 80 deletions(-) create mode 100644 src/nat_check_flow_table.cpp create mode 100644 src/nat_check_flow_table.h (limited to 'src') diff --git a/src/bp_gtest.cpp b/src/bp_gtest.cpp index 3c6b2e40..a1f80407 100755 --- a/src/bp_gtest.cpp +++ b/src/bp_gtest.cpp @@ -32,6 +32,7 @@ limitations under the License. #include #include "platform_cfg.h" #include "latency.h" +#include "nat_check_flow_table.h" int test_policer(){ CPolicer policer; @@ -2400,7 +2401,20 @@ TEST_F(rx_check_system, rx_json) { printf(" %s \n",json.c_str()); } +class nat_check_flow_table : public testing::Test { + protected: + virtual void SetUp() { + } + virtual void TearDown() { + } +public: + CNatCheckFlowTable m_ft; +}; + +TEST_F(nat_check_flow_table, test1) { + m_ft.test(); +}; ////////////////////////////////////////////////////////////// diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp index f46f2824..b229d9bf 100755 --- a/src/bp_sim.cpp +++ b/src/bp_sim.cpp @@ -761,8 +761,10 @@ void CFlowGenStats::clear(){ m_total_close_flows =0; m_nat_lookup_no_flow_id=0; m_nat_lookup_remove_flow_id=0; + m_nat_lookup_wait_ack_state = 0; m_nat_lookup_add_flow_id=0; m_nat_flow_timeout=0; + m_nat_flow_timeout_wait_ack = 0; m_nat_flow_learn_error=0; } @@ -791,9 +793,12 @@ void CFlowGenStats::dump(FILE *fd){ DP(m_nat_lookup_no_flow_id); DP(m_nat_lookup_remove_flow_id); + DP(m_nat_lookup_wait_ack_state); DP(m_nat_lookup_add_flow_id); DP(m_nat_flow_timeout); + DP(m_nat_flow_timeout_wait_ack); DP_name("active_nat",(m_nat_lookup_add_flow_id-m_nat_lookup_remove_flow_id)); + DP_name("active_nat_wait_syn", (m_nat_lookup_add_flow_id - m_nat_lookup_wait_ack_state)); DP(m_nat_flow_learn_error); } @@ -2031,7 +2036,8 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::is_valid_template_loa , " Please give different CAP file, or try different --learn-mode\n"); return kTCPLearnModeBadFlow; } - if ((pkt_0_indication.m_cap_ipg < LEARN_MODE_MIN_IPG / 1000) || (pkt_1_indication.m_cap_ipg < LEARN_MODE_MIN_IPG / 1000)) { + if ((pkt_0_indication.m_cap_ipg < (double)LEARN_MODE_MIN_IPG / 1000) + || (pkt_1_indication.m_cap_ipg < (double)LEARN_MODE_MIN_IPG / 1000)) { fprintf(stderr , "Error: Bad cap file timings. In the chosen learn mode"); fprintf(stderr, "IPG between TCP handshake packets should be at least %d msec.\n", LEARN_MODE_MIN_IPG); @@ -2300,10 +2306,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st lp_prev->m_pkt_indication.m_cap_ipg = lp->m_pkt_indication.m_cap_ipg- lp_prev->m_pkt_indication.m_cap_ipg; - - - - printf("%d: IPG:%f", i, lp_prev->m_pkt_indication.m_cap_ipg); //??? remove if ( lp->m_pkt_indication.m_desc.IsInitSide() != lp_prev->m_pkt_indication.m_desc.IsInitSide()) { lp_prev->m_pkt_indication.m_desc.SetRtt(true); @@ -4177,6 +4179,11 @@ int CFlowGenListPerThread::reschedule_flow(CGenNode *node){ void CFlowGenListPerThread::terminate_nat_flows(CGenNode *p){ m_stats.m_nat_flow_timeout++; m_stats.m_nat_lookup_remove_flow_id++; + if (p->is_nat_wait_ack_state()) { + m_stats.m_nat_flow_timeout_wait_ack++; + } else { + m_stats.m_nat_lookup_wait_ack_state++; + } m_flow_id_to_node_lookup.remove_no_lookup(p->get_short_fid()); free_last_flow_node( p); } @@ -4228,6 +4235,7 @@ void CFlowGenListPerThread::handle_nat_msg(CGenNodeNatInfo * msg){ node->set_nat_tcp_seq_diff_client(nat_msg->m_tcp_seq - tcp->getSeqNumber()); if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { node->set_nat_wait_ack_state(); + m_stats.m_nat_lookup_wait_ack_state++; second = false; } else { node->set_nat_learn_state(); @@ -4256,7 +4264,8 @@ void CFlowGenListPerThread::handle_nat_msg(CGenNodeNatInfo * msg){ node->set_nat_ipv4_port(nat_msg->m_external_port); if ( CGlobalInfo::is_learn_verify_mode() ){ - if (!node->is_external_is_eq_to_internal_ip() ){ + if (!node->is_external_is_eq_to_internal_ip() || + node->get_nat_tcp_seq_diff_client() != 0) { m_stats.m_nat_flow_learn_error++; } } diff --git a/src/bp_sim.h b/src/bp_sim.h index 49e0e8dc..13934d74 100755 --- a/src/bp_sim.h +++ b/src/bp_sim.h @@ -1871,8 +1871,10 @@ public: uint64_t m_total_close_flows; uint64_t m_nat_lookup_no_flow_id; uint64_t m_nat_lookup_remove_flow_id; + uint64_t m_nat_lookup_wait_ack_state; uint64_t m_nat_lookup_add_flow_id; uint64_t m_nat_flow_timeout; + uint64_t m_nat_flow_timeout_wait_ack; uint64_t m_nat_flow_learn_error; public: diff --git a/src/global_io_mode.cpp b/src/global_io_mode.cpp index 2457599e..289863c9 100755 --- a/src/global_io_mode.cpp +++ b/src/global_io_mode.cpp @@ -103,6 +103,14 @@ bool CTrexGlobalIoMode::handle_io_modes(void){ m_g_mode=gNORMAL; } break; + case ccNat: + m_g_mode=gNAT; + m_nat_mode++; + if (m_nat_mode==natLAST) { + m_nat_mode = natDISABLE; + m_g_mode = gNORMAL; + } + break; } @@ -121,17 +129,18 @@ void CTrexGlobalIoMode::Dump(FILE *fd){ } void CTrexGlobalIoMode::DumpHelp(FILE *fd){ - fprintf(fd,"Help for Interactive Commands - Trex \n" ); - fprintf(fd," d : Toggle, Disable all -> Noraml \n"); - fprintf(fd," n : Default mode all in Normal mode \n"); - fprintf(fd," h : Toggle, Help->Normal \n"); - fprintf(fd,"\n"); - fprintf(fd," p : Per ports Toggle mode, disable -> table -> normal \n"); - fprintf(fd," a : Global ports Toggle mode, disable -> enable \n"); - fprintf(fd," l : Latency Toggle mode, disable -> enable -> enhanced \n"); - fprintf(fd," r : Rx check Toggle mode, disable -> enable -> enhanced \n"); - fprintf(fd," m : memory stats , disable -> enable \n"); - fprintf(fd," Press h or 1 to go back to Normal mode \n"); + fprintf(fd, "Help for Interactive Commands\n" ); + fprintf(fd, " %c : Toggle, Disable all/Default \n", ccGDISABLE); + fprintf(fd, " %c : Go back to default mode \n", ccGNORAML); + fprintf(fd, " %c : Toggle, Help/Default \n", ccHELP); + fprintf(fd, "\n"); + fprintf(fd, " %c : Per ports toggle disable -> table -> normal \n", ccGPP); + fprintf(fd, " %c : Global ports toggle disable/enable \n", ccGAP); + fprintf(fd, " %c : Latency toggle disable -> enable -> enhanced \n", ccGL); + fprintf(fd, " %c : Rx check toggle disable -> enable -> enhanced \n", ccGRC); + fprintf(fd, " %c : Memory stats toggle disable/enable \n", ccMem); + fprintf(fd, " %c : NAT pending flows toggle disable/enable \n", ccNat); + fprintf(fd, " Press %c or %c to go back to Normal mode \n", ccHELP, ccGNORAML); } diff --git a/src/global_io_mode.h b/src/global_io_mode.h index 84b402b7..44fa4be5 100755 --- a/src/global_io_mode.h +++ b/src/global_io_mode.h @@ -48,12 +48,13 @@ public: enum Chars{ ccHELP='h', ccGDISABLE='d', - ccGNORAML='n', + ccGNORAML='0', ccGPP='p', ccGAP='a', ccGL='l', ccGRC='r', - ccMem='m' + ccMem='m', + ccNat='n' }; enum CliDumpMode { @@ -67,7 +68,8 @@ public: gDISABLE=0, // no print at all gHELP=1, // help gNORMAL=2, // normal - gMem=3 + gMem=3, + gNAT }; @@ -104,12 +106,20 @@ public: }; typedef uint8_t RxCheckMode_t; + enum NatMode { + natDISABLE = 0, + natENABLE = 1, + natLAST = 2 + }; + typedef uint8_t NatMode_t; + Global_t m_g_mode; bool m_g_disable_first; PerPortCountersMode_t m_pp_mode; AllPortCountersMode_t m_ap_mode; LatecnyMode_t m_l_mode; RxCheckMode_t m_rc_mode; + NatMode_t m_nat_mode; public: void set_mode(CliDumpMode mode); diff --git a/src/latency.cpp b/src/latency.cpp index b9ce2177..768e161b 100644 --- a/src/latency.cpp +++ b/src/latency.cpp @@ -387,12 +387,6 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) { if ( ! is_lateancy_pkt) { -#if 0 - TCPHeader *tcp = (TCPHeader *)parser.m_l4; //????? remove - if (parser.m_ipv4->getProtocol() == 0x6 && tcp->getSynFlag()) { - tcp->dump(stdout); //???? remove - } -#endif #ifdef NAT_TRACE_ printf(" %.3f RX : got packet !!! \n",now_sec() ); #endif @@ -917,6 +911,10 @@ void CLatencyManager::DumpShortRxCheck(FILE *fd){ } } +void CLatencyManager::dump_nat_flow_table(FILE *fd) { + m_nat_check_manager.Dump(fd); +} + void CLatencyManager::rx_check_dump_json(std::string & json){ if ( get_is_rx_check_mode() ) { m_rx_check_manager.dump_json(json ); diff --git a/src/latency.h b/src/latency.h index 552b3999..63e50337 100644 --- a/src/latency.h +++ b/src/latency.h @@ -362,6 +362,7 @@ public: void DumpRxCheck(FILE *fd); // dump all void DumpShortRxCheck(FILE *fd); // dump short histogram of latency + void dump_nat_flow_table(FILE *fd); void rx_check_dump_json(std::string & json); uint16_t get_latency_header_offset(){ return ( m_pkt_gen.get_payload_offset() ); diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index d5e7c9b5..099ebb3b 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -2439,8 +2439,10 @@ public: uint64_t m_active_sockets; uint64_t m_total_nat_time_out; + uint64_t m_total_nat_time_out_wait_ack; uint64_t m_total_nat_no_fid ; uint64_t m_total_nat_active ; + uint64_t m_total_nat_syn_wait; uint64_t m_total_nat_open ; uint64_t m_total_nat_learn_error ; @@ -2559,8 +2561,10 @@ void CGlobalStats::dump_json(std::string & json, bool baseline){ json+=GET_FIELD(m_socket_util); json+=GET_FIELD(m_total_nat_time_out); + json+=GET_FIELD(m_total_nat_time_out_wait_ack); json+=GET_FIELD(m_total_nat_no_fid ); json+=GET_FIELD(m_total_nat_active ); + json+=GET_FIELD(m_total_nat_syn_wait); json+=GET_FIELD(m_total_nat_open ); json+=GET_FIELD(m_total_nat_learn_error); @@ -2596,7 +2600,12 @@ void CGlobalStats::DumpAllPorts(FILE *fd){ fprintf (fd," Platform_factor : %2.1f \n",m_platform_factor); fprintf (fd," Total-Tx : %s ",double_to_human_str(m_tx_bps,"bps",KBYE_1000).c_str()); if ( CGlobalInfo::is_learn_mode() ) { - fprintf (fd," Nat_time_out : %8llu \n", (unsigned long long)m_total_nat_time_out); + fprintf (fd," NAT time out : %8llu", (unsigned long long)m_total_nat_time_out); + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + fprintf (fd," (%llu in wait for syn+ack)\n", (unsigned long long)m_total_nat_time_out_wait_ack); + } else { + fprintf (fd, "\n"); + } }else{ fprintf (fd,"\n"); } @@ -2604,28 +2613,33 @@ void CGlobalStats::DumpAllPorts(FILE *fd){ fprintf (fd," Total-Rx : %s ",double_to_human_str(m_rx_bps,"bps",KBYE_1000).c_str()); if ( CGlobalInfo::is_learn_mode() ) { - fprintf (fd," Nat_no_fid : %8llu \n", (unsigned long long)m_total_nat_no_fid); + fprintf (fd," NAT aged flow id: %8llu \n", (unsigned long long)m_total_nat_no_fid); }else{ fprintf (fd,"\n"); } fprintf (fd," Total-PPS : %s ",double_to_human_str(m_tx_pps,"pps",KBYE_1000).c_str()); if ( CGlobalInfo::is_learn_mode() ) { - fprintf (fd," Total_nat_active: %8llu \n", (unsigned long long)m_total_nat_active); + fprintf (fd," Total NAT active: %8llu", (unsigned long long)m_total_nat_active); + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + fprintf (fd," (%llu waiting for syn)\n", (unsigned long long)m_total_nat_syn_wait); + } else { + fprintf (fd, "\n"); + } }else{ fprintf (fd,"\n"); } fprintf (fd," Total-CPS : %s ",double_to_human_str(m_tx_cps,"cps",KBYE_1000).c_str()); if ( CGlobalInfo::is_learn_mode() ) { - fprintf (fd," Total_nat_open : %8llu \n", (unsigned long long)m_total_nat_open); + fprintf (fd," Total NAT opened: %8llu \n", (unsigned long long)m_total_nat_open); }else{ fprintf (fd,"\n"); } fprintf (fd,"\n"); fprintf (fd," Expected-PPS : %s ",double_to_human_str(m_tx_expected_pps,"pps",KBYE_1000).c_str()); if ( CGlobalInfo::is_learn_verify_mode() ) { - fprintf (fd," Nat_learn_errors: %8llu \n", (unsigned long long)m_total_nat_learn_error); + fprintf (fd," NAT learn errors: %8llu \n", (unsigned long long)m_total_nat_learn_error); }else{ fprintf (fd,"\n"); } @@ -3647,8 +3661,10 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){ uint64_t total_nat_time_out =0; + uint64_t total_nat_time_out_wait_ack =0; uint64_t total_nat_no_fid =0; uint64_t total_nat_active =0; + uint64_t total_nat_syn_wait = 0; uint64_t total_nat_open =0; uint64_t total_nat_learn_error=0; @@ -3677,8 +3693,10 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){ total_sockets += lpt->m_smart_gen.MaxSockets(); total_nat_time_out +=lpt->m_stats.m_nat_flow_timeout; + total_nat_time_out_wait_ack += lpt->m_stats.m_nat_flow_timeout_wait_ack; total_nat_no_fid +=lpt->m_stats.m_nat_lookup_no_flow_id ; total_nat_active +=lpt->m_stats.m_nat_lookup_add_flow_id - lpt->m_stats.m_nat_lookup_remove_flow_id; + total_nat_syn_wait += lpt->m_stats.m_nat_lookup_add_flow_id - lpt->m_stats.m_nat_lookup_wait_ack_state; total_nat_open +=lpt->m_stats.m_nat_lookup_add_flow_id; total_nat_learn_error +=lpt->m_stats.m_nat_flow_learn_error; uint8_t port0 = lpt->getDualPortId() *2; @@ -3700,8 +3718,10 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){ } stats.m_total_nat_time_out = total_nat_time_out; + stats.m_total_nat_time_out_wait_ack = total_nat_time_out_wait_ack; stats.m_total_nat_no_fid = total_nat_no_fid; stats.m_total_nat_active = total_nat_active; + stats.m_total_nat_syn_wait = total_nat_syn_wait; stats.m_total_nat_open = total_nat_open; stats.m_total_nat_learn_error = total_nat_learn_error; @@ -3959,9 +3979,17 @@ CGlobalTRex::handle_slow_path(bool &was_stopped) { m_mg.DumpRxCheck(stdout); break; } - } - + } + } + if ( m_io_modes.m_g_mode == CTrexGlobalIoMode::gNAT ) { + if ( m_io_modes.m_nat_mode == CTrexGlobalIoMode::natENABLE ) { + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + fprintf(stdout, "NAT flow table info\n"); + m_mg.dump_nat_flow_table(stdout); + } else { + fprintf(stdout, "\nThis is only relevant in --learn-mode %d\n", CParserOption::LEARN_MODE_TCP_ACK); + } } } diff --git a/src/nat_check.cpp b/src/nat_check.cpp index c7262e50..f3dd93d1 100755 --- a/src/nat_check.cpp +++ b/src/nat_check.cpp @@ -52,29 +52,6 @@ void CGenNodeNatInfo::init(){ m_cnt=0; } -bool CNatCheckFlowTableMap::find(uint64_t key, uint32 &val) { - nat_check_flow_map_t::iterator iter; - iter = m_map.find(key); - if (iter != m_map.end() ) { - val = (*iter).second; - return true; - }else{ - return false; - } -} - -void CNatCheckFlowTableMap::dump(FILE *fd) { - nat_check_flow_map_iter_t it; - uint32_t val; - uint64_t key; - - for (it = m_map.begin(); it != m_map.end(); it++) { - val = it->second; - key = it->first; - fprintf(fd, "%lx->%x\n", key, val); - } -} - void CNatStats::reset(){ m_total_rx=0; m_total_msg=0; @@ -207,21 +184,20 @@ void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4, bool uint32_t dst_ip = ipv4->getDestIp(); uint16_t dst_port = tcp->getDestPort(); uint64_t map_key = (dst_ip << 16) + dst_port; - m_fm.insert(map_key, tcp_ack); + double time_stamp = now_sec(); + m_ft.insert(map_key, tcp_ack, time_stamp); + m_ft.clear_old(time_stamp - 1); } } else { uint32_t val; // server->client packet. IP/port reversed in regard to first SYN packet uint64_t map_key = (ext_ip << 16) + ext_port; - - if (m_fm.find(map_key, val)) { + if (m_ft.erase(map_key, val)) { get_info_from_tcp_ack(val, fid, thread_id); thread_info = get_thread_info(thread_id); - m_fm.erase(map_key); } else { + // flow was not found in the table thread_info = 0; - // ??? Handle error - // ??? handle aging of flow info } } } @@ -279,7 +255,8 @@ void CNatStats::Dump(FILE *fd){ void CNatRxManager::Dump(FILE *fd){ - m_stats.Dump(stdout); + m_stats.Dump(fd); + m_ft.dump(fd); } void CNatRxManager::DumpShort(FILE *fd){ diff --git a/src/nat_check.h b/src/nat_check.h index 3b526c0b..18add5e0 100755 --- a/src/nat_check.h +++ b/src/nat_check.h @@ -29,6 +29,7 @@ limitations under the License. #include #include #include "os_time.h" +#include "nat_check_flow_table.h" // 2msec timeout #define MAX_TIME_MSG_IN_QUEUE_SEC ( 0.002 ) @@ -211,22 +212,6 @@ public: void Dump(FILE *fd); }; -typedef std::map > nat_check_flow_map_t; -typedef nat_check_flow_map_t::iterator nat_check_flow_map_iter_t; - -class CNatCheckFlowTableMap { -public: - void erase(uint64_t key) {m_map.erase(key);} - bool find(uint64_t fid, uint32_t &val); - void insert(uint64_t key, uint32_t val) {m_map.insert(std::pair(key, val));} - void clear(void) {m_map.clear();} - void dump(FILE *fd); - uint64_t size(void) {return m_map.size();} - -public: - nat_check_flow_map_t m_map; -}; - class CNatRxManager { public: @@ -248,7 +233,7 @@ private: uint8_t m_max_threads; CNatPerThreadInfo * m_per_thread; CNatStats m_stats; - CNatCheckFlowTableMap m_fm; + CNatCheckFlowTable m_ft; }; diff --git a/src/nat_check_flow_table.cpp b/src/nat_check_flow_table.cpp new file mode 100644 index 00000000..18ba8142 --- /dev/null +++ b/src/nat_check_flow_table.cpp @@ -0,0 +1,314 @@ +/* + 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 +#include +#include +#include +#include +#include "nat_check_flow_table.h" +/* + classes in this file: + CNatCheckFlowTableList - List implementation + CNatCheckFlowTableMap - Map implementation + CNatData - element of data that exists in the map and the list. + CNatCheckFlowTable - Wrapper class which is the interface to outside. + New element is always inserted to the list and map. + If element is removed, it is always removed from list and map together. + Map is used to lookup elemnt by key. + List is used to clean up old elements by timestamp. We guarantee the list is always sorted by timestamp, + since we always insert at the end, with increasing timestamp. +*/ + +std::ostream& operator<<(std::ostream& os, const CNatData &cn) { + os << "(" << &cn << ")" << "data:" << cn.m_data << " time:" << cn.m_timestamp; + os << " prev:" << cn.m_prev << " next:" << cn.m_next; + return os; +} + +// map implementation +CNatData *CNatCheckFlowTableMap::erase(uint64_t key) { + nat_check_flow_map_iter_no_const_t it = m_map.find(key); + + if (it != m_map.end()) { + CNatData *val = it->second; + m_map.erase(it); + return val; + } + + return NULL; +} + +bool CNatCheckFlowTableMap::find(uint64_t key, uint32_t &val) { + nat_check_flow_map_iter_t it = m_map.find(key); + + if (it != m_map.end()) { + CNatData *data = it->second; + val = data->get_data(); + return true; + } + + return false; +} + +// for testing +bool CNatCheckFlowTableMap::verify(uint32_t *arr, int size) { + uint32_t val = -1; + int real_size = 0; + + for (int i = 0; i < size; i++) { + if (arr[i] == -1) + continue; + real_size++; + find(i, val); + if (val != arr[i]) + return false; + } + + if (m_map.size() != real_size) { + std::cout << "Error: Wrong map size " << m_map.size() << ". Should be " << real_size << std::endl; + return false; + } + + return true; +} + +CNatData * CNatCheckFlowTableMap::insert(uint64_t key, uint32_t val, double time) { + CNatData *elem = new CNatData; + assert(elem); + elem->set_data(val); + elem->set_key(key); + elem->set_timestamp(time); + std::pair pair = std::pair(key, elem); + + if (m_map.insert(pair).second == false) { + delete elem; + return NULL; + } else { + return elem; + } +} + +std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTableMap& cf) { + nat_check_flow_map_iter_t it; + + os << "NAT check flow table map:\n"; + for (it = cf.m_map.begin(); it != cf.m_map.end(); it++) { + CNatData *data = it->second; + uint32_t key = it->first; + os << " " << key << ":" << *data << std::endl; + } + return os; +} + +void CNatCheckFlowTableList::dump_short(FILE *fd) { + fprintf(fd, "list:\n"); + // this is not fully safe, since we call it from CP core, and rx core changes the list. + // It is usefull as a debug function + if (m_head) { + fprintf(fd, " head: time:%f key:%x data:%x\n", m_head->get_timestamp(), m_head->get_key(), m_head->get_data()); + fprintf(fd, " tail: time:%f key:%x data:%x\n", m_tail->get_timestamp(), m_tail->get_key(), m_tail->get_data()); + } +} + +// list implementation +// The list is always sorted by timestamp, since we always insert at the end, with increasing timestamp +void CNatCheckFlowTableList::erase(CNatData *data) { + if (m_head == data) { + m_head = data->m_next; + } + if (m_tail == data) { + m_tail = data->m_prev; + } + if (data->m_prev) { + data->m_prev->m_next = data->m_next; + } + if (data->m_next) { + data->m_next->m_prev = data->m_prev; + } +} + +// insert as last element in list +void CNatCheckFlowTableList::insert(CNatData *data) { + data->m_prev = m_tail; + data->m_next = NULL; + + if (m_tail != NULL) { + m_tail->m_next = data; + } else { + // if m_tail is NULL m_head is also NULL + m_head = data; + } + m_tail = data; +} + +bool CNatCheckFlowTableList::verify(uint32_t *arr, int size) { + int index = -1; + CNatData *elem = m_head; + int count = 0; + + while (index < size - 1) { + index++; + if (arr[index] == -1) { + continue; + } + count++; + + if (elem->get_data() != arr[index]) { + return false; + } + + if (elem == NULL) { + std::cout << "Too few items in list. Only " << count << std::endl; + return false; + } + elem = elem->m_next; + } + + // We expect number of items in list to be like num of val!=-1 in arr + if (elem != NULL) { + std::cout << "Too many items in list" << std::endl; + return false; + } + + return true; +} + +std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTableList& cf) { + CNatData *it = cf.m_head; + + os << "NAT check flow table list:\n"; + os << " head:" << cf.m_head << " tail:" << cf.m_tail << std::endl; + while (it != NULL) { + os << " " << *it << std::endl; + it = it->m_next; + } + + return os; +} + +// flow table +std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTable& cf) { + os << "========= Flow table start =========" << std::endl; + os << cf.m_map; + os << cf.m_list; + os << "========= Flow table end =========" << std::endl; + + return os; +} + +void CNatCheckFlowTable::clear_old(double time) { + CNatData *data = m_list.get_head(); + uint32_t val; + + while (data) { + if (data->get_timestamp() < time) { + erase(data->get_key(), val); + data = m_list.get_head(); + } else { + break; + } + } +} + +void CNatCheckFlowTable::dump(FILE *fd) { + fprintf(fd, "map size:%lu\n", m_map.size()); + m_list.dump_short(fd); +} + +bool CNatCheckFlowTable::erase(uint64_t key, uint32_t &val) { + CNatData *data = m_map.erase(key); + + if (!data) + return false; + + val = data->get_data(); + m_list.erase(data); + delete data; + + return true; +} + +bool CNatCheckFlowTable::insert(uint64_t key, uint32_t val, double time) { + CNatData *res; + + res = m_map.insert(key, val, time); + if (!res) { + return false; + } else { + m_list.insert(res); + } + return true; +} + +CNatCheckFlowTable::~CNatCheckFlowTable() { + clear_old(UINT64_MAX); +} + +bool CNatCheckFlowTable::test() { + uint32_t size = 100; + uint32_t arr[size]; + int i; + uint32_t val; + + for (i = 0; i < size; i++) { + arr[i] = i+200; + } + + // insert some elements + for (i = 0; i < size; i++) { + val = arr[i]; + assert(insert(i, val, i) == true); + } + + // insert same elements. should fail + for (i = 0; i < size; i++) { + val = arr[i]; + assert(insert(i, val, val) == false); + } + + // remove element we did not insert + assert(erase(size, val) == false); + + assert (m_map.verify(arr, size) == true); + assert (m_list.verify(arr, size) == true); + + // remove even keys + for (i = 0; i < size; i += 2) { + assert(erase(i, val) == true); + assert (val == arr[i]); + arr[i] = -1; + } + + assert (m_map.verify(arr, size) == true); + assert (m_list.verify(arr, size) == true); + + // clear half of the old values (We removed the even already, so 1/4 should be left) + clear_old(size/2); + for (i = 0; i < size/2; i++) { + arr [i] = -1; + } + + assert (m_map.verify(arr, size) == true); + assert (m_list.verify(arr, size) == true); + + return true; +} diff --git a/src/nat_check_flow_table.h b/src/nat_check_flow_table.h new file mode 100644 index 00000000..dddd45b6 --- /dev/null +++ b/src/nat_check_flow_table.h @@ -0,0 +1,111 @@ +/* + 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 NAT_CHECK_FLOW_TABLE_H +#define NAT_CHECK_FLOW_TABLE_H +#include + +class CNatData; +class CNatCheckFlowTableList; + +typedef std::map > nat_check_flow_map_t; +typedef nat_check_flow_map_t::const_iterator nat_check_flow_map_iter_t; +typedef nat_check_flow_map_t::iterator nat_check_flow_map_iter_no_const_t; + +// One element of list and map +class CNatData { + friend class CNatCheckFlowTableList; // access m_next and m_prev + friend std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTableList& cf); + friend std::ostream& operator<<(std::ostream& os, const CNatData &cn); + + public: + uint32_t get_data() {return m_data;} + void set_data(uint32_t val) {m_data = val;} + uint32_t get_key() {return m_key;} + void set_key(uint32_t val) {m_key = val;} + double get_timestamp() {return m_timestamp;} + void set_timestamp(double val) {m_timestamp = val;} + CNatData() { + m_next = NULL; + m_prev = NULL; + } + + private: + double m_timestamp; + uint64_t m_key; + CNatData *m_prev; + CNatData *m_next; + uint32_t m_data; +}; + +class CNatCheckFlowTableMap { + friend class CNatCheckFlowTable; + friend std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTableMap& cf); + + private: + void clear(void) {m_map.clear();} + CNatData *erase(uint64_t key); + bool find(uint64_t key, uint32_t &val); + CNatData *insert(uint64_t key, uint32_t val, double time); + bool verify(uint32_t *arr, int size); + uint64_t size(void) {return m_map.size();} + + private: + nat_check_flow_map_t m_map; +}; + +class CNatCheckFlowTableList { + friend class CNatCheckFlowTable; + friend std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTableList& cf); + + private: + void dump_short(FILE *fd); + void erase(CNatData *data); + CNatData *get_head() {return m_head;} + void insert(CNatData *data); + bool verify(uint32_t *arr, int size); + + CNatCheckFlowTableList() { + m_head = NULL; + m_tail = NULL; + } + + private: + CNatData *m_head; + CNatData *m_tail; +}; + +class CNatCheckFlowTable { + friend std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTable& cf); + + public: + ~CNatCheckFlowTable(); + void clear_old(double time); + void dump(FILE *fd); + bool erase(uint64_t key, uint32_t &val); + bool insert(uint64_t key, uint32_t val, double time); + bool test(); + + private: + CNatCheckFlowTableMap m_map; + CNatCheckFlowTableList m_list; +}; + +#endif diff --git a/src/trex_defs.h b/src/trex_defs.h index bbf3f3ba..9abb38f5 100644 --- a/src/trex_defs.h +++ b/src/trex_defs.h @@ -35,6 +35,9 @@ limitations under the License. #define UINT16_MAX 0xFFFF #endif +#ifndef UINT64_MAX + #define UINT64_MAX 0xFFFFFFFFFFFFFFFF +#endif typedef std::set flow_stat_active_t; typedef std::set::iterator flow_stat_active_it_t; -- cgit 1.2.3-korg