diff options
Diffstat (limited to 'src')
36 files changed, 2449 insertions, 875 deletions
diff --git a/src/bp_gtest.cpp b/src/bp_gtest.cpp index 79ea2458..a1f80407 100755 --- a/src/bp_gtest.cpp +++ b/src/bp_gtest.cpp @@ -32,6 +32,7 @@ limitations under the License. #include <common/cgen_map.h> #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(); +}; ////////////////////////////////////////////////////////////// @@ -2416,7 +2430,7 @@ public: assert(ipv4->getTimeToLive()==255); /* ip option packet */ printf(" rx got ip option packet ! \n"); - mg->handle_packet_ipv4(option,ipv4); + mg->handle_packet_ipv4(option, ipv4, true); delay(10); // delay for queue flush mg->handle_aging(); // flush the RxRing } @@ -2481,8 +2495,10 @@ protected: m_flow_info.Delete(); } public: - CCapFileFlowInfo m_flow_info; + void load_cap_file_errors_helper(std::string cap_file, enum CCapFileFlowInfo::load_cap_file_err expect); +public: + CCapFileFlowInfo m_flow_info; }; TEST_F(file_flow_info, f1) { @@ -2612,30 +2628,58 @@ TEST_F(file_flow_info, http_add_ipv6_option) { po->preview.set_ipv6_mode_enable(false); } +void file_flow_info::load_cap_file_errors_helper(std::string cap_file, enum CCapFileFlowInfo::load_cap_file_err expect) { + enum CCapFileFlowInfo::load_cap_file_err err; + + err = m_flow_info.load_cap_file(cap_file, 1, 0); + if (err == 0) err = m_flow_info.is_valid_template_load_time(); + if (err != expect) { + printf("Error in testing file %s. Expected error to be %d, but it is %d\n", cap_file.c_str(), expect, err); + } + assert (err == expect); +} + // Test error conditions when loading cap file TEST_F(file_flow_info, load_cap_file_errors) { - enum CCapFileFlowInfo::load_cap_file_err err; - CParserOption * po =&CGlobalInfo::m_options; - po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK; + CParserOption *po = &CGlobalInfo::m_options; - // file does not exist - err = m_flow_info.load_cap_file("/tmp/not_exist",1,0); - assert (err == CCapFileFlowInfo::kFileNotExist); + po->m_learn_mode = CParserOption::LEARN_MODE_DISABLED; + load_cap_file_errors_helper("/tmp/not_exist", CCapFileFlowInfo::kFileNotExist); // file format not supported - err = m_flow_info.load_cap_file("cap2/dns.yaml",1,0); - assert (err == CCapFileFlowInfo::kFileNotExist); - // udp in tcp learn mode - err = m_flow_info.load_cap_file("./cap2/dns.pcap",1,0); - assert (err == CCapFileFlowInfo::kNoTCPFromServer); - // First TCP packet without syn - err = m_flow_info.load_cap_file("./exp/tcp_no_syn.pcap",1,0); - assert (err == CCapFileFlowInfo::kNoSyn); - // TCP flags offset is too big - err = m_flow_info.load_cap_file("./exp/many_ip_options.pcap",1,0); - assert (err == CCapFileFlowInfo::kTCPOffsetTooBig); + load_cap_file_errors_helper("cap2/dns.yaml", CCapFileFlowInfo::kFileNotExist); + load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kOK); + load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kOK); + load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kOK); // Non IP packet - err = m_flow_info.load_cap_file("./exp/bad_not_ip.pcap",1,0); - assert (err == CCapFileFlowInfo::kPktProcessFail); + load_cap_file_errors_helper("./exp/bad_not_ip.pcap", CCapFileFlowInfo::kPktProcessFail); + load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK); + // more than 1 flow in cap file + load_cap_file_errors_helper("./exp/syn_attack.pcap", CCapFileFlowInfo::kCapFileErr); + + po->m_learn_mode = CParserOption::LEARN_MODE_IP_OPTION; + load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kOK); + load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kOK); + load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kIPOptionNotAllowed); + load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK); + + po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND; + // udp in tcp learn mode + load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kNoTCPFromServer); + // no SYN in first packet + load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kNoSyn); + // TCP flags offset is too big. We don't allow IP option, so can comment this. + // open this if we do allow IP options in the future + // load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kTCPOffsetTooBig); + load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK); + load_cap_file_errors_helper("./exp/no_tcp_syn_ack.pcap", CCapFileFlowInfo::kOK); + + po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK; + // too short. only two packets + load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kTCPLearnModeBadFlow); + // no SYN+ACK + load_cap_file_errors_helper("./exp/no_tcp_syn_ack.pcap", CCapFileFlowInfo::kNoTCPSynAck); + // IPG between TCP handshake packets too low + load_cap_file_errors_helper("./exp/tcp_low_ipg.pcap", CCapFileFlowInfo::kTCPIpgTooLow); } ////////////////////////////////////////////////////////////// diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp index db90107d..b229d9bf 100755 --- a/src/bp_sim.cpp +++ b/src/bp_sim.cpp @@ -498,40 +498,23 @@ void CRteMemPool::dump_in_case_of_error(FILE *fd){ dump(fd); } -std::string CRteMemPool::add_to_json( - std::string name, - rte_mempool_t * pool, - bool last){ +void CRteMemPool::add_to_json(Json::Value &json, std::string name, rte_mempool_t * pool){ uint32_t p_free = rte_mempool_count(pool); uint32_t p_size = pool->size; - char buff[200]; - sprintf(buff,"\"%s\":[%llu,%llu]",name.c_str(),(unsigned long long)p_free,(unsigned long long)p_size); - std::string json = std::string(buff) + (last?std::string(""):std::string(",")); - return (json); + json[name].append((unsigned long long)p_free); + json[name].append((unsigned long long)p_size); } -std::string CRteMemPool::dump_as_json(uint8_t id,bool last){ - - char buff[200]; - sprintf(buff,"\"socket-%d\":{", (int)id); - - std::string json=std::string(buff); - - json+=add_to_json("64b",m_small_mbuf_pool); - json+=add_to_json("128b",m_mbuf_pool_128); - json+=add_to_json("256b",m_mbuf_pool_256); - json+=add_to_json("512b",m_mbuf_pool_512); - json+=add_to_json("1024b",m_mbuf_pool_1024); - json+=add_to_json("2048b",m_mbuf_pool_2048); - json+=add_to_json("4096b",m_mbuf_pool_4096); - json+=add_to_json("9kb",m_mbuf_pool_9k,true); - - json+="}" ; - if (last==false) { - json+="," ; - } - return (json); +void CRteMemPool::dump_as_json(Json::Value &json){ + add_to_json(json, "64b", m_small_mbuf_pool); + add_to_json(json, "128b", m_mbuf_pool_128); + add_to_json(json, "256b", m_mbuf_pool_256); + add_to_json(json, "512b", m_mbuf_pool_512); + add_to_json(json, "1024b", m_mbuf_pool_1024); + add_to_json(json, "2048b", m_mbuf_pool_2048); + add_to_json(json, "4096b", m_mbuf_pool_4096); + add_to_json(json, "9kb", m_mbuf_pool_9k); } @@ -548,30 +531,24 @@ void CRteMemPool::dump(FILE *fd){ DUMP_MBUF("mbuf_9k",m_mbuf_pool_9k); } -//////////////////////////////////////// - -std::string CGlobalInfo::dump_pool_as_json(void){ +//////////////////////////////////////// - std::string json="\"mbuf_stats\":{"; +void CGlobalInfo::dump_pool_as_json(Json::Value &json){ CPlatformSocketInfo * lpSocket =&m_socket; - int last_socket=-1; - /* calc the last socket */ - int i; - for (i=0; i<(int)MAX_SOCKETS_SUPPORTED; i++) { + for (int i=0; i<(int)MAX_SOCKETS_SUPPORTED; i++) { if (lpSocket->is_sockets_enable((socket_id_t)i)) { - last_socket=i; + std::string socket_id = "cpu-socket-" + std::to_string(i); + m_mem_pool[i].dump_as_json(json["mbuf_stats"][socket_id]); } } +} - for (i=0; i<(int)MAX_SOCKETS_SUPPORTED; i++) { - if (lpSocket->is_sockets_enable((socket_id_t)i)) { - json+=m_mem_pool[i].dump_as_json(i,last_socket==i?true:false); - } - } - json+="},"; - return json; +std::string CGlobalInfo::dump_pool_as_json_str(void){ + Json::Value json; + dump_pool_as_json(json); + return (json.toStyledString()); } void CGlobalInfo::free_pools(){ @@ -771,9 +748,8 @@ void CPreviewMode::Dump(FILE *fd){ fprintf(fd," 1g mode : %d\n", (int)get_1g_mode() ); fprintf(fd," zmq_publish : %d\n", (int)get_zmq_publish_enable() ); fprintf(fd," vlan_enable : %d\n", (int)get_vlan_mode_enable() ); + fprintf(fd," client_cfg : %d\n", (int)get_is_client_cfg_enable() ); fprintf(fd," mbuf_cache_disable : %d\n", (int)isMbufCacheDisabled() ); - fprintf(fd," mac_ip_features : %d\n", (int)get_mac_ip_features_enable()?1:0 ); - fprintf(fd," mac_ip_map : %d\n", (int)get_mac_ip_mapping_enable()?1:0 ); fprintf(fd," vm mode : %d\n", (int)get_vm_one_queue_enable()?1:0 ); } @@ -785,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; } @@ -815,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); } @@ -1962,42 +1943,115 @@ typedef CTmpFlowInfo * flow_tmp_t; typedef std::map<uint16_t, flow_tmp_t> flow_tmp_map_t; typedef flow_tmp_map_t::iterator flow_tmp_map_iter_t; - - -bool CCapFileFlowInfo::is_valid_template_load_time(std::string & err){ - err=""; +enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::is_valid_template_load_time(){ int i; for (i=0; i<Size(); i++) { CFlowPktInfo * lp= GetPacket((uint32_t)i); CPacketIndication * lpd=&lp->m_pkt_indication; if ( lpd->getEtherOffset() !=0 ){ - err=" supported template Ether offset start is 0 \n"; - return (false); + fprintf(stderr, "Error: Bad CAP file. Ether offset start is not 0 in packet %d \n", i+1); + return kPktNotSupp; } - if ( lpd->getIpOffset() !=14 ){ - err=" supported template ip offset is 14 \n"; - return (false); - } - if ( lpd->is_ipv6() ){ - if ( lpd->getTcpOffset() != (14+40) ){ - err=" supported template tcp/udp offset is 54, no ipv6 option header is supported \n"; - return (false); + + if ( CGlobalInfo::is_learn_mode() ) { + // We change TCP ports. Need them to be in first 64 byte mbuf. + // Since we also add IP option, and rx-check feature might add another IP option, better not allow + // OP options in this mode. If needed this limitation can be refined a bit. + if ( lpd->getTcpOffset() - lpd->getIpOffset() != 20 ) { + fprintf(stderr, "Error: Bad CAP file. In learn (NAT) mode, no IP options allowed \n"); + return kIPOptionNotAllowed; } - }else{ - if ( lpd->getTcpOffset() != (14+20) ){ - err=" supported template tcp/udp offset is 34, no ipv4 option is allowed in this version \n"; - return (false); + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) { + if (lpd->getIpProto() != IPPROTO_TCP && !lpd->m_desc.IsInitSide()) { + fprintf(stderr, "Error: In the chosen learn mode, all packets from server to client in CAP file should be TCP.\n"); + fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); + return kNoTCPFromServer; + } } } } if ( CGlobalInfo::is_learn_mode() ) { - if ( GetPacket(0)->m_pkt_indication.m_desc.IsPluginEnable() ) { - err="plugins are not supported with --learn mode \n"; - return(false); + CPacketIndication &pkt_0_indication = GetPacket(0)->m_pkt_indication; + + if ( pkt_0_indication.m_desc.IsPluginEnable() ) { + fprintf(stderr, "Error: plugins are not supported with --learn mode \n"); + return kPlugInWithLearn; + } + + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) { + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + if (Size() < 3) { + fprintf(stderr + , "Error: In the chosen learn mode, need at least the 3 TCP handshake packets.\n"); + fprintf(stderr + , " Please give different CAP file, or try different --learn-mode\n"); + return kTCPLearnModeBadFlow; + } + } + CPacketIndication &pkt_1_indication = GetPacket(1)->m_pkt_indication; + + + // verify first packet is TCP SYN from client + TCPHeader *tcp = (TCPHeader *)(pkt_0_indication.getBasePtr() + pkt_0_indication.getTcpOffset()); + if ( (! pkt_0_indication.m_desc.IsInitSide()) || (! tcp->getSynFlag()) ) { + fprintf(stderr, "Error: In the chosen learn mode, first TCP packet should be SYN from client side.\n"); + fprintf(stderr, " In cap file, first packet side direction is %s. TCP header is:\n" + , pkt_0_indication.m_desc.IsInitSide() ? "outside":"inside"); + tcp->dump(stderr); + fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); + return kNoSyn; + } + + // We want at least the TCP flags to be inside first mbuf + if (pkt_0_indication.getTcpOffset() + 14 > FIRST_PKT_SIZE) { + fprintf(stderr + , "Error: In the chosen learn mode, TCP flags offset should be less than %d, but it is %d.\n" + , FIRST_PKT_SIZE, pkt_0_indication.getTcpOffset() + 14); + fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); + return kTCPOffsetTooBig; + } + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + // To support TCP seq randomization from server to client, we need second packet in flow to be the server SYN+ACK + bool error = false; + if (pkt_1_indication.getIpProto() != IPPROTO_TCP) { + error = true; + } else { + TCPHeader *tcp = (TCPHeader *)(pkt_1_indication.getBasePtr() + pkt_1_indication.getTcpOffset()); + if ( (! tcp->getSynFlag()) || (! tcp->getAckFlag()) || ( pkt_1_indication.m_desc.IsInitSide())) { + error = true; + } + } + if (error) { + fprintf(stderr, "Error: In the chosen learn mode, second packet should be SYN+ACK from server.\n"); + fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); + return kNoTCPSynAck; + } + + CPacketIndication &pkt_2_indication = GetPacket(2)->m_pkt_indication; + if ( (! pkt_2_indication.m_desc.IsInitSide()) ) { + fprintf(stderr + , "Error: Wrong third packet. In the chosen learn mode, need at least the 3 TCP handshake packets.\n"); + fprintf(stderr + , " Please give different CAP file, or try different --learn-mode\n"); + return kTCPLearnModeBadFlow; + } + 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); + fprintf(stderr, " Current delay is %f between second and first, %f between third and second" + , pkt_0_indication.m_cap_ipg, pkt_1_indication.m_cap_ipg); + fprintf(stderr + , " Please give different CAP file, try different --learn-mode, or edit ipg parameters in template file\n"); + return kTCPIpgTooLow; + } + } } } - return(true); + + return(kOK); } @@ -2085,6 +2139,13 @@ void CCapFileFlowInfo::update_info(){ if ( lp->m_pkt_indication.m_desc.IsBiDirectionalFlow() ){ lp->mask_as_learn(); } + + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + // In this mode, we need to see the SYN+ACK as well. + lp = GetPacket(1); + assert(lp); + lp->m_pkt_indication.setTTL(TTL_RESERVE_DUPLICATE); + } } if ( ft.empty() ) @@ -2124,7 +2185,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st m_total_errors=0; CFlow * first_flow=0; bool first_flow_fif_is_swap=false; - bool time_was_set=false; double last_time=0.0; CCapPktRaw raw_packet; @@ -2199,35 +2259,10 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st m_total_errors++; } } - - if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { - // in this mode, first TCP packet must be SYN from client. - if (pkt_indication.getIpProto() == IPPROTO_TCP) { - TCPHeader *tcp = (TCPHeader *)(pkt_indication.getBasePtr() + pkt_indication.getTcpOffset()); - if ( (! pkt_indication.m_desc.IsInitSide()) || (! tcp->getSynFlag()) ) { - fprintf(stderr, "Error: In the chosen learn mode, first TCP packet should be SYN from client side.\n"); - fprintf(stderr, " In cap file, first packet side direction is %s. TCP header is:\n", pkt_indication.m_desc.IsInitSide() ? "outside":"inside"); - tcp->dump(stderr); - fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); - - return kNoSyn; - } - // We want at least the TCP flags to be inside first mbuf - if (pkt_indication.getTcpOffset() + 14 > FIRST_PKT_SIZE) { - fprintf(stderr, "Error: In the chosen learn mode, first TCP packet TCP flags offset should be less than %d, but it is %d.\n" - , FIRST_PKT_SIZE, pkt_indication.getTcpOffset() + 14); - fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); - return kTCPOffsetTooBig; - } - } - } - }else{ /* no FIF */ - pkt_indication.m_desc.SetFlowId(lpflow->flow_id); if ( multi_flow_enable ==false ){ - if (lpflow == first_flow) { // add to bool init_side= @@ -2249,16 +2284,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st } } - - if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { - // This test must be down here, after initializing init side indication - if (pkt_indication.getIpProto() != IPPROTO_TCP && !pkt_indication.m_desc.IsInitSide()) { - fprintf(stderr, "Error: In the chosen learn mode, all packets from server to client in CAP file should be TCP.\n"); - fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); - return kNoTCPFromServer; - } - } - }else{ fprintf(stderr, "ERROR packet %d is not supported, should be Ethernet/IP(0x0800)/(TCP|UDP) format try to convert it using Wireshark !\n",cnt); return kPktNotSupp; @@ -2269,7 +2294,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st } } - /* set the last */ CFlowPktInfo * last_pkt =GetPacket((uint32_t)(Size()-1)); last_pkt->m_pkt_indication.m_desc.SetIsLastPkt(true); @@ -2282,9 +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; - - - 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); @@ -2449,26 +2470,6 @@ void CCapFileFlowInfo::Delete(){ RemoveAll(); } -void operator >> (const YAML::Node& node, mac_mapping_t &fi) { - utl_yaml_read_ip_addr(node,"ip", fi.ip); - const YAML::Node& mac_info = node["mac"]; - for(unsigned i=0;i<mac_info.size();i++) { - const YAML::Node & node_2 =mac_info; - uint32_t value; - node_2[i] >> value; - fi.mac.mac[i] = value; - } -} - -void operator >> (const YAML::Node& node, std::map<uint32_t, mac_addr_align_t> &mac_info) { - const YAML::Node& mac_node = node["items"]; - mac_mapping_t mac_mapping; - for (unsigned i=0;i<mac_node.size();i++) { - mac_node[i] >> mac_mapping; - mac_info[mac_mapping.ip] = mac_mapping.mac; - } -} - void operator >> (const YAML::Node& node, CFlowYamlDpPkt & fi) { uint32_t val; node["pkt_id"] >> val; @@ -3166,11 +3167,8 @@ bool CFlowGeneratorRec::Create(CFlowYamlInfo * info, int res=m_flow_info.load_cap_file(info->m_name.c_str(),_id,m_info->m_plugin_id); if ( res==0 ) { fixup_ipg_if_needed(); - std::string err; - /* verify that template are valid */ - bool is_valid=m_flow_info.is_valid_template_load_time(err); - if (!is_valid) { - printf("\n ERROR template file is not valid '%s' \n",err.c_str()); + + if (m_flow_info.is_valid_template_load_time() != 0) { return (false); } m_flow_info.update_info(); @@ -3348,7 +3346,7 @@ bool CFlowGenListPerThread::Create(uint32_t thread_id, /* split the clients to threads */ CTupleGenYamlInfo * tuple_gen = &m_flow_list->m_yaml_info.m_tuple_gen; - m_smart_gen.Create(0,m_thread_id,m_flow_list->get_is_mac_conf()); + m_smart_gen.Create(0,m_thread_id); /* split the clients to threads using the mask */ CIpPortion portion; @@ -3358,14 +3356,14 @@ bool CFlowGenListPerThread::Create(uint32_t thread_id, portion); m_smart_gen.add_client_pool(tuple_gen->m_client_pool[i].m_dist, - portion.m_ip_start, - portion.m_ip_end, - get_longest_flow(i,true), - get_total_kcps(i,true)*1000, - &m_flow_list->m_mac_info, - tuple_gen->m_client_pool[i].m_tcp_aging_sec, - tuple_gen->m_client_pool[i].m_udp_aging_sec - ); + portion.m_ip_start, + portion.m_ip_end, + get_longest_flow(i,true), + get_total_kcps(i,true)*1000, + m_flow_list->m_client_config_info, + tuple_gen->m_client_pool[i].m_tcp_aging_sec, + tuple_gen->m_client_pool[i].m_udp_aging_sec + ); } for (int i=0;i<tuple_gen->m_server_pool.size();i++) { split_ips(m_thread_id, m_max_threads, getDualPortId(), @@ -3744,11 +3742,11 @@ inline int CNodeGenerator::teardown(CFlowGenListPerThread * thread, } return (0); } - + template<int SCH_MODE> -inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, +inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, dsec_t d_time, bool always, CFlowGenListPerThread * thread, @@ -3767,9 +3765,9 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, sch_state_t state = scINIT; node = m_p_queue.top(); - n_time = node->m_time + offset; + n_time = node->m_time + offset; cur_time = now_sec(); - + while (state!=scTERMINATE) { switch (state) { @@ -3795,7 +3793,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, break; } node = m_p_queue.top(); - n_time = node->m_time + offset; + n_time = node->m_time + offset; if ((n_time-cur_time)>EAT_WINDOW_DTIME) { state=scINIT; @@ -3805,7 +3803,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, break; case scWAIT: - do_sleep(cur_time,thread,n_time); // estimate loop + do_sleep(cur_time,thread,n_time); // estimate loop state=scWORK; break; default: @@ -3816,7 +3814,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, return (teardown(thread,always,old_offset,offset)); } -FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time, +FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time, dsec_t d_time, bool always, CFlowGenListPerThread * thread, @@ -3843,7 +3841,7 @@ FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time, return (teardown(thread,always,old_offset,0)); } -int CNodeGenerator::flush_file(dsec_t max_time, +int CNodeGenerator::flush_file(dsec_t max_time, dsec_t d_time, bool always, CFlowGenListPerThread * thread, @@ -3864,7 +3862,7 @@ int CNodeGenerator::flush_file(dsec_t max_time, void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thread) { - /*repeat and NAT is not supported */ + /*repeat and NAT is not supported together */ if ( node->is_nat_first_state() ) { node->set_nat_wait_state(); flush_one_node_to_file(node); @@ -3875,7 +3873,7 @@ void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thre if ( node->is_nat_wait_state() ) { if (node->is_responder_pkt()) { m_p_queue.pop(); - /* time out, need to free the flow and remove the association , we didn't get convertion yet*/ + /* time out, need to free the flow and remove the association , we didn't get conversion yet*/ thread->terminate_nat_flows(node); return; @@ -3886,7 +3884,22 @@ void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thre #endif } } else { - assert(0); + if ( node->is_nat_wait_ack_state() ) { + if (node->is_initiator_pkt()) { + m_p_queue.pop(); + /* time out, need to free the flow and remove the association , we didn't get conversion yet*/ + thread->terminate_nat_flows(node); + return; + + } else { + flush_one_node_to_file(node); +#ifdef _DEBUG + update_stats(node); +#endif + } + } else { + assert(0); + } } } m_p_queue.pop(); @@ -4166,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); } @@ -4190,38 +4208,74 @@ void CFlowGenListPerThread::handle_latency_pkt_msg(CGenNodeLatencyPktInfo * msg) m_node_gen.m_v_if->send_one_pkt((pkt_dir_t)msg->m_dir,msg->m_pkt); } - void CFlowGenListPerThread::handle_nat_msg(CGenNodeNatInfo * msg){ int i; + bool first = true, second = true; + for (i=0; i<msg->m_cnt; i++) { + first = true; + second = true; CNatFlowInfo * nat_msg=&msg->m_data[i]; CGenNode * node=m_flow_id_to_node_lookup.lookup(nat_msg->m_fid); if (!node) { - /* this should be move to a notification module */ - #ifdef NAT_TRACE_ + /* this should be moved to a notification module */ +#ifdef NAT_TRACE_ printf(" ERORR not valid flow_id %d probably flow was aged \n",nat_msg->m_fid); - #endif +#endif m_stats.m_nat_lookup_no_flow_id++; continue; } - #ifdef NAT_TRACE_ - printf(" %.03f RX :set node %p:%x %x:%x:%x \n",now_sec() ,node,nat_msg->m_fid,nat_msg->m_external_ip,nat_msg->m_external_ip_server,nat_msg->m_external_port); - #endif - node->set_nat_ipv4_addr(nat_msg->m_external_ip); - node->set_nat_ipv4_port(nat_msg->m_external_port); - node->set_nat_ipv4_addr_server(nat_msg->m_external_ip_server); - - assert(node->is_nat_wait_state()); - if ( CGlobalInfo::is_learn_verify_mode() ){ - if (!node->is_external_is_eq_to_internal_ip() ){ - m_stats.m_nat_flow_learn_error++; + + // Calculate diff between tcp seq of SYN packet, and TCP ack of SYN+ACK packet + // For supporting firewalls who do TCP seq num randomization + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) { + if (node->is_nat_wait_state()) { + char *syn_pkt = node->m_flow_info->GetPacket(0)->m_packet->raw; + TCPHeader *tcp = (TCPHeader *)(syn_pkt + node->m_pkt_info->m_pkt_indication.getFastTcpOffset()); + 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(); + } + } else { + char *syn_ack_pkt = node->m_flow_info->GetPacket(1)->m_packet->raw; + TCPHeader *tcp = (TCPHeader *)(syn_ack_pkt + node->m_pkt_info->m_pkt_indication.getFastTcpOffset()); + node->set_nat_tcp_seq_diff_server(nat_msg->m_tcp_seq - tcp->getSeqNumber()); + assert(node->is_nat_wait_ack_state()); + node->set_nat_learn_state(); + first = false; + } + } else { + assert(node->is_nat_wait_state()); + node->set_nat_learn_state(); + } + + if (first) { +#ifdef NAT_TRACE_ + printf(" %.03f RX :set node %p:%x %x:%x TCP diff %x\n" + , now_sec(), node,nat_msg->m_fid, nat_msg->m_external_ip, nat_msg->m_external_port + , node->get_nat_tcp_seq_diff_client()); +#endif + + node->set_nat_ipv4_addr(nat_msg->m_external_ip); + 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() || + node->get_nat_tcp_seq_diff_client() != 0) { + m_stats.m_nat_flow_learn_error++; + } } } - node->set_nat_learn_state(); - /* remove from the hash */ - m_flow_id_to_node_lookup.remove_no_lookup(nat_msg->m_fid); - m_stats.m_nat_lookup_remove_flow_id++; + if (second) { + /* remove from the hash */ + m_flow_id_to_node_lookup.remove_no_lookup(nat_msg->m_fid); + m_stats.m_nat_lookup_remove_flow_id++; + } } } @@ -4317,7 +4371,7 @@ void CFlowGenListPerThread::start_generate_stateful(std::string erf_file_name, fprintf(stderr," nothing to generate no template loaded \n"); return; } - + m_preview_mode = preview; m_node_gen.open_file(erf_file_name,&m_preview_mode); dsec_t d_time_flow=get_delta_flow_is_sec(); @@ -4419,32 +4473,11 @@ void CFlowGenList::clean_p_thread_info(void){ m_threads_info.clear(); } - - -int CFlowGenList::load_from_mac_file(std::string file_name) { - if ( !utl_is_file_exists (file_name) ){ - printf(" ERROR no mac_file is set, file %s does not exist \n",file_name.c_str()); - exit(-1); - } - m_mac_info.set_configured(true); - - try { - std::ifstream fin((char *)file_name.c_str()); - YAML::Parser parser(fin); - YAML::Node doc; - - parser.GetNextDocument(doc); - doc[0] >> m_mac_info.get_mac_info(); - } catch ( const std::exception& e ) { - std::cout << e.what() << "\n"; - m_mac_info.clear(); - exit(-1); - } - - return (0); +int CFlowGenList::load_client_config_file(std::string file_name) { + m_client_config_info.load_yaml_file(file_name); + return (0); } - int CFlowGenList::load_from_yaml(std::string file_name, uint32_t num_threads){ uint8_t idx; @@ -4524,6 +4557,16 @@ double CFlowGenList::GetCpuUtil(){ return (c/m_threads_info.size()); } +double CFlowGenList::GetCpuUtilRaw(){ + int i; + double c=0.0; + for (i=0; i<(int)m_threads_info.size(); i++) { + CFlowGenListPerThread * lp=m_threads_info[i]; + c+=lp->m_cpu_cp_u.GetValRaw(); + } + return (c/m_threads_info.size()); +} + void CFlowGenList::UpdateFast(){ @@ -4755,21 +4798,20 @@ bool CParserOption::is_valid_opt_val(int val, int min, int max, const std::strin void CParserOption::dump(FILE *fd){ preview.Dump(fd); - fprintf(fd," cfg file : %s \n",cfg_file.c_str()); - fprintf(fd," mac file : %s \n",mac_file.c_str()); - fprintf(fd," out file : %s \n",out_file.c_str()); - fprintf(fd," duration : %.0f \n",m_duration); - fprintf(fd," factor : %.0f \n",m_factor); - fprintf(fd," mbuf_factor : %.0f \n",m_mbuf_factor); - fprintf(fd," latency : %d pkt/sec \n",m_latency_rate); - fprintf(fd," zmq_port : %d \n",m_zmq_port); - fprintf(fd," telnet_port : %d \n",m_telnet_port); - fprintf(fd," expected_ports : %d \n",m_expected_portd); + fprintf(fd," cfg file : %s \n",cfg_file.c_str()); + fprintf(fd," mac file : %s \n",client_cfg_file.c_str()); + fprintf(fd," out file : %s \n",out_file.c_str()); + fprintf(fd," client cfg file : %s \n",out_file.c_str()); + fprintf(fd," duration : %.0f \n",m_duration); + fprintf(fd," factor : %.0f \n",m_factor); + fprintf(fd," mbuf_factor : %.0f \n",m_mbuf_factor); + fprintf(fd," latency : %d pkt/sec \n",m_latency_rate); + fprintf(fd," zmq_port : %d \n",m_zmq_port); + fprintf(fd," telnet_port : %d \n",m_telnet_port); + fprintf(fd," expected_ports : %d \n",m_expected_portd); if (preview.get_vlan_mode_enable() ) { - fprintf(fd," vlans : [%d,%d] \n",m_vlan_port[0],m_vlan_port[1]); + fprintf(fd," vlans : [%d,%d] \n",m_vlan_port[0],m_vlan_port[1]); } - fprintf(fd," mac spreading: %d \n",(int)m_mac_splitter); - int i; for (i = 0; i < TREX_MAX_PORTS; i++) { @@ -4782,6 +4824,15 @@ void CParserOption::dump(FILE *fd){ } } +void CParserOption::verify() { + /* check for mutual exclusion options */ + if (preview.get_is_client_cfg_enable()) { + if (preview.get_vlan_mode_enable() || preview.get_mac_ip_overide_enable()) { + throw std::runtime_error("VLAN / MAC override cannot be combined with client configuration"); + } + } +} + #if 0 void CTupleGlobalGenerator::Dump(FILE *fd){ @@ -5068,37 +5119,68 @@ int CErfIFStl::send_node(CGenNode * _no_to_use){ return (0); } +void CErfIF::add_vlan(uint16_t vlan_id) { + uint8_t *buffer =(uint8_t *)m_raw->raw; + uint16_t vlan_protocol = EthernetHeader::Protocol::VLAN; + uint32_t vlan_tag = (vlan_protocol << 16) | vlan_id; + vlan_tag = PKT_HTONL(vlan_tag); -int CErfIF::send_node(CGenNode * node){ + /* insert vlan tag and adjust packet size */ + memcpy(cbuff+4, buffer + 12, m_raw->pkt_len - 12); + memcpy(cbuff, &vlan_tag, 4); + memcpy(buffer + 12, cbuff, m_raw->pkt_len - 8); - if ( m_preview_mode->getFileWrite() ){ + m_raw->pkt_len += 4; +} - CFlowPktInfo * lp=node->m_pkt_info; - rte_mbuf_t * m=lp->generate_new_mbuf(node); - pkt_dir_t dir=node->cur_interface_dir(); +void CErfIF::apply_client_config(const ClientCfg *cfg, pkt_dir_t dir) { + assert(cfg); + uint8_t *p = (uint8_t *)m_raw->raw; - fill_raw_packet(m,node,dir); + const ClientCfgDir &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder); + + /* dst mac */ + if (cfg_dir.has_dst_mac_addr()) { + memcpy(p, cfg_dir.get_dst_mac_addr(), 6); + } + + /* src mac */ + if (cfg_dir.has_src_mac_addr()) { + memcpy(p + 6, cfg_dir.get_src_mac_addr(), 6); + } + + /* VLAN */ + if (cfg_dir.has_vlan()) { + add_vlan(cfg_dir.get_vlan()); + } +} + +int CErfIF::send_node(CGenNode *node){ + + if (!m_preview_mode->getFileWrite()) { + return (0); + } + + CFlowPktInfo *lp = node->m_pkt_info; + rte_mbuf_t *m = lp->generate_new_mbuf(node); + pkt_dir_t dir = node->cur_interface_dir(); + + fill_raw_packet(m, node, dir); /* update mac addr dest/src 12 bytes */ uint8_t *p=(uint8_t *)m_raw->raw; int p_id=(int)dir; memcpy(p,CGlobalInfo::m_options.get_dst_src_mac_addr(p_id),12); - /* If vlan is enabled, add vlan header */ - if ( unlikely( CGlobalInfo::m_options.preview.get_vlan_mode_enable() ) ){ - /* retrieve vlan ID and form vlan tag */ - uint8_t vlan_port = (node->m_src_ip &1); - uint16_t vlan_protocol = EthernetHeader::Protocol::VLAN; - uint16_t vlan_id = CGlobalInfo::m_options.m_vlan_port[vlan_port]; - uint32_t vlan_tag = (vlan_protocol << 16) | vlan_id; - vlan_tag = PKT_HTONL(vlan_tag); + /* if a client configuration was provided - apply the config */ + if (CGlobalInfo::m_options.preview.get_is_client_cfg_enable()) { + apply_client_config(node->m_client_cfg, dir); - /* insert vlan tag and adjust packet size */ - memcpy(cbuff+4, p+12, m_raw->pkt_len-12); - memcpy(cbuff, &vlan_tag, 4); - memcpy(p+12, cbuff, m_raw->pkt_len-8); - m_raw->pkt_len += 4; + } else if (CGlobalInfo::m_options.preview.get_vlan_mode_enable()) { + uint8_t vlan_port = (node->m_src_ip & 1); + uint16_t vlan_id = CGlobalInfo::m_options.m_vlan_port[vlan_port]; + add_vlan(vlan_id); } //utl_DumpBuffer(stdout,p, 12,0); @@ -5107,8 +5189,8 @@ int CErfIF::send_node(CGenNode * node){ BP_ASSERT(rc == 0); rte_pktmbuf_free(m); - } - return (0); + + return (0); } int CErfIF::flush_tx_queue(void){ @@ -6438,7 +6520,7 @@ void CGenNodeBase::free_base(){ CGenNodePCAP *p = (CGenNodePCAP *)this; p->destroy(); return; - } + } if ( m_type == COMMAND ) { CGenNodeCommand* p=(CGenNodeCommand*)this; @@ -6446,4 +6528,3 @@ void CGenNodeBase::free_base(){ } } - diff --git a/src/bp_sim.h b/src/bp_sim.h index 18db61ca..b4ef54d1 100755 --- a/src/bp_sim.h +++ b/src/bp_sim.h @@ -59,6 +59,7 @@ limitations under the License. #include "platform_cfg.h" #include "flow_stat.h" #include "trex_watchdog.h" +#include "trex_client_config.h" #include <trex_stateless_dp_core.h> @@ -68,8 +69,6 @@ limitations under the License. class CGenNodePCAP; -#undef NAT_TRACE_ - #define FORCE_NO_INLINE __attribute__ ((noinline)) #define FORCE_INLINE __attribute__((always_inline)) @@ -87,10 +86,6 @@ typedef struct { */ #define INET_PORTSTRLEN 5 - - - - /* VM commands */ class CMiniVMCmdBase { @@ -186,6 +181,12 @@ inline int ip_to_str(uint32_t ip,char * str){ return(strlen(str)); } +inline std::string ip_to_str(uint32_t ip) { + char tmp[INET_ADDRSTRLEN]; + ip_to_str(ip, tmp); + return tmp; +} + // Routine to create IPv6 address string inline int ipv6_to_str(ipaddr_t *ip,char * str){ int idx=0; @@ -627,7 +628,7 @@ public: void set_mac_ip_overide_enable(bool enable){ btSetMaskBit32(m_flags,30,30,enable?1:0); if (enable) { - set_mac_ip_features_enable(enable); + set_slowpath_features_on(enable); } } @@ -639,26 +640,27 @@ public: btSetMaskBit32(m_flags,31,31,enable?1:0); } - - bool get_mac_ip_features_enable(){ - return (btGetMaskBit32(m_flags1,0,0) ? true:false); + bool get_is_slowpath_features_on() { + return (btGetMaskBit32(m_flags1, 0, 0) ? true : false); } - void set_mac_ip_features_enable(bool enable){ - btSetMaskBit32(m_flags1,0,0,enable?1:0); + void set_slowpath_features_on(bool enable) { + btSetMaskBit32(m_flags1, 0, 0, enable ? 1 : 0); } - bool get_mac_ip_mapping_enable(){ - return (btGetMaskBit32(m_flags1,1,1) ? true:false); + bool get_is_client_cfg_enable() { + return (btGetMaskBit32(m_flags1, 1, 1) ? true : false); } - void set_mac_ip_mapping_enable(bool enable){ - btSetMaskBit32(m_flags1,1,1,enable?1:0); + void set_client_cfg_enable(bool enable){ + btSetMaskBit32(m_flags1, 1, 1, enable ? 1 : 0); if (enable) { - set_mac_ip_features_enable(enable); + set_slowpath_features_on(enable); } } + + bool get_vm_one_queue_enable(){ return (btGetMaskBit32(m_flags1,2,2) ? true:false); } @@ -684,16 +686,6 @@ public: return (btGetMaskBit32(m_flags1,3,3) ? true:false); } - - /* split mac is enabled */ - void setDestMacSplit(bool enable){ - btSetMaskBit32(m_flags1,4,4,enable?1:0); - } - - bool getDestMacSplit(){ - return (btGetMaskBit32(m_flags1,4,4) ? true:false); - } - /* split mac is enabled */ void setWDDisable(bool wd_disable){ btSetMaskBit32(m_flags1,6,6,wd_disable?1:0); @@ -771,7 +763,10 @@ public: LEARN_MODE_DISABLED=0, LEARN_MODE_TCP_ACK=1, LEARN_MODE_IP_OPTION=2, - LEARN_MODE_MAX=LEARN_MODE_IP_OPTION + LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND=3, + LEARN_MODE_MAX=LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND, + // This is used to check if 1 or 3 exist + LEARN_MODE_TCP=100 }; public: @@ -782,6 +777,7 @@ public: m_latency_rate =0; m_latency_mask =0xffffffff; m_latency_prev=0; + m_wait_before_traffic=1; m_zmq_port=4500; m_telnet_port =4501; m_platform_factor=1.0; @@ -793,7 +789,6 @@ public: m_io_mode=1; m_run_flags=0; prefix=""; - m_mac_splitter=0; m_run_mode = RUN_MODE_INVALID; m_l_pkt_mode = 0; m_rx_thread_enabled = false; @@ -812,6 +807,7 @@ public: uint32_t m_latency_rate; /* pkt/sec for each thread/port zero disable */ uint32_t m_latency_mask; uint32_t m_latency_prev; + uint32_t m_wait_before_traffic; uint16_t m_rx_check_sample; /* the sample rate of flows */ uint16_t m_rx_check_hops; uint16_t m_zmq_port; @@ -819,7 +815,6 @@ public: uint16_t m_expected_portd; uint16_t m_io_mode; //0,1,2 0 disable, 1- normal , 2 - short uint16_t m_run_flags; - uint8_t m_mac_splitter; uint8_t m_l_pkt_mode; uint8_t m_learn_mode; uint16_t m_debug_pkt_proto; @@ -829,7 +824,7 @@ public: std::string cfg_file; - std::string mac_file; + std::string client_cfg_file; std::string platform_cfg_file; std::string out_file; @@ -887,6 +882,8 @@ public: } void dump(FILE *fd); bool is_valid_opt_val(int val, int min, int max, const std::string &opt_name); + + void verify(); }; @@ -1196,13 +1193,10 @@ public: void dump_in_case_of_error(FILE *fd); - std::string dump_as_json(uint8_t id,bool last); + void dump_as_json(Json::Value &json); private: - std::string add_to_json(std::string name, - rte_mempool_t * pool, - bool last=false); - + void add_to_json(Json::Value &json, std::string name, rte_mempool_t * pool); public: rte_mempool_t * m_small_mbuf_pool; /* pool for start packets */ @@ -1261,7 +1255,11 @@ public: } static inline bool is_learn_mode(CParserOption::trex_learn_mode_e mode){ - return ( (m_options.m_learn_mode == mode)); + if (mode == CParserOption::LEARN_MODE_TCP) { + return ((m_options.m_learn_mode == CParserOption::LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND) + || (m_options.m_learn_mode == CParserOption::LEARN_MODE_TCP_ACK)); + } else + return (m_options.m_learn_mode == mode); } static inline bool is_ipv6_enable(void){ @@ -1296,7 +1294,8 @@ public: } - static std::string dump_pool_as_json(void); + static void dump_pool_as_json(Json::Value &json); + static std::string dump_pool_as_json_str(void); public: @@ -1572,12 +1571,12 @@ public: CTupleGeneratorSmart *m_tuple_gen; // cache line 1 - 64bytes waste of space ! - uint32_t m_nat_external_ipv4; /* client */ - uint32_t m_nat_external_ipv4_server; - uint16_t m_nat_external_port; - - uint16_t m_nat_pad[3]; - mac_addr_align_t m_src_mac; + uint32_t m_nat_external_ipv4; // NAT client IP + uint32_t m_nat_tcp_seq_diff_client; // support for firewalls that do TCP seq num randomization + uint32_t m_nat_tcp_seq_diff_server; // And some do seq num randomization for server->client also + uint16_t m_nat_external_port; // NAT client port + uint16_t m_nat_pad[1]; + const ClientCfg *m_client_cfg; uint32_t m_src_idx; uint32_t m_dest_idx; uint32_t m_end_of_cache_line[6]; @@ -1713,6 +1712,15 @@ public: return (btGetMaskBit16(m_flags,4,3)==2?true:false) ; } + // We saw first TCP SYN. Waiting for SYN+ACK + inline void set_nat_wait_ack_state() { + btSetMaskBit16(m_flags, 4, 3, 3); + } + + inline bool is_nat_wait_ack_state(){ + return (btGetMaskBit16(m_flags,4,3) == 3) ? true : false; + } + inline void set_nat_learn_state(){ m_type=FLOW_PKT; /* normal operation .. repeat might work too */ } @@ -1726,14 +1734,21 @@ public: return (m_thread_id); } - inline void set_nat_ipv4_addr_server(uint32_t ip){ - m_nat_external_ipv4_server =ip; + inline void set_nat_tcp_seq_diff_client(uint32_t diff) { + m_nat_tcp_seq_diff_client = diff; + } + + inline uint32_t get_nat_tcp_seq_diff_client() { + return m_nat_tcp_seq_diff_client; } - inline uint32_t get_nat_ipv4_addr_server(){ - return ( m_nat_external_ipv4_server ); + inline void set_nat_tcp_seq_diff_server(uint32_t diff) { + m_nat_tcp_seq_diff_server = diff; } + inline uint32_t get_nat_tcp_seq_diff_server() { + return m_nat_tcp_seq_diff_server; + } inline void set_nat_ipv4_addr(uint32_t ip){ m_nat_external_ipv4 =ip; @@ -1754,8 +1769,7 @@ public: bool is_external_is_eq_to_internal_ip(){ /* this API is used to check TRex itself */ if ( (get_nat_ipv4_addr() == m_src_ip ) && - (get_nat_ipv4_port()==m_src_port) && - ( get_nat_ipv4_addr_server() == m_dest_ip) ) { + (get_nat_ipv4_port()==m_src_port)) { return (true); }else{ return (false); @@ -1869,8 +1883,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: @@ -1923,7 +1939,8 @@ public: protected: - + void add_vlan(uint16_t vlan_id); + void apply_client_config(const ClientCfg *cfg, pkt_dir_t dir); virtual void fill_raw_packet(rte_mbuf_t * m,CGenNode * node,pkt_dir_t dir); CFileWriterBase * m_writer; @@ -3017,6 +3034,8 @@ inline void CFlowPktInfo::update_pkt_info(char *p, (void)et; uint16_t src_port = node->m_src_port; + uint32_t tcp_seq_diff_client = 0; + uint32_t tcp_seq_diff_server = 0; pkt_dir_t ip_dir = node->cur_pkt_ip_addr_dir(); pkt_dir_t port_dir = node->cur_pkt_port_addr_dir(); @@ -3037,7 +3056,6 @@ inline void CFlowPktInfo::update_pkt_info(char *p, }else{ if ( unlikely ( CGlobalInfo::is_learn_mode() ) ){ - if (m_pkt_indication.m_desc.IsLearn()) { /* might be done twice */ #ifdef NAT_TRACE_ @@ -3046,42 +3064,48 @@ inline void CFlowPktInfo::update_pkt_info(char *p, ipv4->setTimeToLive(TTL_RESERVE_DUPLICATE); /* first ipv4 option add the info in case of learn packet, usualy only the first packet */ - if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_IP_OPTION)) { - CNatOption *lpNat =(CNatOption *)ipv4->getOption(); - lpNat->set_fid(node->get_short_fid()); - lpNat->set_thread_id(node->get_thread_id()); - } else { - // This method only work on first TCP SYN - if (ipv4->getProtocol() == IPPROTO_TCP) { - TCPHeader *tcp = (TCPHeader *)(((uint8_t *)ipv4) + ipv4->getHeaderLength()); - if (tcp->getSynFlag()) { - tcp->setAckNumber(CNatRxManager::calc_tcp_ack_val(node->get_short_fid(), node->get_thread_id())); - } + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_IP_OPTION)) { + CNatOption *lpNat =(CNatOption *)ipv4->getOption(); + lpNat->set_fid(node->get_short_fid()); + lpNat->set_thread_id(node->get_thread_id()); + } else { + // This method only work on first TCP SYN + if (ipv4->getProtocol() == IPPROTO_TCP) { + TCPHeader *tcp = (TCPHeader *)(((uint8_t *)ipv4) + ipv4->getHeaderLength()); + if (tcp->getSynFlag()) { + tcp->setAckNumber(CNatRxManager::calc_tcp_ack_val(node->get_short_fid(), node->get_thread_id())); + } #ifdef NAT_TRACE_ - printf(" %.3f : flow_id: %x thread_id %x TCP ack %x\n",now_sec(), node->get_short_fid(), node->get_thread_id(), tcp->getAckNumber()); + printf(" %.3f : flow_id: %x thread_id %x TCP ack %x seq %x\n" + ,now_sec(), node->get_short_fid(), node->get_thread_id(), tcp->getAckNumber() + , tcp->getSeqNumber()); #endif - } - } + } + } } /* in all cases update the ip using the outside ip */ if ( m_pkt_indication.m_desc.IsInitSide() ) { #ifdef NAT_TRACE_ if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) { - printf(" %.3f : DP : i %x:%x -> %x flow_id: %lx\n",now_sec(),node->m_src_ip,node->m_src_port,node->m_dest_ip,node->m_flow_id); + printf(" %.3f : DP : i %x:%x -> %x flow_id: %lx\n",now_sec(), node->m_src_ip + , node->m_src_port, node->m_dest_ip, node->m_flow_id); } #endif + tcp_seq_diff_server = node->get_nat_tcp_seq_diff_server(); ipv4->updateIpSrc(node->m_src_ip); ipv4->updateIpDst(node->m_dest_ip); - }else{ + } else { #ifdef NAT_TRACE_ if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) { - printf(" %.3f : r %x -> %x:%x flow_id: %lx \n",now_sec(),node->m_dest_ip,node->m_src_ip,node->m_src_port,node->m_flow_id); + printf(" %.3f : r %x -> %x:%x flow_id: %lx \n", now_sec(), node->m_dest_ip + , node->m_src_ip, node->m_src_port, node->m_flow_id); } #endif src_port = node->get_nat_ipv4_port(); - ipv4->updateIpSrc(node->get_nat_ipv4_addr_server()); + tcp_seq_diff_client = node->get_nat_tcp_seq_diff_client(); + ipv4->updateIpSrc(node->m_dest_ip); ipv4->updateIpDst(node->get_nat_ipv4_addr()); } @@ -3089,7 +3113,7 @@ inline void CFlowPktInfo::update_pkt_info(char *p, #ifdef NAT_TRACE_ if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) { if ( m_pkt_indication.m_desc.IsInitSide() ==false ){ - printf(" %.3f : pkt ==> %x:%x %x:%x \n",now_sec(),node->get_nat_ipv4_addr(),node->get_nat_ipv4_addr_server(), + printf(" %.3f : pkt ==> %x %x:%x \n",now_sec(),node->get_nat_ipv4_addr(), node->get_nat_ipv4_port(),node->m_src_port); }else{ printf(" %.3f : pkt ==> init pkt sent \n",now_sec()); @@ -3137,8 +3161,10 @@ inline void CFlowPktInfo::update_pkt_info(char *p, /* replace port */ if ( port_dir == CLIENT_SIDE ) { m_tcp->setSourcePort(src_port); + m_tcp->setAckNumber(m_tcp->getAckNumber() + tcp_seq_diff_server); }else{ m_tcp->setDestPort(src_port); + m_tcp->setAckNumber(m_tcp->getAckNumber() + tcp_seq_diff_client); } #ifdef RTE_DPDK @@ -3409,6 +3435,8 @@ public: class CCapFileFlowInfo { public: + const int LEARN_MODE_MIN_IPG = 10; // msec + enum load_cap_file_err { kOK = 0, kFileNotExist, @@ -3416,9 +3444,14 @@ public: kNoSyn, kTCPOffsetTooBig, kNoTCPFromServer, + kNoTCPSynAck, + kTCPLearnModeBadFlow, kPktNotSupp, kPktProcessFail, - kCapFileErr + kCapFileErr, + kPlugInWithLearn, + kIPOptionNotAllowed, + kTCPIpgTooLow }; bool Create(); @@ -3435,7 +3468,7 @@ public: /* update flow info */ void update_info(); - bool is_valid_template_load_time(std::string & err); + enum CCapFileFlowInfo::load_cap_file_err is_valid_template_load_time(); void save_to_erf(std::string cap_file_name,int pcap); @@ -3930,13 +3963,15 @@ public: public: int load_from_yaml(std::string csv_file,uint32_t num_threads); - int load_from_mac_file(std::string csv_file); + int load_client_config_file(std::string file_name); + public: void Dump(FILE *fd); void DumpCsv(FILE *fd); void DumpPktSize(); void UpdateFast(); double GetCpuUtil(); + double GetCpuUtilRaw(); public: double get_total_kcps(); @@ -3944,12 +3979,12 @@ public: double get_total_tx_bps(); uint32_t get_total_repeat_flows(); double get_delta_flow_is_sec(); - bool get_is_mac_conf() { return m_mac_info.is_configured();} + public: - std::vector<CFlowGeneratorRec *> m_cap_gen; /* global info */ - CFlowsYamlInfo m_yaml_info; /* global yaml*/ - std::vector<CFlowGenListPerThread *> m_threads_info; - CFlowGenListMac m_mac_info; + std::vector<CFlowGeneratorRec *> m_cap_gen; /* global info */ + CFlowsYamlInfo m_yaml_info; /* global yaml*/ + std::vector<CFlowGenListPerThread *> m_threads_info; + ClientCfgDB m_client_config_info; }; @@ -3989,9 +4024,8 @@ inline void CCapFileFlowInfo::generate_flow(CTupleTemplateGeneratorSmart * tup node->m_src_idx = tuple.getClientId(); node->m_dest_idx = tuple.getServerId(); node->m_src_port = tuple.getClientPort(); - memcpy(&node->m_src_mac, - tuple.getClientMac(), - sizeof(mac_addr_align_t)); + node->m_client_cfg = tuple.getClientCfg(); + node->m_plugin_info =(void *)0; if ( unlikely( CGlobalInfo::is_learn_mode() ) ){ diff --git a/src/common/Network/Packet/TcpHeader.h b/src/common/Network/Packet/TcpHeader.h index c19cd262..97575a60 100755 --- a/src/common/Network/Packet/TcpHeader.h +++ b/src/common/Network/Packet/TcpHeader.h @@ -23,6 +23,11 @@ class TCPHeader { public: + enum TCPHeader_enum_t + { + TCP_INVALID_PORT = 0 + }; + TCPHeader(){} TCPHeader(uint16_t argSourcePort, diff --git a/src/flow_stat.cpp b/src/flow_stat.cpp index 5503434f..8c2f2566 100644 --- a/src/flow_stat.cpp +++ b/src/flow_stat.cpp @@ -796,6 +796,9 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) { if (m_num_started_streams == 0) { send_start_stop_msg_to_rx(true); // First transmitting stream. Rx core should start reading packets; + //also good time to zero global counters + memset(m_rx_cant_count_err, 0, sizeof(m_rx_cant_count_err)); + memset(m_tx_cant_count_err, 0, sizeof(m_tx_cant_count_err)); // wait to make sure that message is acknowledged. RX core might be in deep sleep mode, and we want to // start transmitting packets only after it is working, otherwise, packets will get lost. @@ -910,7 +913,7 @@ int CFlowStatRuleMgr::stop_stream(TrexStream * stream) { m_num_started_streams--; assert (m_num_started_streams >= 0); if (m_num_started_streams == 0) { - send_start_stop_msg_to_rx(false); // No more transmittig streams. Rx core shoulde get into idle loop. + send_start_stop_msg_to_rx(false); // No more transmittig streams. Rx core should get into idle loop. } return 0; } @@ -947,6 +950,7 @@ bool CFlowStatRuleMgr::dump_json(std::string & s_json, std::string & l_json, boo tx_per_flow_t tx_stats[MAX_FLOW_STATS]; tx_per_flow_t tx_stats_payload[MAX_FLOW_STATS_PAYLOAD]; rfc2544_info_t rfc2544_info[MAX_FLOW_STATS_PAYLOAD]; + CRxCoreErrCntrs rx_err_cntrs; Json::FastWriter writer; Json::Value s_root; Json::Value l_root; @@ -973,6 +977,7 @@ bool CFlowStatRuleMgr::dump_json(std::string & s_json, std::string & l_json, boo } m_api->get_rfc2544_info(rfc2544_info, 0, m_max_hw_id_payload, false); + m_api->get_rx_err_cntrs(&rx_err_cntrs); // read hw counters, and update for (uint8_t port = 0; port < m_num_ports; port++) { @@ -1046,10 +1051,21 @@ bool CFlowStatRuleMgr::dump_json(std::string & s_json, std::string & l_json, boo // general per port data for (uint8_t port = 0; port < m_num_ports; port++) { std::string str_port = static_cast<std::ostringstream*>( &(std::ostringstream() << int(port) ) )->str(); - if (m_rx_cant_count_err[port] != 0) - s_data_section["port_data"][str_port]["rx_err"] = m_rx_cant_count_err[port]; - if (m_tx_cant_count_err[port] != 0) - s_data_section["port_data"][str_port]["tx_err"] = m_tx_cant_count_err[port]; + if ((m_rx_cant_count_err[port] != 0) || baseline) + s_data_section["global"]["rx_err"][str_port] = m_rx_cant_count_err[port]; + if ((m_tx_cant_count_err[port] != 0) || baseline) + s_data_section["global"]["tx_err"][str_port] = m_tx_cant_count_err[port]; + } + + // payload rules rx errors + uint64_t tmp_cnt; + tmp_cnt = rx_err_cntrs.get_bad_header(); + if (tmp_cnt || baseline) { + l_data_section["global"]["bad_hdr"] = Json::Value::UInt64(tmp_cnt); + } + tmp_cnt = rx_err_cntrs.get_old_flow(); + if (tmp_cnt || baseline) { + l_data_section["global"]["old_flow"] = Json::Value::UInt64(tmp_cnt); } flow_stat_user_id_map_it_t it; 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/gtest/tuple_gen_test.cpp b/src/gtest/tuple_gen_test.cpp index f3b9fa1e..fa760c6d 100755 --- a/src/gtest/tuple_gen_test.cpp +++ b/src/gtest/tuple_gen_test.cpp @@ -25,6 +25,22 @@ limitations under the License. /* TEST case for CClientInfo*/ +class CClientInfo : public CSimpleClientInfo<CIpInfo> { +public: + CClientInfo() : CSimpleClientInfo<CIpInfo>(0) { + + } +}; + +class CClientInfoL : public CSimpleClientInfo<CIpInfoL> { +public: + CClientInfoL() : CSimpleClientInfo<CIpInfoL>(0) { + + } +}; + +static ClientCfgDB g_dummy; + class CClientInfoUT { public: CClientInfoUT(CClientInfo *a) { @@ -157,7 +173,7 @@ TEST(CClientInfoLTest, get_new_free_port) { TEST(tuple_gen,clientPoolL) { CClientPool gen; gen.Create(cdSEQ_DIST, - 0x10000001, 0x10000f01, 64000,1,NULL,false, + 0x10000001, 0x10000f01, 64000,1, g_dummy, 0,0); CTupleBase result; uint32_t result_src; @@ -181,7 +197,7 @@ TEST(tuple_gen,clientPoolL) { TEST(tuple_gen,clientPool) { CClientPool gen; gen.Create(cdSEQ_DIST, - 0x10000001, 0x10000021, 64000,1000,NULL,false, + 0x10000001, 0x10000021, 64000,1000, g_dummy, 0,0); CTupleBase result; uint32_t result_src; @@ -273,7 +289,7 @@ TEST(tuple_gen,GenerateTuple2) { CClientPool c_gen; CClientPool c_gen_2; c_gen.Create(cdSEQ_DIST, - 0x10000001, 0x1000000f, 64000,4,NULL,false, + 0x10000001, 0x1000000f, 64000,4, g_dummy, 0,0); CServerPool s_gen; CServerPool s_gen_2; @@ -302,7 +318,7 @@ TEST(tuple_gen,GenerateTuple2) { c_gen.Delete(); // EXPECT_EQ((size_t)0, gen.m_clients.size()); c_gen.Create(cdSEQ_DIST, - 0x10000001, 0x1000000f, 64000,400,NULL,false, + 0x10000001, 0x1000000f, 64000,400, g_dummy, 0,0); s_gen.Create(cdSEQ_DIST, 0x30000001, 0x30000001, 64000,10); @@ -325,39 +341,6 @@ TEST(tuple_gen,GenerateTuple2) { } -TEST(tuple_gen,GenerateTupleMac) { - CFlowGenList fl; - fl.Create(); - fl.load_from_mac_file("avl/mac_uit.yaml"); - - CClientPool gen; - gen.Create(cdSEQ_DIST, - 0x10000001, 0x1000000f, 64000,2, &fl.m_mac_info,true,0,0); - - CTupleBase result; - uint32_t result_src; - uint16_t result_port; - mac_addr_align_t* result_mac; - for(int i=0;i<10;i++) { - gen.GenerateTuple(result); - printf(" C:%x P:%d \n",result.getClient(),result.getClientPort()); - - result_src = result.getClient(); - result_port = result.getClientPort(); - result_mac = result.getClientMac(); - EXPECT_EQ(result_src, (uint32_t)(0x10000001+i%2)); - EXPECT_EQ(result_port, 1024+i/2); - printf("i:%d,mac:%d\n",i,result_mac->mac[3]); - if (i%2==0) - EXPECT_EQ(result_mac->mac[3], 5); - else - EXPECT_EQ(result_mac->mac[3], 1); - } - - gen.Delete(); -// EXPECT_EQ((size_t)0, gen.m_clients.size()); -} - TEST(tuple_gen,split1) { CIpPortion portion; @@ -421,7 +404,7 @@ TEST(tuple_gen,split2) { TEST(tuple_gen,template1) { CTupleGeneratorSmart gen; gen.Create(1, 1); - gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0); + gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4, g_dummy, 0, 0); gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false); CTupleTemplateGeneratorSmart template_1; template_1.Create(&gen,0,0); @@ -446,7 +429,7 @@ TEST(tuple_gen,template1) { TEST(tuple_gen,template2) { CTupleGeneratorSmart gen; gen.Create(1, 1); - gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0); + gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,g_dummy,0,0); gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false); CTupleTemplateGeneratorSmart template_1; template_1.Create(&gen,0,0); @@ -475,7 +458,7 @@ TEST(tuple_gen,template2) { TEST(tuple_gen,no_free) { CTupleGeneratorSmart gen; gen.Create(1, 1); - gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000001,64000,4,NULL,0,0); + gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000001,64000,4,g_dummy,0,0); gen.add_server_pool(cdSEQ_DIST,0x30000001,0x400000ff,64000,4,false); CTupleTemplateGeneratorSmart template_1; template_1.Create(&gen,0,0); @@ -497,7 +480,7 @@ TEST(tuple_gen,no_free) { TEST(tuple_gen,try_to_free) { CTupleGeneratorSmart gen; gen.Create(1, 1); - gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000001,64000,4,NULL,0,0); + gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000001,64000,4,g_dummy,0,0); gen.add_server_pool(cdSEQ_DIST,0x30000001,0x400000ff,64000,4,false); CTupleTemplateGeneratorSmart template_1; template_1.Create(&gen,0,0); @@ -524,7 +507,7 @@ TEST(tuple_gen,try_to_free) { TEST(tuple_gen_2,GenerateTuple) { CTupleGeneratorSmart gen; gen.Create(1, 1); - gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000f01,64000,4,NULL,0,0); + gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000f01,64000,4,g_dummy,0,0); gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false); CTupleTemplateGeneratorSmart template_1; template_1.Create(&gen,0,0); @@ -552,7 +535,7 @@ TEST(tuple_gen_2,GenerateTuple) { TEST(tuple_gen_2,GenerateTuple2) { CTupleGeneratorSmart gen; gen.Create(1, 1); - gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0); + gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,g_dummy,0,0); gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false); CTupleTemplateGeneratorSmart template_1; template_1.Create(&gen,0,0); @@ -576,7 +559,7 @@ TEST(tuple_gen_2,GenerateTuple2) { gen.Delete(); // EXPECT_EQ((size_t)0, gen.m_clients.size()); gen.Create(1, 1); - gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0); + gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,g_dummy,0,0); gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false); template_1.Create(&gen,0,0); for(int i=0;i<200;i++) { @@ -599,7 +582,7 @@ TEST(tuple_gen_2,GenerateTuple2) { TEST(tuple_gen_2,template1) { CTupleGeneratorSmart gen; gen.Create(1, 1); - gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0); + gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,g_dummy,0,0); gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false); CTupleTemplateGeneratorSmart template_1; template_1.Create(&gen,0,0); @@ -626,7 +609,7 @@ TEST(tuple_gen_2,template1) { TEST(tuple_gen_2,template2) { CTupleGeneratorSmart gen; gen.Create(1, 1); - gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0); + gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,g_dummy,0,0); gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false); CTupleTemplateGeneratorSmart template_1; template_1.Create(&gen,0,0); diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h index a52f9e60..7037584b 100644 --- a/src/internal_api/trex_platform_api.h +++ b/src/internal_api/trex_platform_api.h @@ -28,6 +28,7 @@ limitations under the License. #include <string.h> #include "flow_stat_parser.h" #include "trex_defs.h" +#include <json/json.h> /** * Global stats @@ -148,6 +149,7 @@ public: virtual int get_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset , TrexPlatformApi::driver_stat_cap_e type) const = 0; virtual int get_rfc2544_info(void *rfc2544_info, int min, int max, bool reset) const = 0; + 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; @@ -156,6 +158,8 @@ public: virtual bool get_promiscuous(uint8_t port_id) const = 0; virtual void flush_dp_messages() const = 0; virtual int get_active_pgids(flow_stat_active_t &result) const = 0; + virtual int get_cpu_util_full(cpu_util_full_t &result) const = 0; + virtual int get_mbuf_util(Json::Value &result) const = 0; virtual CFlowStatParser *get_flow_stat_parser() const = 0; virtual ~TrexPlatformApi() {} }; @@ -180,6 +184,7 @@ public: int get_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset , TrexPlatformApi::driver_stat_cap_e type) const; int get_rfc2544_info(void *rfc2544_info, int min, int max, bool reset) const; + 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; @@ -188,6 +193,8 @@ public: bool get_promiscuous(uint8_t port_id) const; void flush_dp_messages() const; int get_active_pgids(flow_stat_active_t &result) const; + int get_cpu_util_full(cpu_util_full_t &result) const; + int get_mbuf_util(Json::Value &result) const; CFlowStatParser *get_flow_stat_parser() const; }; @@ -237,6 +244,7 @@ public: int get_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset , TrexPlatformApi::driver_stat_cap_e type) const {return 0;}; virtual int get_rfc2544_info(void *rfc2544_info, int min, int max, bool reset) const {return 0;}; + 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;} @@ -252,6 +260,8 @@ public: void flush_dp_messages() const { } int get_active_pgids(flow_stat_active_t &result) const {return 0;} + int get_cpu_util_full(cpu_util_full_t &result) const {return 0;} + int get_mbuf_util(Json::Value &result) const {return 0;} CFlowStatParser *get_flow_stat_parser() const {return new CFlowStatParser();} private: diff --git a/src/latency.cpp b/src/latency.cpp index 841913cf..768e161b 100644 --- a/src/latency.cpp +++ b/src/latency.cpp @@ -436,7 +436,7 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) { m_no_ipv4_option++; return (false); } - m_parent->get_nat_manager()->handle_packet_ipv4(lp,parser.m_ipv4); + m_parent->get_nat_manager()->handle_packet_ipv4(lp, parser.m_ipv4, true); opt_len -= CNatOption::noOPTION_LEN; opt_ptr += CNatOption::noOPTION_LEN; break; @@ -445,10 +445,11 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) { return (false); } // End of switch } // End of while - if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK) - && parser.IsNatInfoPkt()) { - m_parent->get_nat_manager()->handle_packet_ipv4(NULL, parser.m_ipv4); - } + + bool first; + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP) && parser.IsNatInfoPkt(first)) { + m_parent->get_nat_manager()->handle_packet_ipv4(NULL, parser.m_ipv4, first); + } return (true); } // End of check for non-latency packet @@ -910,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 724621f0..63e50337 100644 --- a/src/latency.h +++ b/src/latency.h @@ -107,19 +107,27 @@ public: } // Check if this packet contains NAT info in TCP ack - inline bool IsNatInfoPkt() { - if (!m_ipv4 || (m_protocol != IPPROTO_TCP)) { - return false; - } - if (! m_l4 || (m_l4 - rte_pktmbuf_mtod(m_m, uint8_t*) + TCP_HEADER_LEN) > m_m->data_len) { - return false; - } - // If we are here, relevant fields from tcp header are guaranteed to be in first mbuf - TCPHeader *tcp = (TCPHeader *)m_l4; - if (!tcp->getSynFlag() || (tcp->getAckNumber() == 0)) { - return false; - } - return true; + // first - set to true if this is the first packet of the flow. false otherwise. + // relevant only if return value is true + inline bool IsNatInfoPkt(bool &first) { + if (!m_ipv4 || (m_protocol != IPPROTO_TCP)) { + return false; + } + if (! m_l4 || (m_l4 - rte_pktmbuf_mtod(m_m, uint8_t*) + TCP_HEADER_LEN) > m_m->data_len) { + return false; + } + // If we are here, relevant fields from tcp header are guaranteed to be in first mbuf + // We want to handle SYN and SYN+ACK packets + TCPHeader *tcp = (TCPHeader *)m_l4; + if (! tcp->getSynFlag()) + return false; + + if (! tcp->getAckFlag()) { + first = true; + } else { + first = false; + } + return true; } public: @@ -354,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/mac_mapping.h b/src/mac_mapping.h deleted file mode 100644 index 84151e8c..00000000 --- a/src/mac_mapping.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef MAC_MAPPING_H_ -#define MAC_MAPPING_H_ - -#define INUSED 0 -#define UNUSED 1 -typedef struct mac_addr_align_ { -public: - uint8_t mac[6]; - uint8_t inused; - uint8_t pad; -} mac_addr_align_t; - -typedef struct mac_mapping_ { - mac_addr_align_t mac; - uint32_t ip; -} mac_mapping_t; - -class CFlowGenListMac { -public: - CFlowGenListMac() { - set_configured(false); - } - - std::map<uint32_t, mac_addr_align_t> & - get_mac_info () { - return m_mac_info; - } - - bool is_configured() { - return is_mac_info_configured; - } - - void set_configured(bool is_conf) { - is_mac_info_configured = is_conf; - } - - void clear() { - set_configured(false); - m_mac_info.clear(); - } - - uint32_t is_mac_exist(uint32_t ip) { - if (is_configured()) { - return m_mac_info.count(ip); - } else { - return 0; - } - } - mac_addr_align_t* get_mac_addr_by_ip(uint32_t ip) { - if (is_mac_exist(ip)!=0) { - return &(m_mac_info[ip]); - } - return NULL; - } -private: - bool is_mac_info_configured; - std::map<uint32_t, mac_addr_align_t> m_mac_info; /* global mac info loaded form mac_file*/ -}; - -#endif //MAC_MAPPING_H_ diff --git a/src/main.cpp b/src/main.cpp index 62eee880..de6cef45 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,6 +22,7 @@ limitations under the License. #include "bp_sim.h" #include "os_time.h" +#include "trex_client_config.h" #include <unordered_map> #include <string> @@ -35,7 +36,7 @@ using namespace std; // An enum for all the option types enum { OPT_HELP, OPT_CFG, OPT_NODE_DUMP, OP_STATS, - OPT_FILE_OUT, OPT_UT, OPT_PCAP, OPT_IPV6, OPT_MAC_FILE, + OPT_FILE_OUT, OPT_UT, OPT_PCAP, OPT_IPV6, OPT_CLIENT_CFG_FILE, OPT_SL, OPT_DP_CORE_COUNT, OPT_DP_CORE_INDEX, OPT_LIMIT, OPT_DRY_RUN}; @@ -61,22 +62,22 @@ typedef enum { */ static CSimpleOpt::SOption parser_options[] = { - { OPT_HELP, "-?", SO_NONE }, - { OPT_HELP, "-h", SO_NONE }, - { OPT_HELP, "--help", SO_NONE }, - { OPT_UT, "--ut", SO_NONE }, - { OP_STATS, "-s", SO_NONE }, - { OPT_CFG, "-f", SO_REQ_SEP }, - { OPT_MAC_FILE, "--mac", SO_REQ_SEP }, - { OPT_FILE_OUT , "-o", SO_REQ_SEP }, - { OPT_NODE_DUMP , "-v", SO_REQ_SEP }, - { OPT_PCAP, "--pcap", SO_NONE }, - { OPT_IPV6, "--ipv6", SO_NONE }, - { OPT_SL, "--sl", SO_NONE }, - { OPT_DP_CORE_COUNT, "--cores", SO_REQ_SEP }, - { OPT_DP_CORE_INDEX, "--core_index", SO_REQ_SEP }, - { OPT_LIMIT, "--limit", SO_REQ_SEP }, - { OPT_DRY_RUN, "--dry", SO_NONE }, + { OPT_HELP, "-?", SO_NONE }, + { OPT_HELP, "-h", SO_NONE }, + { OPT_HELP, "--help", SO_NONE }, + { OPT_UT, "--ut", SO_NONE }, + { OP_STATS, "-s", SO_NONE }, + { OPT_CFG, "-f", SO_REQ_SEP }, + { OPT_CLIENT_CFG_FILE, "--client_cfg", SO_REQ_SEP }, + { OPT_FILE_OUT , "-o", SO_REQ_SEP }, + { OPT_NODE_DUMP , "-v", SO_REQ_SEP }, + { OPT_PCAP, "--pcap", SO_NONE }, + { OPT_IPV6, "--ipv6", SO_NONE }, + { OPT_SL, "--sl", SO_NONE }, + { OPT_DP_CORE_COUNT, "--cores", SO_REQ_SEP }, + { OPT_DP_CORE_INDEX, "--core_index", SO_REQ_SEP }, + { OPT_LIMIT, "--limit", SO_REQ_SEP }, + { OPT_DRY_RUN, "--dry", SO_NONE }, SO_END_OF_OPTIONS @@ -159,8 +160,8 @@ static int parse_options(int argc, po->cfg_file = args.OptionArg(); break; - case OPT_MAC_FILE: - po->mac_file = args.OptionArg(); + case OPT_CLIENT_CFG_FILE: + po->client_cfg_file = args.OptionArg(); break; case OPT_FILE_OUT: diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 820371ab..7bff8253 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -141,8 +141,8 @@ public: virtual int configure_drop_queue(CPhyEthIF * _if); virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats)=0; virtual void clear_extended_stats(CPhyEthIF * _if)=0; - virtual int wait_for_stable_link()=0; - virtual void wait_after_link_up(){}; + virtual int wait_for_stable_link(); + virtual void wait_after_link_up(); virtual bool flow_control_disable_supported(){return true;} virtual bool hw_rx_stat_supported(){return false;} virtual int get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, uint32_t *prev_pkts, uint32_t *bytes, uint32_t *prev_bytes @@ -538,6 +538,7 @@ enum { OPT_HELP, OPT_ONLY_LATENCY, OPT_1G_MODE, OPT_LATENCY_PREVIEW , + OPT_WAIT_BEFORE_TRAFFIC, OPT_PCAP, OPT_RX_CHECK, OPT_IO_MODE, @@ -548,12 +549,10 @@ enum { OPT_HELP, OPT_L_PKT_MODE, OPT_NO_FLOW_CONTROL, OPT_RX_CHECK_HOPS, - OPT_MAC_FILE, + OPT_CLIENT_CFG_FILE, OPT_NO_KEYBOARD_INPUT, - OPT_VLAN, OPT_VIRT_ONE_TX_RX_QUEUE, OPT_PREFIX, - OPT_MAC_SPLIT, OPT_SEND_DEBUG_PKT, OPT_NO_WATCHDOG, OPT_ALLOW_COREDUMP, @@ -602,6 +601,7 @@ static CSimpleOpt::SOption parser_options[] = { OPT_1G_MODE, "-1g", SO_NONE }, { OPT_LATENCY_PREVIEW , "-k", SO_REQ_SEP }, + { OPT_WAIT_BEFORE_TRAFFIC , "-w", SO_REQ_SEP }, { OPT_PCAP, "--pcap", SO_NONE }, { OPT_RX_CHECK, "--rx-check", SO_REQ_SEP }, { OPT_IO_MODE, "--iom", SO_REQ_SEP }, @@ -612,12 +612,10 @@ static CSimpleOpt::SOption parser_options[] = { OPT_LEARN_VERIFY, "--learn-verify", SO_NONE }, { OPT_L_PKT_MODE, "--l-pkt-mode", SO_REQ_SEP }, { OPT_NO_FLOW_CONTROL, "--no-flow-control-change", SO_NONE }, - { OPT_VLAN, "--vlan", SO_NONE }, - { OPT_MAC_FILE, "--mac", SO_REQ_SEP }, + { OPT_CLIENT_CFG_FILE, "--client_cfg", SO_REQ_SEP }, { OPT_NO_KEYBOARD_INPUT ,"--no-key", SO_NONE }, { OPT_VIRT_ONE_TX_RX_QUEUE, "--vm-sim", SO_NONE }, { OPT_PREFIX, "--prefix", SO_REQ_SEP }, - { OPT_MAC_SPLIT, "--mac-spread", SO_REQ_SEP }, { OPT_SEND_DEBUG_PKT, "--send-debug-pkt", SO_REQ_SEP }, { OPT_MBUF_FACTOR , "--mbuf-factor", SO_REQ_SEP }, { OPT_NO_WATCHDOG , "--no-watchdog", SO_NONE }, @@ -644,7 +642,7 @@ static int usage(){ printf(" options \n\n"); - printf(" --mac [file] : YAML file with <client ip, mac addr> configuration \n"); + printf(" --client_cfg [file] : YAML file which describes clients configuration\n"); printf(" \n\n"); printf(" -c [number of threads] : default is 1. number of threads to allocate for each dual ports. \n"); printf(" \n"); @@ -682,6 +680,8 @@ static int usage(){ printf(" \n"); printf(" -k [sec] : run latency test before starting the test. it will wait for x sec sending packet and x sec after that \n"); printf(" \n"); + printf(" -w [sec] : wait between init of interfaces and sending traffic, default is 1\n"); + printf(" \n"); printf(" --cfg [platform_yaml] : load and configure platform using this file see example in cfg/cfg_examplexx.yaml file \n"); printf(" this file is used to configure/mask interfaces cores affinity and mac addr \n"); @@ -690,9 +690,10 @@ static int usage(){ printf(" --ipv6 : work in ipv6 mode\n"); printf(" --learn (deprecated). Replaced by --learn-mode. To get older behaviour, use --learn-mode 2\n"); - printf(" --learn-mode [1-2] : Work in NAT environments, learn the dynamic NAT translation and ALG \n"); - printf(" 1 Use TCP ACK in first SYN to pass NAT translation information. Will work only for TCP streams. Initial SYN packet must be present in stream.\n"); + printf(" --learn-mode [1-3] : Work in NAT environments, learn the dynamic NAT translation and ALG \n"); + printf(" 1 Use TCP ACK in first SYN to pass NAT translation information. Will work only for TCP streams. Initial SYN packet must be first packet in stream.\n"); printf(" 2 Add special IP option to pass NAT translation information. Will not work on certain firewalls if they drop packets with IP options\n"); + printf(" 3 Like 1, but without support for sequence number randomization in server->clien direction. Performance (flow/second) better than 1\n"); printf(" --learn-verify : Learn the translation, but intended for verification of the mechanism in cases that NAT does not exist \n"); printf(" \n"); printf(" --l-pkt-mode [0-3] : Set mode for sending latency packets.\n"); @@ -719,8 +720,6 @@ static int usage(){ printf(" --no-key : daemon mode, don't get input from keyboard \n"); printf(" --no-flow-control-change : By default TRex disables flow-control. If this option is given, it does not touch it\n"); printf(" --prefix : for multi trex, each instance should have a different name \n"); - printf(" --mac-spread : Spread the destination mac-order by this factor. e.g 2 will generate the traffic to 2 devices DEST-MAC ,DEST-MAC+1 \n"); - printf(" maximum is up to 128 devices \n"); printf(" --mbuf-factor : factor for packet memory \n"); printf(" \n"); printf(" --no-watchdog : disable watchdog \n"); @@ -745,7 +744,7 @@ static int usage(){ printf("\n"); printf("\n"); - printf(" Copyright (c) 2015-2015 Cisco Systems, Inc. \n"); + printf(" Copyright (c) 2015-2016 Cisco Systems, Inc. \n"); printf(" \n"); printf(" Licensed under the Apache License, Version 2.0 (the 'License') \n"); printf(" you may not use this file except in compliance with the License. \n"); @@ -830,8 +829,8 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t po->preview.set_no_keyboard(true); break; - case OPT_MAC_FILE : - po->mac_file = args.OptionArg(); + case OPT_CLIENT_CFG_FILE : + po->client_cfg_file = args.OptionArg(); break; case OPT_PLAT_CFG_FILE : @@ -846,9 +845,6 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t po->preview.set_ipv6_mode_enable(true); break; - case OPT_VLAN: - po->preview.set_vlan_mode_enable(true); - break; case OPT_LEARN : po->m_learn_mode = CParserOption::LEARN_MODE_IP_OPTION; @@ -954,6 +950,10 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t sscanf(args.OptionArg(),"%d", &po->m_latency_prev); break; + case OPT_WAIT_BEFORE_TRAFFIC : + sscanf(args.OptionArg(),"%d", &po->m_wait_before_traffic); + break; + case OPT_PCAP: po->preview.set_pcap_mode_enable(true); break; @@ -980,13 +980,6 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t po->prefix = args.OptionArg(); break; - case OPT_MAC_SPLIT: - sscanf(args.OptionArg(),"%d", &tmp_data); - po->m_mac_splitter = (uint8_t)tmp_data; - po->preview.set_mac_ip_features_enable(true); - po->preview.setDestMacSplit(true); - break; - case OPT_SEND_DEBUG_PKT: sscanf(args.OptionArg(),"%d", &tmp_data); po->m_debug_pkt_proto = (uint8_t)tmp_data; @@ -1014,17 +1007,9 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t parse_err("Please provide single run mode (e.g. batch or interactive)"); } - if ( po->m_mac_splitter > 128 ){ - std::stringstream ss; - ss << "maximum mac spreading is 128 you set it to: " << po->m_mac_splitter; - parse_err(ss.str()); - } - - if ( CGlobalInfo::is_learn_mode() ){ - if ( po->preview.get_ipv6_mode_enable() ){ - parse_err("--learn mode is not supported with --ipv6, beacuse there is not such thing NAT66 ( ipv6-ipv6) \n" \ - "if you think it is important,open a defect \n"); - } + if (CGlobalInfo::is_learn_mode() && po->preview.get_ipv6_mode_enable()) { + parse_err("--learn mode is not supported with --ipv6, beacuse there is not such thing NAT66 ( ipv6-ipv6) \n" \ + "if you think it is important,open a defect \n"); } if (po->preview.get_is_rx_check_enable() || po->is_latency_enabled() || CGlobalInfo::is_learn_mode()) { @@ -1777,7 +1762,9 @@ public: virtual void flush_dp_rx_queue(void); virtual int flush_tx_queue(void); __attribute__ ((noinline)) void flush_rx_queue(); - __attribute__ ((noinline)) void update_mac_addr(CGenNode * node,uint8_t *p); + __attribute__ ((noinline)) void handle_slowpath_features(CGenNode *node, rte_mbuf_t *m, uint8_t *p, pkt_dir_t dir); + + void apply_client_cfg(const ClientCfg *cfg, rte_mbuf_t *m, pkt_dir_t dir, uint8_t *p); bool process_rx_pkt(pkt_dir_t dir,rte_mbuf_t * m); @@ -1810,6 +1797,8 @@ protected: rte_mbuf_t *m, CVirtualIFPerSideStats * lp_stats); + void add_vlan(rte_mbuf_t *m, uint16_t vlan_id); + protected: uint8_t m_core_id; uint16_t m_mbuf_cache; @@ -2034,27 +2023,6 @@ void CCoreEthIF::send_one_pkt(pkt_dir_t dir, lp_port->m_len = 0; } - -void CCoreEthIF::update_mac_addr(CGenNode * node,uint8_t *p){ - - if ( CGlobalInfo::m_options.preview.getDestMacSplit() ) { - p[5]+= (node->m_src_ip % CGlobalInfo::m_options.m_mac_splitter); - } - - if ( unlikely( CGlobalInfo::m_options.preview.get_mac_ip_mapping_enable() ) ) { - /* mac mapping file is configured - */ - if ( node->is_initiator_pkt() && (node->m_src_mac.inused==INUSED)) { - memcpy(p+6, &node->m_src_mac.mac, 6); - } - } else if ( unlikely( CGlobalInfo::m_options.preview.get_mac_ip_overide_enable() ) ){ - /* client side */ - if ( node->is_initiator_pkt() ){ - *((uint32_t*)(p+6))=PKT_NTOHL(node->m_src_ip); - } - } -} - int CCoreEthIFStateless::send_node_flow_stat(rte_mbuf *m, CGenNodeStateless * node_sl, CCorePerPort * lp_port , CVirtualIFPerSideStats * lp_stats, bool is_const) { // Defining this makes 10% percent packet loss. 1% packet reorder. @@ -2180,8 +2148,59 @@ int CCoreEthIFStateless::handle_slow_path_node(CGenNode * no) { return (-1); } +void CCoreEthIF::apply_client_cfg(const ClientCfg *cfg, rte_mbuf_t *m, pkt_dir_t dir, uint8_t *p) { + + assert(cfg); + + /* take the right direction config */ + const ClientCfgDir &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder); + + /* dst mac */ + if (cfg_dir.has_dst_mac_addr()) { + memcpy(p, cfg_dir.get_dst_mac_addr(), 6); + } + + /* src mac */ + if (cfg_dir.has_src_mac_addr()) { + memcpy(p + 6, cfg_dir.get_src_mac_addr(), 6); + } + + /* VLAN */ + if (cfg_dir.has_vlan()) { + add_vlan(m, cfg_dir.get_vlan()); + } +} + + +void CCoreEthIF::add_vlan(rte_mbuf_t *m, uint16_t vlan_id) { + m->ol_flags = PKT_TX_VLAN_PKT; + m->l2_len = 14; + m->vlan_tci = 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 */ + if ( node->is_initiator_pkt() ) { + *((uint32_t*)(p+6)) = PKT_NTOHL(node->m_src_ip); + } + } + + /* flag is faster than checking the node pointer (another cacheline) */ + if ( unlikely(CGlobalInfo::m_options.preview.get_is_client_cfg_enable() ) ) { + apply_client_cfg(node->m_client_cfg, m, dir, p); + } + +} -int CCoreEthIF::send_node(CGenNode * node){ +int CCoreEthIF::send_node(CGenNode * node) { if ( unlikely( node->get_cache_mbuf() !=NULL ) ) { pkt_dir_t dir; @@ -2199,33 +2218,30 @@ int CCoreEthIF::send_node(CGenNode * node){ rte_mbuf_t * m=lp->generate_new_mbuf(node); pkt_dir_t dir; - bool single_port; + bool single_port; - dir = node->cur_interface_dir(); + 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); - - /* set the vlan */ - m->ol_flags = PKT_TX_VLAN_PKT; - m->l2_len =14; - uint16_t vlan_id = CGlobalInfo::m_options.m_vlan_port[vlan_port]; - + uint16_t vlan_id = CGlobalInfo::m_options.m_vlan_port[vlan_port]; if (likely( vlan_id >0 ) ) { - m->vlan_tci = vlan_id; dir = dir ^ vlan_port; }else{ /* both from the same dir but with VLAN0 */ - m->vlan_tci = CGlobalInfo::m_options.m_vlan_port[0]; + vlan_id = CGlobalInfo::m_options.m_vlan_port[0]; dir = dir ^ 0; } + + add_vlan(m, vlan_id); } - CCorePerPort * lp_port=&m_ports[dir]; - CVirtualIFPerSideStats * lp_stats = &m_stats[dir]; + CCorePerPort *lp_port = &m_ports[dir]; + CVirtualIFPerSideStats *lp_stats = &m_stats[dir]; if (unlikely(m==0)) { lp_stats->m_tx_alloc_error++; @@ -2233,19 +2249,17 @@ 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(); - - + 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); - /* if customer enables both mac_file and get_mac_ip_overide, - * we will apply mac_file. - */ - if ( unlikely(CGlobalInfo::m_options.preview.get_mac_ip_features_enable() ) ) { - update_mac_addr(node,p); + /* when slowpath features are on */ + if ( unlikely( CGlobalInfo::m_options.preview.get_is_slowpath_features_on() ) ) { + 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); @@ -2446,8 +2460,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 ; @@ -2468,6 +2484,7 @@ public: float m_active_flows; float m_open_flows; float m_cpu_util; + float m_cpu_util_raw; float m_rx_cpu_util; float m_bw_per_core; uint8_t m_threads; @@ -2477,7 +2494,7 @@ public: public: void Dump(FILE *fd,DumpFormat mode); void DumpAllPorts(FILE *fd); - void dump_json(std::string & json, bool baseline,uint32_t stats_tick); + void dump_json(std::string & json, bool baseline); private: std::string get_field(std::string name,float &f); std::string get_field(std::string name,uint64_t &f); @@ -2517,7 +2534,7 @@ std::string CGlobalStats::get_field_port(int port,std::string name,uint64_t &f){ } -void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_tick){ +void CGlobalStats::dump_json(std::string & json, bool baseline){ /* refactor this to JSON */ json="{\"name\":\"trex-global\",\"type\":0,"; @@ -2535,6 +2552,7 @@ void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_ti #define GET_FIELD_PORT(p,f) get_field_port(p,std::string(#f),lp->f) json+=GET_FIELD(m_cpu_util); + json+=GET_FIELD(m_cpu_util_raw); json+=GET_FIELD(m_bw_per_core); json+=GET_FIELD(m_rx_cpu_util); json+=GET_FIELD(m_platform_factor); @@ -2564,8 +2582,10 @@ void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_ti 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); @@ -2584,10 +2604,6 @@ void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_ti json+=GET_FIELD_PORT(i,m_total_rx_pps); } json+=m_template.dump_as_json("template"); - if ( stats_tick %4==0){ - json+=CGlobalInfo::dump_pool_as_json(); /* no need a feq update beacuse it trash the cores D cache, once in 2 sec */ - } - json+="\"unknown\":0}}" ; } @@ -2605,7 +2621,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"); } @@ -2613,28 +2634,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"); } @@ -3381,7 +3407,9 @@ int CGlobalTRex::ixgbe_prob_init(void){ if ( !CTRexExtendedDriverDb::Ins()->is_driver_exists(dev_info.driver_name) ){ - printf(" ERROR driver name %s is not supported \n",dev_info.driver_name); + printf(" Error: driver %s is not supported. Please consult the documentation for a list of supported drivers\n" + ,dev_info.driver_name); + exit(1); } int i; @@ -3604,6 +3632,7 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){ stats.m_num_of_ports = m_max_ports; stats.m_cpu_util = m_fl.GetCpuUtil(); + stats.m_cpu_util_raw = m_fl.GetCpuUtilRaw(); if (get_is_stateless()) { stats.m_rx_cpu_util = m_rx_sl.get_cpu_util(); } @@ -3655,8 +3684,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; @@ -3685,8 +3716,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; @@ -3708,8 +3741,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; @@ -3835,7 +3870,7 @@ CGlobalTRex::publish_async_data(bool sync_now, bool baseline) { get_stats(m_stats); } - m_stats.dump_json(json, baseline,m_stats_cnt); + m_stats.dump_json(json, baseline); m_zmq_publisher.publish_json(json); /* generator json , all cores are the same just sample the first one */ @@ -3929,7 +3964,7 @@ CGlobalTRex::handle_slow_path(bool &was_stopped) { if (m_io_modes.m_g_mode == CTrexGlobalIoMode::gMem) { if ( m_stats_cnt%4==0) { - fprintf (stdout," %s \n",CGlobalInfo::dump_pool_as_json().c_str()); + fprintf (stdout," %s \n",CGlobalInfo::dump_pool_as_json_str().c_str()); } } @@ -3967,9 +4002,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); + } } } @@ -4221,12 +4264,24 @@ int CGlobalTRex::start_master_statefull() { m_fl.Create(); m_fl.load_from_yaml(CGlobalInfo::m_options.cfg_file,get_cores_tx()); - if (CGlobalInfo::m_options.mac_file != "") { - CGlobalInfo::m_options.preview.set_mac_ip_mapping_enable(true); - m_fl.load_from_mac_file(CGlobalInfo::m_options.mac_file); - m_fl.m_mac_info.set_configured(true); - } else { - m_fl.m_mac_info.set_configured(false); + + /* client config */ + if (CGlobalInfo::m_options.client_cfg_file != "") { + try { + m_fl.load_client_config_file(CGlobalInfo::m_options.client_cfg_file); + } catch (const std::runtime_error &e) { + std::cout << "\n*** " << e.what() << "\n\n"; + exit(-1); + } + CGlobalInfo::m_options.preview.set_client_cfg_enable(true); + } + + /* verify options */ + try { + CGlobalInfo::m_options.verify(); + } catch (const std::runtime_error &e) { + std::cout << "\n*** " << e.what() << "\n\n"; + exit(-1); } m_expected_pps = m_fl.get_total_pps(); @@ -4860,6 +4915,18 @@ int main_test(int argc , char * argv[]){ return (0); } +void wait_x_sec(int sec) { + int i; + printf(" wait %d sec ", sec); + fflush(stdout); + for (i=0; i<sec; i++) { + delay(1000); + printf("."); + fflush(stdout); + } + printf("\n"); + fflush(stdout); +} ////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////// @@ -4870,32 +4937,28 @@ int CTRexExtendedDriverBase::configure_drop_queue(CPhyEthIF * _if) { return (rte_eth_dev_rx_queue_stop(port_id, 0)); } +int CTRexExtendedDriverBase::wait_for_stable_link() { + wait_x_sec(CGlobalInfo::m_options.m_wait_before_traffic); + return 0; +} + +void CTRexExtendedDriverBase::wait_after_link_up() { + wait_x_sec(CGlobalInfo::m_options.m_wait_before_traffic); +} + CFlowStatParser *CTRexExtendedDriverBase::get_flow_stat_parser() { CFlowStatParser *parser = new CFlowStatParser(); assert (parser); return parser; } -void wait_x_sec(int sec) { - int i; - printf(" wait %d sec ", sec); - fflush(stdout); - for (i=0; i<sec; i++) { - delay(1000); - printf("."); - fflush(stdout); - } - printf("\n"); - fflush(stdout); -} - // in 1G we need to wait if links became ready to soon void CTRexExtendedDriverBase1G::wait_after_link_up(){ - wait_x_sec(7); + wait_x_sec(6 + CGlobalInfo::m_options.m_wait_before_traffic); } int CTRexExtendedDriverBase1G::wait_for_stable_link(){ - wait_x_sec(10); + wait_x_sec(9 + CGlobalInfo::m_options.m_wait_before_traffic); return(0); } @@ -5277,7 +5340,7 @@ void CTRexExtendedDriverBase10G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSta } int CTRexExtendedDriverBase10G::wait_for_stable_link(){ - delay(2000); + wait_x_sec(1 + CGlobalInfo::m_options.m_wait_before_traffic); return (0); } @@ -5531,11 +5594,10 @@ void CTRexExtendedDriverBase40G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSta stats->f_ibytes = 0; - stats->ierrors = stats1.ierrors + stats1.imissed + stats1.ibadcrc + + stats->ierrors = stats1.imissed + stats1.ibadcrc + stats1.ibadlen + stats1.ierrors + stats1.oerrors + - stats1.imcasts + stats1.rx_nombuf + stats1.tx_pause_xon + stats1.rx_pause_xon + @@ -5550,7 +5612,7 @@ void CTRexExtendedDriverBase40G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSta } int CTRexExtendedDriverBase40G::wait_for_stable_link(){ - delay(2000); + wait_x_sec(1 + CGlobalInfo::m_options.m_wait_before_traffic); return (0); } @@ -5605,11 +5667,10 @@ void CTRexExtendedDriverBase1GVm::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSt stats->f_ibytes = 0; - stats->ierrors = stats1.ierrors + stats1.imissed + stats1.ibadcrc + + stats->ierrors = stats1.imissed + stats1.ibadcrc + stats1.ibadlen + stats1.ierrors + stats1.oerrors + - stats1.imcasts + stats1.rx_nombuf + stats1.tx_pause_xon + stats1.rx_pause_xon + @@ -5624,7 +5685,7 @@ void CTRexExtendedDriverBase1GVm::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSt } int CTRexExtendedDriverBase1GVm::wait_for_stable_link(){ - delay(10); + wait_x_sec(CGlobalInfo::m_options.m_wait_before_traffic); return (0); } @@ -5782,6 +5843,10 @@ int TrexDpdkPlatformApi::get_rfc2544_info(void *rfc2544_info, int min, int max, return g_trex.m_rx_sl.get_rfc2544_info((rfc2544_info_t *)rfc2544_info, min, max, reset); } +int TrexDpdkPlatformApi::get_rx_err_cntrs(void *rx_err_cntrs) const { + return g_trex.m_rx_sl.get_rx_err_cntrs((CRxCoreErrCntrs *)rx_err_cntrs); +} + int TrexDpdkPlatformApi::reset_hw_flow_stats(uint8_t port_id) const { return g_trex.m_ports[port_id].reset_hw_flow_stats(); } @@ -5812,6 +5877,20 @@ int TrexDpdkPlatformApi::get_active_pgids(flow_stat_active_t &result) const { return g_trex.m_trex_stateless->m_rx_flow_stat.get_active_pgids(result); } +int TrexDpdkPlatformApi::get_cpu_util_full(cpu_util_full_t &cpu_util_full) const { + cpu_util_full.resize((int)g_trex.m_fl.m_threads_info.size()); + for (int thread_id=0; thread_id<(int)g_trex.m_fl.m_threads_info.size(); thread_id++) { + CFlowGenListPerThread * lp=g_trex.m_fl.m_threads_info[thread_id]; + lp->m_cpu_cp_u.GetHistory(cpu_util_full[thread_id]); + } + return 0; +} + +int TrexDpdkPlatformApi::get_mbuf_util(Json::Value &mbuf_pool) const { + CGlobalInfo::dump_pool_as_json(mbuf_pool); + return 0; +} + CFlowStatParser *TrexDpdkPlatformApi::get_flow_stat_parser() const { return CTRexExtendedDriverDb::Ins()->get_drv()->get_flow_stat_parser(); } diff --git a/src/nat_check.cpp b/src/nat_check.cpp index 7e224430..f3dd93d1 100755 --- a/src/nat_check.cpp +++ b/src/nat_check.cpp @@ -41,7 +41,8 @@ void CGenNodeNatInfo::dump(FILE *fd){ int i; for (i=0; i<m_cnt; i++) { CNatFlowInfo * lp=&m_data[i]; - fprintf (fd," id:%d , external ip:%08x:%x , ex_port: %04x , fid: %d \n",i,lp->m_external_ip,lp->m_external_ip_server,lp->m_external_port,lp->m_fid); + fprintf (fd," id:%d , external ip:%08x , ex_port: %04x , TCP seq:%x fid: %d \n" + , i, lp->m_external_ip, lp->m_external_port, lp->m_tcp_seq, lp->m_fid); } } @@ -51,7 +52,6 @@ void CGenNodeNatInfo::init(){ m_cnt=0; } - void CNatStats::reset(){ m_total_rx=0; m_total_msg=0; @@ -147,14 +147,17 @@ void CNatRxManager::get_info_from_tcp_ack(uint32_t tcp_ack, uint32_t &fid, uint8 * option - pointer to our proprietary NAT info IP option. * If it is NULL, the NAT info is in the TCP ACK number * ipv4 - pointer to ipv4 header to extract info from. + * is_first - Is this the first packet of the flow or second. To handle firewalls that do + * TCP seq randomization on the server->client side, we also look at the second + * packet of the flow (SYN+ACK), and extract its seq num. */ -void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) { +void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4, bool is_first) { CNatPerThreadInfo * thread_info; uint32_t fid=0; + uint32_t tcp_seq; /* Extract info from the packet ! */ uint32_t ext_ip = ipv4->getSourceIp(); - uint32_t ext_ip_server = ipv4->getDestIp(); uint8_t proto = ipv4->getProtocol(); /* must be TCP/UDP this is the only supported proto */ if (!( (proto==6) || (proto==17) )){ @@ -165,21 +168,48 @@ void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) { TCPHeader *tcp = (TCPHeader *) (((char *)ipv4)+ ipv4->getHeaderLength()); uint16_t ext_port = tcp->getSourcePort(); + tcp_seq = tcp->getSeqNumber(); + if (option) { thread_info = get_thread_info(option->get_thread_id()); fid = option->get_fid(); } else { - uint8_t thread_id; - get_info_from_tcp_ack(tcp->getAckNumber(), fid, thread_id); - thread_info = get_thread_info(thread_id); + uint8_t thread_id; + + if (is_first) { + uint32_t tcp_ack = tcp->getAckNumber(); + get_info_from_tcp_ack(tcp_ack, fid, thread_id); + thread_info = get_thread_info(thread_id); + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + uint32_t dst_ip = ipv4->getDestIp(); + uint16_t dst_port = tcp->getDestPort(); + uint64_t map_key = (dst_ip << 16) + dst_port; + 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_ft.erase(map_key, val)) { + get_info_from_tcp_ack(val, fid, thread_id); + thread_info = get_thread_info(thread_id); + } else { + // flow was not found in the table + thread_info = 0; + } + } } + if (unlikely(!thread_info)) { return; } #ifdef NAT_TRACE_ - printf("rx msg ext ip : %08x:%08x ext port : %04x flow_id : %d \n",ext_ip,ext_ip_server,ext_port, fid); + printf("rx msg ext ip: %08x ext port: %04x TCP Seq: %08x flow_id : %d (%s) \n", ext_ip, ext_port, tcp_seq, fid + , is_first ? "first":"second"); #endif CGenNodeNatInfo * node=thread_info->m_cur_nat_msg; @@ -194,9 +224,13 @@ void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) { CNatFlowInfo * msg=node->get_next_msg(); /* fill the message */ - msg->m_external_ip = ext_ip; - msg->m_external_ip_server = ext_ip_server; - msg->m_external_port = ext_port; + if (is_first) { + msg->m_external_ip = ext_ip; + msg->m_external_port = ext_port; + } else { + msg->m_external_port = TCPHeader::TCP_INVALID_PORT; + } + msg->m_tcp_seq = tcp_seq; msg->m_fid = fid; msg->m_pad = 0xee; @@ -221,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 133501ae..18add5e0 100755 --- a/src/nat_check.h +++ b/src/nat_check.h @@ -21,6 +21,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +#include <map> #include "msg_manager.h" #include <common/Network/Packet/TcpHeader.h> #include <common/Network/Packet/UdpHeader.h> @@ -28,6 +29,7 @@ limitations under the License. #include <common/Network/Packet/IPv6Header.h> #include <common/Network/Packet/EthernetHeader.h> #include "os_time.h" +#include "nat_check_flow_table.h" // 2msec timeout #define MAX_TIME_MSG_IN_QUEUE_SEC ( 0.002 ) @@ -121,7 +123,7 @@ private: struct CNatFlowInfo { uint32_t m_external_ip; - uint32_t m_external_ip_server; + uint32_t m_tcp_seq; uint32_t m_fid; uint16_t m_external_port; uint16_t m_pad; @@ -210,13 +212,12 @@ public: void Dump(FILE *fd); }; - class CNatRxManager { public: bool Create(); void Delete(); - void handle_packet_ipv4(CNatOption * option, IPHeader * ipv4); + void handle_packet_ipv4(CNatOption * option, IPHeader * ipv4, bool is_first); void handle_aging(); void Dump(FILE *fd); void DumpShort(FILE *fd); @@ -232,6 +233,7 @@ private: uint8_t m_max_threads; CNatPerThreadInfo * m_per_thread; CNatStats m_stats; + 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 <assert.h> +#include <stdint.h> +#include <string> +#include <iostream> +#include <new> +#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<uint64_t, CNatData *> pair = std::pair<uint64_t, CNatData *>(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 <map> + +class CNatData; +class CNatCheckFlowTableList; + +typedef std::map<uint64_t, CNatData *, std::less<uint64_t> > 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/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp index 68ea2587..27010e0e 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp @@ -157,6 +157,29 @@ TrexRpcCmdGetActivePGIds::_run(const Json::Value ¶ms, Json::Value &result) { return (TREX_RPC_CMD_OK); } +// get utilization of CPU per thread with up to 20 latest values + mbufs per socket +trex_rpc_cmd_rc_e +TrexRpcCmdGetUtilization::_run(const Json::Value ¶ms, Json::Value &result) { + cpu_util_full_t cpu_util_full; + + Json::Value §ion = result["result"]; + + if (get_stateless_obj()->get_platform_api()->get_mbuf_util(section) != 0) { + return TREX_RPC_CMD_INTERNAL_ERR; + } + + if (get_stateless_obj()->get_platform_api()->get_cpu_util_full(cpu_util_full) != 0) { + return TREX_RPC_CMD_INTERNAL_ERR; + } + + for (int thread_id = 0; thread_id < cpu_util_full.size(); thread_id++) { + for (int history_id = 0; history_id < cpu_util_full[thread_id].size(); history_id++) { + section["cpu"][thread_id].append(cpu_util_full[thread_id][history_id]); + } + } + return (TREX_RPC_CMD_OK); +} + /** * get the CPU model * diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h index affa65c1..2776727d 100644 --- a/src/rpc-server/commands/trex_rpc_cmds.h +++ b/src/rpc-server/commands/trex_rpc_cmds.h @@ -67,6 +67,7 @@ TREX_RPC_CMD_DEFINE(TrexRpcPublishNow, "publish_now", 2, false, TREX_RPC_CMD_DEFINE(TrexRpcCmdGetCmds, "get_supported_cmds", 0, false, APIClass::API_CLASS_TYPE_CORE); TREX_RPC_CMD_DEFINE(TrexRpcCmdGetVersion, "get_version", 0, false, APIClass::API_CLASS_TYPE_CORE); TREX_RPC_CMD_DEFINE(TrexRpcCmdGetActivePGIds, "get_active_pgids", 0, false, APIClass::API_CLASS_TYPE_CORE); +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetUtilization, "get_utilization", 0, false, APIClass::API_CLASS_TYPE_CORE); TREX_RPC_CMD_DEFINE_EXTENDED(TrexRpcCmdGetSysInfo, "get_system_info", 0, false, APIClass::API_CLASS_TYPE_CORE, diff --git a/src/rpc-server/trex_rpc_cmds_table.cpp b/src/rpc-server/trex_rpc_cmds_table.cpp index 7104792e..6144d265 100644 --- a/src/rpc-server/trex_rpc_cmds_table.cpp +++ b/src/rpc-server/trex_rpc_cmds_table.cpp @@ -39,6 +39,7 @@ TrexRpcCommandsTable::TrexRpcCommandsTable() { register_command(new TrexRpcCmdGetCmds()); register_command(new TrexRpcCmdGetVersion()); register_command(new TrexRpcCmdGetActivePGIds()); + register_command(new TrexRpcCmdGetUtilization()); register_command(new TrexRpcCmdGetSysInfo()); register_command(new TrexRpcCmdGetOwner()); register_command(new TrexRpcCmdAcquire()); diff --git a/src/sim/trex_sim_stateful.cpp b/src/sim/trex_sim_stateful.cpp index 88698cd1..7546644d 100644 --- a/src/sim/trex_sim_stateful.cpp +++ b/src/sim/trex_sim_stateful.cpp @@ -165,6 +165,24 @@ int load_list_of_cap_files(CParserOption * op){ CFlowGenList fl; fl.Create(); fl.load_from_yaml(op->cfg_file,1); + + if (op->client_cfg_file != "") { + try { + fl.load_client_config_file(op->client_cfg_file); + } catch (const std::runtime_error &e) { + std::cout << "\n*** " << e.what() << "\n\n"; + exit(-1); + } + CGlobalInfo::m_options.preview.set_client_cfg_enable(true); + } + + try { + CGlobalInfo::m_options.verify(); + } catch (const std::runtime_error &e) { + std::cout << "\n*** " << e.what() << "\n\n"; + exit(-1); + } + if ( op->preview.getVMode() >0 ) { fl.DumpCsv(stdout); } @@ -596,5 +614,10 @@ int merge_2_cap_files_sip() { int SimStateful::run() { assert( CMsgIns::Ins()->Create(4) ); - return load_list_of_cap_files(&CGlobalInfo::m_options); + try { + return load_list_of_cap_files(&CGlobalInfo::m_options); + } catch (const std::runtime_error &e) { + std::cout << "\n*** " << e.what() << "\n\n"; + exit(-1); + } } diff --git a/src/stateless/cp/trex_stateless.cpp b/src/stateless/cp/trex_stateless.cpp index 698ede90..8633897e 100644 --- a/src/stateless/cp/trex_stateless.cpp +++ b/src/stateless/cp/trex_stateless.cpp @@ -54,7 +54,11 @@ TrexStateless::TrexStateless(const TrexStatelessCfg &cfg) { m_publisher = cfg.m_publisher; /* API core version */ - m_api_classes[APIClass::API_CLASS_TYPE_CORE].init(APIClass::API_CLASS_TYPE_CORE, 1, 2); + const int API_VER_MAJOR = 1; + const int API_VER_MINOR = 3; + m_api_classes[APIClass::API_CLASS_TYPE_CORE].init(APIClass::API_CLASS_TYPE_CORE, + API_VER_MAJOR, + API_VER_MINOR); } /** diff --git a/src/stateless/rx/trex_stateless_rx_core.cpp b/src/stateless/rx/trex_stateless_rx_core.cpp index 0bd601b6..853fc868 100644 --- a/src/stateless/rx/trex_stateless_rx_core.cpp +++ b/src/stateless/rx/trex_stateless_rx_core.cpp @@ -206,6 +206,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t * if (unlikely(fsp_head->magic != FLOW_STAT_PAYLOAD_MAGIC) || hw_id >= MAX_FLOW_STATS_PAYLOAD) { good_packet = false; + m_err_cntrs.m_bad_header++; } else { curr_rfc2544 = &m_rfc2544[hw_id]; @@ -216,6 +217,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t * if (fsp_head->flow_seq == curr_rfc2544->get_prev_flow_seq()) { // packet from previous flow using this hw_id that arrived late good_packet = false; + m_err_cntrs.m_old_flow++; } else { if (curr_rfc2544->no_flow_seq()) { // first packet we see from this flow @@ -224,6 +226,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t * } else { // garbage packet good_packet = false; + m_err_cntrs.m_bad_header++; } } } @@ -278,9 +281,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t * } } else { hw_id = get_hw_id(ip_id); - if (hw_id >= MAX_FLOW_STATS) { - // increase some error counter - } else { + if (hw_id < MAX_FLOW_STATS) { lp->m_port.m_rx_pg_stat[hw_id].add_pkts(1); lp->m_port.m_rx_pg_stat[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC } @@ -444,6 +445,11 @@ int CRxCoreStateless::get_rfc2544_info(rfc2544_info_t *rfc2544_info, int min, in return 0; } +int CRxCoreStateless::get_rx_err_cntrs(CRxCoreErrCntrs *rx_err) { + *rx_err = m_err_cntrs; + return 0; +} + void CRxCoreStateless::set_working_msg_ack(bool val) { sanb_smp_memory_barrier(); m_ack_start_work_msg = val; diff --git a/src/stateless/rx/trex_stateless_rx_core.h b/src/stateless/rx/trex_stateless_rx_core.h index 209dc29f..fc66704e 100644 --- a/src/stateless/rx/trex_stateless_rx_core.h +++ b/src/stateless/rx/trex_stateless_rx_core.h @@ -95,6 +95,25 @@ class CRFC2544Info { uint16_t m_prev_flow_seq; }; +class CRxCoreErrCntrs { + friend CRxCoreStateless; + + public: + uint64_t get_bad_header() {return m_bad_header;} + uint64_t get_old_flow() {return m_old_flow;} + CRxCoreErrCntrs() { + reset(); + } + void reset() { + m_bad_header = 0; + m_old_flow = 0; + } + + private: + uint64_t m_bad_header; + uint64_t m_old_flow; +}; + class CRxCoreStateless { enum state_e { STATE_IDLE, @@ -109,7 +128,11 @@ class CRxCoreStateless { int get_rx_stats(uint8_t port_id, rx_per_flow_t *rx_stats, int min, int max, bool reset , TrexPlatformApi::driver_stat_cap_e type); int get_rfc2544_info(rfc2544_info_t *rfc2544_info, int min, int max, bool reset); - void work() {m_state = STATE_WORKING;} + int get_rx_err_cntrs(CRxCoreErrCntrs *rx_err); + void work() { + m_state = STATE_WORKING; + m_err_cntrs.reset(); // When starting to work, reset global counters + } void idle() {m_state = STATE_IDLE;} void quit() {m_state = STATE_QUIT;} bool is_working() const {return (m_ack_start_work_msg == true);} @@ -146,6 +169,7 @@ class CRxCoreStateless { CCpuUtlCp m_cpu_cp_u; // Used for acking "work" (go out of idle) messages from cp volatile bool m_ack_start_work_msg __rte_cache_aligned; + CRxCoreErrCntrs m_err_cntrs; CRFC2544Info m_rfc2544[MAX_FLOW_STATS_PAYLOAD]; }; #endif diff --git a/src/trex_client_config.cpp b/src/trex_client_config.cpp new file mode 100644 index 00000000..b56037ea --- /dev/null +++ b/src/trex_client_config.cpp @@ -0,0 +1,235 @@ +/* + Itay Marom + Cisco Systems, Inc. +*/ + +/* +Copyright (c) 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 <stdexcept> +#include <sstream> +#include <fstream> + +#include <yaml-cpp/yaml.h> + +#include "utl_yaml.h" +#include "trex_client_config.h" +#include "common/basic_utils.h" +#include "bp_sim.h" + +void +ClientCfgEntry::dump() const { + + std::cout << "IP start: " << ip_to_str(m_ip_start) << "\n"; + std::cout << "IP end: " << ip_to_str(m_ip_end) << "\n"; + + //m_cfg.dump(); + + #if 0 + std::cout << "Init. MAC addr: "; + for (int i = 0; i < 6; i++) { + printf("%lx:", ( (m_initiator.m_dst_mac >> ( (6-i) * 8)) & 0xFF ) ); + } + std::cout << "\n"; + + std::cout << "Init. VLAN: " << m_initiator.m_vlan << "\n"; + + std::cout << "Res. MAC addr: "; + for (int i = 0; i < 6; i++) { + printf("%lx:", ( (m_responder.m_dst_mac >> ( (6-i) * 8)) & 0xFF ) ); + } + std::cout << "\n"; + + std::cout << "Res. VLAN: " << m_responder.m_vlan << "\n"; + #endif +} + + +/** + * loads a YAML file containing + * the client groups configuration + * + */ +void +ClientCfgDB::load_yaml_file(const std::string &filename) { + std::stringstream ss; + + m_groups.clear(); + m_cache_group = NULL; + + /* wrapper parser */ + YAML::Node root; + YAMLParserWrapper parser(filename); + parser.load(root); + + /* parse globals */ + m_under_vlan = parser.parse_bool(root, "vlan"); + + const YAML::Node &groups = parser.parse_list(root, "groups"); + + /* parse each group */ + for (int i = 0; i < groups.size(); i++) { + parse_single_group(parser, groups[i]); + } + + verify(parser); + + m_is_empty = false; + +} + +/** + * reads a single group of clients from YAML + * + */ +void +ClientCfgDB::parse_single_group(YAMLParserWrapper &parser, const YAML::Node &node) { + ClientCfgEntry group; + + /* ip_start */ + group.m_ip_start = parser.parse_ip(node, "ip_start"); + + /* ip_end */ + group.m_ip_end = parser.parse_ip(node, "ip_end"); + + /* sanity check */ + if (group.m_ip_end < group.m_ip_start) { + parser.parse_err("ip_end must be >= ip_start", node); + } + + const YAML::Node &init = parser.parse_map(node, "initiator"); + const YAML::Node &resp = parser.parse_map(node, "responder"); + + parse_dir(parser, init, group.m_cfg.m_initiator); + parse_dir(parser, resp, group.m_cfg.m_responder); + + + group.m_count = parser.parse_uint(node, "count", 0, UINT64_MAX, 1); + + /* add to map with copying */ + m_groups[group.m_ip_start] = group; + +} + +void +ClientCfgDB::parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDir &dir) { + if (node.FindValue("src_mac")) { + dir.set_dst_mac_addr(parser.parse_mac_addr(node, "src_mac")); + } + + if (node.FindValue("dst_mac")) { + dir.set_dst_mac_addr(parser.parse_mac_addr(node, "dst_mac")); + } + + if (m_under_vlan) { + dir.set_vlan(parser.parse_uint(node, "vlan", 0, 0xfff)); + } else { + if (node.FindValue("vlan")) { + parser.parse_err("VLAN config was disabled", node["vlan"]); + } + } +} + +/** + * sanity checks + * + * @author imarom (28-Jun-16) + */ +void +ClientCfgDB::verify(const YAMLParserWrapper &parser) const { + std::stringstream ss; + uint32_t monotonic = 0; + + /* check that no interval overlaps */ + + /* all intervals do not overloap iff when sorted each start/end dots are strong monotonic */ + for (const auto &p : m_groups) { + const ClientCfgEntry &group = p.second; + + if ( (monotonic > 0 ) && (group.m_ip_start <= monotonic) ) { + ss << "IP '" << ip_to_str(group.m_ip_start) << "' - '" << ip_to_str(group.m_ip_end) << "' overlaps with other groups"; + parser.parse_err(ss.str()); + } + + monotonic = group.m_ip_end; + } +} + +/** + * lookup function + * should be fast + * + */ +ClientCfgEntry * +ClientCfgDB::lookup(uint32_t ip) { + + /* a cache to avoid constant search (usually its a range of IPs) */ + if ( (m_cache_group) && (m_cache_group->contains(ip)) ) { + return m_cache_group; + } + + /* clear the cache pointer */ + m_cache_group = NULL; + + std::map<uint32_t ,ClientCfgEntry>::iterator it; + + /* upper bound fetchs the first greater element */ + it = m_groups.upper_bound(ip); + + /* if the first element in the map is bigger - its not in the map */ + if (it == m_groups.begin()) { + return NULL; + } + + /* go one back - we know it's not on begin so we have at least one back */ + it--; + + ClientCfgEntry &group = (*it).second; + + /* because this is a non overlapping intervals + if IP exists it must be in this group + because if it exists in some other previous group + its end_range >= ip so start_range > ip + and so start_range is the upper_bound + */ + if (group.contains(ip)) { + /* save as cache */ + m_cache_group = &group; + return &group; + } else { + return NULL; + } + +} + +/** + * for convenience - search by IP as string + * + * @author imarom (28-Jun-16) + * + * @param ip + * + * @return ClientCfgEntry* + */ +ClientCfgEntry * +ClientCfgDB::lookup(const std::string &ip) { + uint32_t addr = (uint32_t)inet_addr(ip.c_str()); + addr = PKT_NTOHL(addr); + + return lookup(addr); +} + + diff --git a/src/trex_client_config.h b/src/trex_client_config.h new file mode 100644 index 00000000..a5bb83b3 --- /dev/null +++ b/src/trex_client_config.h @@ -0,0 +1,265 @@ +/* + Itay Marom + Cisco Systems, Inc. +*/ + +/* +Copyright (c) 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 __TREX_CLIENT_CONFIG_H__ +#define __TREX_CLIENT_CONFIG_H__ + +#include <stdint.h> +#include <string> +#include <map> + +class YAMLParserWrapper; + + +/** + * client configuration per direction + * + * @author imarom (29-Jun-16) + */ +class ClientCfgDir { + +private: + enum { + HAS_SRC_MAC = 0x1, + HAS_DST_MAC = 0x2, + HAS_VLAN = 0x4, + }; + + uint8_t m_src_mac[6]; + uint8_t m_dst_mac[6]; + uint16_t m_vlan; + uint8_t m_bitfield; + + +public: + ClientCfgDir() { + m_bitfield = 0; + } + + bool has_src_mac_addr() const { + return (m_bitfield & HAS_SRC_MAC); + } + + bool has_dst_mac_addr() const { + return (m_bitfield & HAS_DST_MAC); + } + bool has_vlan() const { + return (m_bitfield & HAS_VLAN); + } + + void set_src_mac_addr(uint64_t mac_addr) { + for (int i = 0; i < 6; i++) { + m_src_mac[i] = ( mac_addr >> ((5 - i) * 8) ) & 0xFF; + } + m_bitfield |= HAS_SRC_MAC; + } + + void set_dst_mac_addr(uint64_t mac_addr) { + for (int i = 0; i < 6; i++) { + m_dst_mac[i] = ( mac_addr >> ((5 - i) * 8) ) & 0xFF; + } + m_bitfield |= HAS_DST_MAC; + } + + void set_vlan(uint16_t vlan_id) { + m_vlan = vlan_id; + m_bitfield |= HAS_VLAN; + } + + /* updates a configuration with a group index member */ + + void update(uint32_t index) { + if (has_src_mac_addr()) { + mac_add(m_src_mac, index); + } + + if (has_dst_mac_addr()) { + mac_add(m_dst_mac, index); + } + } + + const uint8_t *get_src_mac_addr() const { + assert(has_src_mac_addr()); + return m_src_mac; + } + + const uint8_t *get_dst_mac_addr() const { + assert(has_dst_mac_addr()); + return m_dst_mac; + } + + uint16_t get_vlan() const { + assert(has_vlan()); + return m_vlan; + } + +private: + /** + * transform MAC address to uint64_t + * performs add and return to MAC format + * + */ + void mac_add(uint8_t *mac, uint32_t i) { + uint64_t tmp = 0; + + for (int i = 0; i < 6; i++) { + tmp <<= 8; + tmp |= mac[i]; + } + + tmp += i; + + for (int i = 0; i < 6; i++) { + mac[i] = ( tmp >> ((5 - i) * 8) ) & 0xFF; + } + + } +}; + +/** + * single client config + * + */ +class ClientCfg { + +public: + + void update(uint32_t index) { + m_initiator.update(index); + m_responder.update(index); + } + + ClientCfgDir m_initiator; + ClientCfgDir m_responder; +}; + +/******************************** internal section ********************************/ + +/** + * describes a single client config + * entry loaded from the config file + * + */ +class ClientCfgEntry { + +public: + + ClientCfgEntry() { + reset(); + } + + + void dump() const; + + bool contains(uint32_t ip) const { + return ( (ip >= m_ip_start) && (ip <= m_ip_end) ); + } + + void reset() { + m_iterator = 0; + } + + + /** + * assings a client config from the group + * it will advance MAC addresses andf etc. + * + * @author imarom (27-Jun-16) + * + * @param info + */ + void assign(ClientCfg &info) { + info = m_cfg; + info.update(m_iterator); + + /* advance for the next assign */ + m_iterator = (m_iterator + 1) % m_count; + } + +public: + uint32_t m_ip_start; + uint32_t m_ip_end; + + ClientCfg m_cfg; + + uint32_t m_count; + +private: + uint32_t m_iterator; +}; + +/** + * holds all the configured clients groups + * + */ +class ClientCfgDB { +public: + + ClientCfgDB() { + m_is_empty = true; + m_cache_group = NULL; + m_under_vlan = false; + } + + /** + * if no config file was loaded + * this should return true + * + */ + bool is_empty() { + return m_is_empty; + } + + /** + * loads a YAML file + * configuration will be built + * according to the YAML config + * + */ + void load_yaml_file(const std::string &filename); + + /** + * lookup for a specific IP address for + * a group that contains this IP + * + */ + ClientCfgEntry * lookup(uint32_t ip); + ClientCfgEntry * lookup(const std::string &ip); + +private: + void parse_single_group(YAMLParserWrapper &parser, const YAML::Node &node); + void parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDir &dir); + + /** + * verify the YAML file loaded in valid + * + */ + void verify(const YAMLParserWrapper &parser) const; + + /* maps the IP start value to client groups */ + std::map<uint32_t, ClientCfgEntry> m_groups; + bool m_under_vlan; + + ClientCfgEntry *m_cache_group; + bool m_is_empty; +}; + +#endif /* __TREX_CLIENT_CONFIG_H__ */ + diff --git a/src/trex_defs.h b/src/trex_defs.h index 665c1edc..9abb38f5 100644 --- a/src/trex_defs.h +++ b/src/trex_defs.h @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ #include <set> +#include <queue> +#include <vector> #ifndef __TREX_DEFS_H__ #define __TREX_DEFS_H__ @@ -33,8 +35,13 @@ limitations under the License. #define UINT16_MAX 0xFFFF #endif +#ifndef UINT64_MAX + #define UINT64_MAX 0xFFFFFFFFFFFFFFFF +#endif typedef std::set<uint32_t> flow_stat_active_t; typedef std::set<uint32_t>::iterator flow_stat_active_it_t; +typedef std::vector<std::vector<uint8_t>> cpu_util_full_t; +typedef std::vector<uint8_t> cpu_vct_t; #endif diff --git a/src/trex_watchdog.cpp b/src/trex_watchdog.cpp index d099933b..88711b7a 100644 --- a/src/trex_watchdog.cpp +++ b/src/trex_watchdog.cpp @@ -92,7 +92,7 @@ std::string Backtrace(int skip = 1) /* add the addr2line info */ std::stringstream addr2line; - addr2line << "/usr/bin/addr2line -e " << get_exe_name() << " "; + addr2line << "/usr/bin/addr2line -s -e " << get_exe_name() << " "; for (int i = skip; i < nFrames; i++) { addr2line << callstack[i] << " "; } diff --git a/src/trex_watchdog.h b/src/trex_watchdog.h index 1c948d56..af9a3993 100644 --- a/src/trex_watchdog.h +++ b/src/trex_watchdog.h @@ -117,15 +117,12 @@ private: /* write fields are first */ volatile bool m_active; volatile bool m_tickled; - dsec_t m_ts; - int m_handle; + dsec_t m_ts; double m_timeout_sec; pthread_t m_tid; std::string m_name; - /* for for a full cacheline */ - uint8_t pad[15]; } __rte_cache_aligned; @@ -203,6 +200,5 @@ private: static bool g_signal_init; }; -static_assert(sizeof(TrexMonitor) == RTE_CACHE_LINE_SIZE, "sizeof(TrexMonitor) != RTE_CACHE_LINE_SIZE" ); #endif /* __TREX_WATCHDOG_H__ */ diff --git a/src/tuple_gen.cpp b/src/tuple_gen.cpp index d221a4d9..6861b73f 100755 --- a/src/tuple_gen.cpp +++ b/src/tuple_gen.cpp @@ -25,6 +25,7 @@ limitations under the License. #include "tuple_gen.h" #include <string.h> #include "utl_yaml.h" +#include "bp_sim.h" void CServerPool::Create(IP_DIST_t dist_value, uint32_t min_ip, @@ -52,97 +53,117 @@ void CServerPool::Create(IP_DIST_t dist_value, -void CClientPool::Create(IP_DIST_t dist_value, - uint32_t min_ip, - uint32_t max_ip, - double l_flow, - double t_cps, - CFlowGenListMac* mac_info, - bool has_mac_map, - uint16_t tcp_aging, - uint16_t udp_aging) { - assert(max_ip>=min_ip); +void CClientPool::Create(IP_DIST_t dist_value, + uint32_t min_ip, + uint32_t max_ip, + double l_flow, + double t_cps, + ClientCfgDB &client_info, + uint16_t tcp_aging, + uint16_t udp_aging) { + + assert(max_ip >= min_ip); + set_dist(dist_value); - uint32_t total_ip = max_ip - min_ip +1; - uint32_t avail_ip = total_ip; - if (has_mac_map && (mac_info!=NULL)) { - for(int idx=0;idx<total_ip;idx++){ - mac_addr_align_t *mac_adr = NULL; - mac_adr = mac_info->get_mac_addr_by_ip(min_ip+idx); - if (mac_adr == NULL) { - avail_ip--; - } - } - } - if (avail_ip!=0) { - m_ip_info.resize(avail_ip); + + uint32_t total_ip = max_ip - min_ip +1; + bool is_long_range = total_ip > (l_flow * t_cps / MAX_PORT); + + m_ip_info.resize(total_ip); + + /* if client info is empty - flat allocation o.w use configured clients */ + if (client_info.is_empty()) { + allocate_simple_clients(min_ip, total_ip, is_long_range); } else { - printf("\n Error, invalid mac file is configured.\n"); - assert(0); + allocate_configured_clients(min_ip, total_ip, is_long_range, client_info); } - int skip_cnt=0; - if (total_ip > ((l_flow*t_cps/MAX_PORT))) { - if (has_mac_map) { - skip_cnt=0; - for(int idx=0;idx<total_ip;idx++){ - mac_addr_align_t *mac_adr = NULL; - mac_adr = mac_info->get_mac_addr_by_ip( min_ip+idx); - if (mac_adr != NULL) { - m_ip_info[idx-skip_cnt] = new CClientInfoL(has_mac_map); - m_ip_info[idx-skip_cnt]->set_ip(min_ip+idx); - m_ip_info[idx-skip_cnt]->set_mac(mac_adr); - } else { - skip_cnt++; - } - } + m_tcp_aging = tcp_aging; + m_udp_aging = udp_aging; + + CreateBase(); +} + +/** + * simple allocation of a client - no configuration was provided + * + * @author imarom (27-Jun-16) + * + * @param ip + * @param index + * @param is_long_range + */ +void CClientPool::allocate_simple_clients(uint32_t min_ip, + uint32_t total_ip, + bool is_long_range) { + + /* simple creation of clients - no extended info */ + for (uint32_t i = 0; i < total_ip; i++) { + uint32_t ip = min_ip + i; + if (is_long_range) { + m_ip_info[i] = new CSimpleClientInfo<CIpInfoL>(ip); } else { - for(int idx=0;idx<total_ip;idx++){ - m_ip_info[idx] = new CClientInfoL(has_mac_map); - m_ip_info[idx]->set_ip(min_ip+idx); - } - } - } else { - if (has_mac_map) { - skip_cnt=0; - for(int idx=0;idx<total_ip;idx++){ - mac_addr_align_t *mac_adr = NULL; - mac_adr = mac_info->get_mac_addr_by_ip(min_ip+idx); - if (mac_adr != NULL) { - m_ip_info[idx-skip_cnt] = new CClientInfo(has_mac_map); - m_ip_info[idx-skip_cnt]->set_ip(min_ip+idx); - m_ip_info[idx-skip_cnt]->set_mac(mac_adr); - } else { - skip_cnt++; - } - } + m_ip_info[i] = new CSimpleClientInfo<CIpInfo>(ip); + } + } + +} + +/** + * simple allocation of a client - no configuration was provided + * + * @author imarom (27-Jun-16) + * + * @param ip + * @param index + * @param is_long_range + */ +void CClientPool::allocate_configured_clients(uint32_t min_ip, + uint32_t total_ip, + bool is_long_range, + ClientCfgDB &client_info) { + + for (uint32_t i = 0; i < total_ip; i++) { + uint32_t ip = min_ip + i; + + /* lookup for the right group of clients */ + ClientCfgEntry *group = client_info.lookup(ip); + if (!group) { + std::stringstream ss; + ss << "client configuration error: could not map IP '" << ip_to_str(ip) << "' to a group\n"; + throw std::runtime_error(ss.str()); + } + + ClientCfg info; + group->assign(info); + + if (is_long_range) { + m_ip_info[i] = new CConfiguredClientInfo<CIpInfoL>(ip, info); } else { - for(int idx=0;idx<total_ip;idx++){ - m_ip_info[idx] = new CClientInfo(has_mac_map); - m_ip_info[idx]->set_ip(min_ip+idx); - } - } - + m_ip_info[i] = new CConfiguredClientInfo<CIpInfo>(ip, info); + } } - m_tcp_aging = tcp_aging; - m_udp_aging = udp_aging; - CreateBase(); } -bool CTupleGeneratorSmart::add_client_pool(IP_DIST_t client_dist, - uint32_t min_client, - uint32_t max_client, - double l_flow, - double t_cps, - CFlowGenListMac* mac_info, - uint16_t tcp_aging, - uint16_t udp_aging){ +bool CTupleGeneratorSmart::add_client_pool(IP_DIST_t client_dist, + uint32_t min_client, + uint32_t max_client, + double l_flow, + double t_cps, + ClientCfgDB &client_info, + uint16_t tcp_aging, + uint16_t udp_aging) { assert(max_client>=min_client); CClientPool* pool = new CClientPool(); - pool->Create(client_dist, min_client, max_client, - l_flow, t_cps, mac_info, m_has_mac_mapping, - tcp_aging, udp_aging); + pool->Create(client_dist, + min_client, + max_client, + l_flow, + t_cps, + client_info, + tcp_aging, + udp_aging); m_client_pool.push_back(pool); return(true); @@ -170,19 +191,17 @@ bool CTupleGeneratorSmart::add_server_pool(IP_DIST_t server_dist, bool CTupleGeneratorSmart::Create(uint32_t _id, - uint32_t thread_id, - bool has_mac) + uint32_t thread_id) + { m_thread_id = thread_id; m_id = _id; m_was_init=true; - m_has_mac_mapping = has_mac; return(true); } void CTupleGeneratorSmart::Delete(){ m_was_init=false; - m_has_mac_mapping = false; for (int idx=0;idx<m_client_pool.size();idx++) { m_client_pool[idx]->Delete(); diff --git a/src/tuple_gen.h b/src/tuple_gen.h index b2f6e34a..2491f489 100755 --- a/src/tuple_gen.h +++ b/src/tuple_gen.h @@ -37,15 +37,17 @@ limitations under the License. #include "common/c_common.h" #include <bitset> #include <yaml-cpp/yaml.h> -#include <mac_mapping.h> +#include "trex_client_config.h" #include <random> class CTupleBase { public: + CTupleBase() { - m_client_mac.inused = UNUSED; + m_client_cfg = NULL; } + uint32_t getClient() { return m_client_ip; } @@ -83,22 +85,20 @@ public: void setClientPort(uint16_t port) { m_client_port = port; } - mac_addr_align_t* getClientMac() { - return &m_client_mac; + void setClientCfg(ClientCfg *cfg) { + m_client_cfg = cfg; } - void setClientMac(mac_addr_align_t* mac_info) { - if (mac_info != NULL) { - memcpy(&m_client_mac, mac_info, sizeof(mac_addr_align_t)); - m_client_mac.inused = INUSED; - } else { - m_client_mac.inused = UNUSED; - } + ClientCfg *getClientCfg() { + return m_client_cfg; } - void setClientTuple(uint32_t ip,mac_addr_align_t*mac,uint16_t port) { + + + void setClientTuple(uint32_t ip, ClientCfg *cfg, uint16_t port) { setClient(ip); - setClientMac(mac); setClientPort(port); + setClientCfg(cfg); } + void setClientAll2(uint32_t id, uint32_t ip,uint16_t port) { setClientId(id); setClient(ip); @@ -121,9 +121,12 @@ public: private: uint32_t m_client_ip; uint32_t m_client_idx; + uint32_t m_server_ip; uint32_t m_server_idx; - mac_addr_align_t m_client_mac; + + ClientCfg *m_client_cfg; + uint16_t m_client_port; uint16_t m_server_port; }; @@ -171,8 +174,6 @@ typedef enum { class CIpInfoBase { public: - virtual mac_addr_align_t* get_mac() { return NULL;} - virtual void set_mac(mac_addr_align_t*){;} virtual uint16_t get_new_free_port() = 0; virtual void return_port(uint16_t a) = 0; virtual void generate_tuple(CTupleBase & tuple) = 0; @@ -303,76 +304,54 @@ class CIpInfo : public CIpInfoBase { } }; -class CClientInfo : public CIpInfo { + +/** + * a flat client info (no configuration) + * + * using template to avoid duplicating the code for CIpInfo and + * CIpInfoL + * + * @author imarom (27-Jun-16) + */ +template <typename T> +class CSimpleClientInfo : public T { + public: - CClientInfo (bool has_mac) { - if (has_mac==true) { - m_mac = new mac_addr_align_t(); - } else { - m_mac = NULL; - } - } - CClientInfo () { - m_mac = NULL; - } + CSimpleClientInfo(uint32_t ip) { + T::set_ip(ip); + } - mac_addr_align_t* get_mac() { - return m_mac; - } - void set_mac(mac_addr_align_t *mac) { - memcpy(m_mac, mac, sizeof(mac_addr_align_t)); - } - ~CClientInfo() { - if (m_mac!=NULL){ - delete m_mac; - m_mac=NULL; - } - } - - void generate_tuple(CTupleBase & tuple) { - tuple.setClientTuple(m_ip, m_mac, - get_new_free_port()); + void generate_tuple(CTupleBase &tuple) { + tuple.setClientTuple(T::m_ip, + NULL, + T::get_new_free_port()); } -private: - mac_addr_align_t *m_mac; }; -class CClientInfoL : public CIpInfoL { -public: - CClientInfoL (bool has_mac) { - if (has_mac==true) { - m_mac = new mac_addr_align_t(); - } else { - m_mac = NULL; - } - } - CClientInfoL () { - m_mac = NULL; - } +/** + * a configured client object + * + * @author imarom (26-Jun-16) + */ +template <typename T> +class CConfiguredClientInfo : public T { - mac_addr_align_t* get_mac() { - return m_mac; +public: + CConfiguredClientInfo(uint32_t ip, const ClientCfg &cfg) : m_cfg(cfg) { + T::set_ip(ip); } - void set_mac(mac_addr_align_t *mac) { - memcpy(m_mac, mac, sizeof(mac_addr_align_t)); + void generate_tuple(CTupleBase &tuple) { + tuple.setClientTuple(T::m_ip, + &m_cfg, + T::get_new_free_port()); } - ~CClientInfoL() { - if (m_mac!=NULL){ - delete m_mac; - m_mac=NULL; - } - } - - void generate_tuple(CTupleBase & tuple) { - tuple.setClientTuple(m_ip, m_mac, - get_new_free_port()); - } private: - mac_addr_align_t *m_mac; + ClientCfg m_cfg; }; + class CServerInfo : public CIpInfo { void generate_tuple(CTupleBase & tuple) { tuple.setServer(m_ip); @@ -475,12 +454,6 @@ class CIpPool { client->return_port(port); } - mac_addr_align_t * get_curr_mac() { - return m_ip_info[m_cur_idx]->get_mac(); - } - mac_addr_align_t *get_mac(uint32_t idx) { - return m_ip_info[idx]->get_mac(); - } public: std::vector<CIpInfoBase*> m_ip_info; @@ -536,19 +509,30 @@ public: uint16_t get_udp_aging() { return m_udp_aging; } - void Create(IP_DIST_t dist_value, - uint32_t min_ip, - uint32_t max_ip, - double l_flow, - double t_cps, - CFlowGenListMac* mac_info, - bool has_mac_map, - uint16_t tcp_aging, - uint16_t udp_aging); + + void Create(IP_DIST_t dist_value, + uint32_t min_ip, + uint32_t max_ip, + double l_flow, + double t_cps, + ClientCfgDB &client_info, + uint16_t tcp_aging, + uint16_t udp_aging); public: uint16_t m_tcp_aging; uint16_t m_udp_aging; + +private: + void allocate_simple_clients(uint32_t min_ip, + uint32_t total_ip, + bool is_long_range); + + void allocate_configured_clients(uint32_t min_ip, + uint32_t total_ip, + bool is_long_range, + ClientCfgDB &client_info); + }; class CServerPoolBase { @@ -685,10 +669,9 @@ public: public: CTupleGeneratorSmart(){ m_was_init=false; - m_has_mac_mapping = false; } - bool Create(uint32_t _id, - uint32_t thread_id, bool has_mac=false); + + bool Create(uint32_t _id, uint32_t thread_id); void Delete(); @@ -704,20 +687,22 @@ public: return (total_alloc_error); } - bool add_client_pool(IP_DIST_t client_dist, - uint32_t min_client, - uint32_t max_client, - double l_flow, - double t_cps, - CFlowGenListMac* mac_info, - uint16_t tcp_aging, - uint16_t udp_aging); + bool add_client_pool(IP_DIST_t client_dist, + uint32_t min_client, + uint32_t max_client, + double l_flow, + double t_cps, + ClientCfgDB &client_info, + uint16_t tcp_aging, + uint16_t udp_aging); + bool add_server_pool(IP_DIST_t server_dist, - uint32_t min_server, - uint32_t max_server, - double l_flow, - double t_cps, - bool is_bundling); + uint32_t min_server, + uint32_t max_server, + double l_flow, + double t_cps, + bool is_bundling); + CClientPool* get_client_pool(uint8_t idx) { return m_client_pool[idx]; } @@ -736,7 +721,6 @@ private: std::vector<CClientPool*> m_client_pool; std::vector<CServerPoolBase*> m_server_pool; bool m_was_init; - bool m_has_mac_mapping; }; class CTupleTemplateGeneratorSmart { diff --git a/src/utl_cpuu.cpp b/src/utl_cpuu.cpp index 4d75cf6f..7786356e 100755 --- a/src/utl_cpuu.cpp +++ b/src/utl_cpuu.cpp @@ -25,7 +25,9 @@ limitations under the License. void CCpuUtlCp::Create(CCpuUtlDp * cdp){ m_dpcpu=cdp; - m_cpu_util=0.0; + memset(m_cpu_util, 0, sizeof(m_cpu_util)); + m_history_latest_index = 0; + m_cpu_util_lpf=0.0; m_ticks=0; m_work=0; } @@ -41,17 +43,34 @@ void CCpuUtlCp::Update(){ m_work++; } if (m_ticks==100) { - double window_cpu_u = ((double)m_work/(double)m_ticks); /* LPF*/ - m_cpu_util = (m_cpu_util*0.75)+(window_cpu_u*0.25); + m_cpu_util_lpf = (m_cpu_util_lpf*0.75)+((double)m_work*0.25); + AppendHistory(m_work); m_ticks=0; m_work=0; - } } -/* return cpu % */ +/* return cpu % Smoothed */ double CCpuUtlCp::GetVal(){ - return (m_cpu_util*100); + return (m_cpu_util_lpf); +} + +/* return cpu % Raw */ +uint8_t CCpuUtlCp::GetValRaw(){ + return (m_cpu_util[m_history_latest_index]); } +/* get cpu % utilization history */ +void CCpuUtlCp::GetHistory(cpu_vct_t &cpu_vct){ + cpu_vct.clear(); + for (int i = m_history_latest_index + m_history_size; i > m_history_latest_index; i--) { + cpu_vct.push_back(m_cpu_util[i % m_history_size]); + } +} + +/* save last CPU % util in history */ +void CCpuUtlCp::AppendHistory(uint8_t val){ + m_history_latest_index = (m_history_latest_index + 1) % m_history_size; + m_cpu_util[m_history_latest_index] = val; +} diff --git a/src/utl_cpuu.h b/src/utl_cpuu.h index e5305783..109fff4f 100755 --- a/src/utl_cpuu.h +++ b/src/utl_cpuu.h @@ -22,6 +22,8 @@ limitations under the License. */ #include <stdint.h> +#include <cstring> +#include "trex_defs.h" #include "os_time.h" #include "mbuf.h" @@ -56,13 +58,18 @@ public: void Update(); /* return cpu % */ double GetVal(); - + uint8_t GetValRaw(); + void GetHistory(cpu_vct_t &cpu_vct); private: - CCpuUtlDp * m_dpcpu; - uint16_t m_ticks; - uint16_t m_work; + void AppendHistory(uint8_t); + CCpuUtlDp * m_dpcpu; + uint8_t m_ticks; + uint8_t m_work; - double m_cpu_util; + static const int m_history_size=20; + uint8_t m_cpu_util[m_history_size]; // history as cyclic array + uint8_t m_history_latest_index; + double m_cpu_util_lpf; }; #endif diff --git a/src/utl_yaml.cpp b/src/utl_yaml.cpp index 828817e4..8352e887 100755 --- a/src/utl_yaml.cpp +++ b/src/utl_yaml.cpp @@ -21,7 +21,9 @@ See the License for the specific language governing permissions and limitations under the License. */ - +#include <istream> +#include <fstream> +#include "common/basic_utils.h" #define INADDRSZ 4 @@ -65,8 +67,8 @@ static int my_inet_pton4(const char *src, unsigned char *dst) bool utl_yaml_read_ip_addr(const YAML::Node& node, - std::string name, - uint32_t & val){ + const std::string &name, + uint32_t & val){ std::string tmp; uint32_t ip; bool res=false; @@ -83,8 +85,10 @@ bool utl_yaml_read_ip_addr(const YAML::Node& node, return (res); } + + bool utl_yaml_read_uint32(const YAML::Node& node, - std::string name, + const std::string &name, uint32_t & val){ bool res=false; if ( node.FindValue(name) ) { @@ -95,8 +99,8 @@ bool utl_yaml_read_uint32(const YAML::Node& node, } bool utl_yaml_read_uint16(const YAML::Node& node, - std::string name, - uint16_t & val){ + const std::string &name, + uint16_t & val){ uint32_t val_tmp; bool res=false; if ( node.FindValue(name) ) { @@ -108,15 +112,250 @@ bool utl_yaml_read_uint16(const YAML::Node& node, return (res); } -bool utl_yaml_read_bool(const YAML::Node& node, - std::string name, - bool & val){ - bool res=false; - if ( node.FindValue(name) ) { - node[name] >> val ; - res=true; +static void +split_str_by_delimiter(std::string str, char delim, std::vector<std::string> &tokens) { + size_t pos = 0; + std::string token; + + while ((pos = str.find(delim)) != std::string::npos) { + token = str.substr(0, pos); + tokens.push_back(token); + str.erase(0, pos + 1); + } + + if (str.size() > 0) { + tokens.push_back(str); + } +} + +static bool mac2uint64(const std::string &mac_str, uint64_t &mac_num) { + std::vector<std::string> tokens; + uint64_t val; + + split_str_by_delimiter(mac_str, ':', tokens); + if (tokens.size() != 6) { + return false; + } + + val = 0; + + for (int i = 0; i < 6 ; i++) { + char *endptr = NULL; + unsigned long octet = strtoul(tokens[i].c_str(), &endptr, 16); + + if ( (*endptr != 0) || (octet > 0xff) ) { + return false; + } + + val = (val << 8) + octet; } - return( res); + + mac_num = val; + + return true; } +/************************ + * YAML Parser Wrapper + * + ***********************/ +void +YAMLParserWrapper::load(YAML::Node &root) { + std::stringstream ss; + + /* first check file exists */ + if (!utl_is_file_exists(m_filename)){ + ss << "file '" << m_filename << "' does not exists"; + throw std::runtime_error(ss.str()); + } + + std::ifstream fin(m_filename); + + try { + YAML::Parser base_parser(fin); + base_parser.GetNextDocument(root); + + } catch (const YAML::Exception &e) { + parse_err(e.what()); + } +} + +bool +YAMLParserWrapper::parse_bool(const YAML::Node &node, const std::string &name, bool def) { + if (!node.FindValue(name)) { + return def; + } + + return parse_bool(node, name); +} + +bool +YAMLParserWrapper::parse_bool(const YAML::Node &node, const std::string &name) { + try { + bool val; + node[name] >> val; + return (val); + + } catch (const YAML::InvalidScalar &ex) { + parse_err("expecting true/false for field '" + name + "'", node[name]); + + } catch (const YAML::KeyNotFound &ex) { + parse_err("cannot locate mandatory field '" + name + "'", node); + } + + assert(0); +} + +const YAML::Node & +YAMLParserWrapper::parse_list(const YAML::Node &node, const std::string &name) { + + try { + const YAML::Node &val = node[name]; + if (val.Type() != YAML::NodeType::Sequence) { + throw YAML::InvalidScalar(node[name].GetMark()); + } + return val; + + } catch (const YAML::InvalidScalar &ex) { + parse_err("expecting sequence/list for field '" + name + "'", node[name]); + + } catch (const YAML::KeyNotFound &ex) { + parse_err("cannot locate mandatory field '" + name + "'", node); + } + + assert(0); +} + +const YAML::Node & +YAMLParserWrapper::parse_map(const YAML::Node &node, const std::string &name) { + + try { + const YAML::Node &val = node[name]; + if (val.Type() != YAML::NodeType::Map) { + throw YAML::InvalidScalar(node[name].GetMark()); + } + return val; + + } catch (const YAML::InvalidScalar &ex) { + parse_err("expecting map for field '" + name + "'", node[name]); + } catch (const YAML::KeyNotFound &ex) { + parse_err("cannot locate mandatory field '" + name + "'", node); + } + + assert(0); +} + +uint32_t +YAMLParserWrapper::parse_ip(const YAML::Node &node, const std::string &name) { + + + try { + std::string ip_str; + uint32_t ip_num; + + node[name] >> ip_str; + int rc = my_inet_pton4((char *)ip_str.c_str(), (unsigned char *)&ip_num); + if (!rc) { + parse_err("invalid IP address: " + ip_str, node[name]); + } + + return PKT_NTOHL(ip_num); + + } catch (const YAML::InvalidScalar &ex) { + parse_err("expecting valid IP address for field '" + name + "'", node[name]); + + } catch (const YAML::KeyNotFound &ex) { + parse_err("cannot locate mandatory field '" + name + "'", node); + } + + assert(0); +} + +uint64_t +YAMLParserWrapper::parse_mac_addr(const YAML::Node &node, const std::string &name, uint64_t def) { + if (!node.FindValue(name)) { + return def; + } + + return parse_mac_addr(node, name); +} + +uint64_t +YAMLParserWrapper::parse_mac_addr(const YAML::Node &node, const std::string &name) { + + std::string mac_str; + uint64_t mac_num; + + try { + + node[name] >> mac_str; + bool rc = mac2uint64(mac_str, mac_num); + if (!rc) { + parse_err("invalid MAC address: " + mac_str, node[name]); + } + return mac_num; + + } catch (const YAML::InvalidScalar &ex) { + parse_err("expecting true/false for field '" + name + "'", node[name]); + + } catch (const YAML::KeyNotFound &ex) { + parse_err("cannot locate mandatory field '" + name + "'", node); + } + + assert(0); +} + +uint64_t +YAMLParserWrapper::parse_uint(const YAML::Node &node, const std::string &name, uint64_t low, uint64_t high, uint64_t def) { + if (!node.FindValue(name)) { + return def; + } + + return parse_uint(node, name, low, high); +} + +uint64_t +YAMLParserWrapper::parse_uint(const YAML::Node &node, const std::string &name, uint64_t low, uint64_t high) { + + try { + + uint64_t val; + node[name] >> val; + + if ( (val < low) || (val > high) ) { + std::stringstream ss; + ss << "valid range for field '" << name << "' is: [" << low << " - " << high << "]"; + parse_err(ss.str(), node[name]); + } + + return (val); + + } catch (const YAML::InvalidScalar &ex) { + parse_err("expecting true/false for field '" + name + "'", node[name]); + + } catch (const YAML::KeyNotFound &ex) { + parse_err("cannot locate mandatory field '" + name + "'", node); + } + + assert(0); +} + +void +YAMLParserWrapper::parse_err(const std::string &err, const YAML::Node &node) const { + std::stringstream ss; + + ss << "'" << m_filename << "' - YAML parsing error at line " << node.GetMark().line << ": "; + ss << err; + + throw std::runtime_error(ss.str()); +} + +void +YAMLParserWrapper::parse_err(const std::string &err) const { + std::stringstream ss; + + ss << "'" << m_filename << "' - YAML parsing error: " << err; + + throw std::runtime_error(ss.str()); +} diff --git a/src/utl_yaml.h b/src/utl_yaml.h index 71655488..59104b21 100755 --- a/src/utl_yaml.h +++ b/src/utl_yaml.h @@ -24,23 +24,56 @@ limitations under the License. #include <stdint.h> #include <yaml-cpp/yaml.h> - + +/* static methods - please prefer the wrapper over those */ bool utl_yaml_read_ip_addr(const YAML::Node& node, - std::string name, - uint32_t & val - ); + const std::string &name, + uint32_t & val); bool utl_yaml_read_uint32(const YAML::Node& node, - std::string name, - uint32_t & val); + const std::string &name, + uint32_t & val); + bool utl_yaml_read_uint16(const YAML::Node& node, - std::string name, - uint16_t & val); + const std::string &name, + uint16_t & val); + +/* a thin wrapper to customize errors */ +class YAMLParserWrapper { +public: + /* a header that will start every error message */ + YAMLParserWrapper(const std::string &filename) : m_filename(filename) { + } + + /** + * loads the file (while parsing it) + * + */ + void load(YAML::Node &root); + + /* bool */ + bool parse_bool(const YAML::Node &node, const std::string &name); + bool parse_bool(const YAML::Node &node, const std::string &name, bool def); -bool utl_yaml_read_bool(const YAML::Node& node, - std::string name, - bool & val); + const YAML::Node & parse_list(const YAML::Node &node, const std::string &name); + const YAML::Node & parse_map(const YAML::Node &node, const std::string &name); + uint32_t parse_ip(const YAML::Node &node, const std::string &name); + uint64_t parse_mac_addr(const YAML::Node &node, const std::string &name); + uint64_t parse_mac_addr(const YAML::Node &node, const std::string &name, uint64_t def); + + uint64_t parse_uint(const YAML::Node &node, const std::string &name, uint64_t low = 0, uint64_t high = UINT64_MAX); + uint64_t parse_uint(const YAML::Node &node, const std::string &name, uint64_t low, uint64_t high, uint64_t def); + + +public: + void parse_err(const std::string &err, const YAML::Node &node) const; + void parse_err(const std::string &err) const; + + +private: + std::string m_filename; +}; #endif |