diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/bp_gtest.cpp | 2 | ||||
-rwxr-xr-x | src/bp_sim.cpp | 19 | ||||
-rwxr-xr-x | src/bp_sim.h | 6 | ||||
-rw-r--r-- | src/gtest/trex_stateless_gtest.cpp | 1072 | ||||
-rwxr-xr-x | src/main_dpdk.cpp | 22 | ||||
-rw-r--r-- | src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 44 | ||||
-rw-r--r-- | src/rpc-server/commands/trex_rpc_cmd_test.cpp | 5 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_cmd.cpp | 20 | ||||
-rw-r--r-- | src/rpc-server/trex_rpc_cmd_api.h | 3 | ||||
-rw-r--r-- | src/stateless/cp/trex_stream.cpp | 35 | ||||
-rw-r--r-- | src/stateless/cp/trex_stream.h | 86 | ||||
-rw-r--r-- | src/stateless/cp/trex_stream_vm.cpp | 804 | ||||
-rw-r--r-- | src/stateless/cp/trex_stream_vm.h | 854 | ||||
-rw-r--r-- | src/stateless/cp/trex_streams_compiler.cpp | 37 | ||||
-rw-r--r-- | src/stateless/cp/trex_streams_compiler.h | 9 | ||||
-rw-r--r-- | src/stateless/dp/trex_stateless_dp_core.cpp | 129 | ||||
-rw-r--r-- | src/stateless/dp/trex_stream_node.h | 63 |
17 files changed, 3022 insertions, 188 deletions
diff --git a/src/bp_gtest.cpp b/src/bp_gtest.cpp index 3ef25c9d..005801c4 100755 --- a/src/bp_gtest.cpp +++ b/src/bp_gtest.cpp @@ -2041,7 +2041,7 @@ public: virtual int send_node(CGenNode * node); - virtual int update_mac_addr_from_global_cfg(pkt_dir_t dir, rte_mbuf_t *m){ + virtual int update_mac_addr_from_global_cfg(pkt_dir_t dir, uint8_t * p){ return (0); } diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp index 49cdbd23..081553b5 100755 --- a/src/bp_sim.cpp +++ b/src/bp_sim.cpp @@ -4655,13 +4655,24 @@ int CErfIFStl::send_node(CGenNode * _no_to_use){ if ( m_preview_mode->getFileWrite() ){ CGenNodeStateless * node_sl=(CGenNodeStateless *) _no_to_use; - + + pkt_dir_t dir=(pkt_dir_t)node_sl->get_mbuf_cache_dir(); + /* check that we have mbuf */ rte_mbuf_t * m=node_sl->get_cache_mbuf(); - assert( m ); - pkt_dir_t dir=(pkt_dir_t)node_sl->get_mbuf_cache_dir(); + if (m) { + /* cache packet */ + fill_raw_packet(m,_no_to_use,dir); + /* can't free the m, it is cached*/ + }else{ + + m=node_sl->alloc_node_with_vm(); + assert(m); + fill_raw_packet(m,_no_to_use,dir); + rte_pktmbuf_free(m); + + } - fill_raw_packet(m,_no_to_use,dir); BP_ASSERT(m_writer); bool res=m_writer->write_packet(m_raw); diff --git a/src/bp_sim.h b/src/bp_sim.h index 3d81c759..da8e8780 100755 --- a/src/bp_sim.h +++ b/src/bp_sim.h @@ -381,7 +381,7 @@ public: * * @return */ - virtual int update_mac_addr_from_global_cfg(pkt_dir_t dir, rte_mbuf_t *m)=0; + virtual int update_mac_addr_from_global_cfg(pkt_dir_t dir, uint8_t * p)=0; /** * translate a port_id to the correct dir on the core @@ -1805,7 +1805,7 @@ public: virtual int write_pkt(CCapPktRaw *pkt_raw); virtual int close_file(void); - virtual int update_mac_addr_from_global_cfg(pkt_dir_t dir, rte_mbuf_t *m){ + virtual int update_mac_addr_from_global_cfg(pkt_dir_t dir, uint8_t * p){ return (0); } @@ -1884,7 +1884,7 @@ public: return (0); } - virtual int update_mac_addr_from_global_cfg(pkt_dir_t dir, rte_mbuf_t *m){ + virtual int update_mac_addr_from_global_cfg(pkt_dir_t dir, uint8_t * p){ return (0); } diff --git a/src/gtest/trex_stateless_gtest.cpp b/src/gtest/trex_stateless_gtest.cpp index 566e7ed4..1626ac25 100644 --- a/src/gtest/trex_stateless_gtest.cpp +++ b/src/gtest/trex_stateless_gtest.cpp @@ -32,6 +32,894 @@ limitations under the License. #include <iostream> #include <vector> + + + +class CPcapLoader { +public: + CPcapLoader(); + ~CPcapLoader(); + + +public: + bool load_pcap_file(std::string file,int pkt_id=0); + void update_ip_src(uint32_t ip_addr); + void clone_packet_into_stream(TrexStream * stream); + void dump_packet(); + +public: + bool m_valid; + CCapPktRaw m_raw; + CPacketIndication m_pkt_indication; +}; + +CPcapLoader::~CPcapLoader(){ +} + +bool CPcapLoader::load_pcap_file(std::string cap_file,int pkt_id){ + m_valid=false; + CPacketParser parser; + + CCapReaderBase * lp=CCapReaderFactory::CreateReader((char *)cap_file.c_str(),0); + + if (lp == 0) { + printf(" ERROR file %s does not exist or not supported \n",(char *)cap_file.c_str()); + return false; + } + + int cnt=0; + bool found =false; + + + while ( true ) { + /* read packet */ + if ( lp->ReadPacket(&m_raw) ==false ){ + break; + } + if (cnt==pkt_id) { + found = true; + break; + } + cnt++; + } + if ( found ){ + if ( parser.ProcessPacket(&m_pkt_indication, &m_raw) ){ + m_valid = true; + } + } + + delete lp; + return (m_valid); +} + +void CPcapLoader::update_ip_src(uint32_t ip_addr){ + + if ( m_pkt_indication.l3.m_ipv4 ) { + m_pkt_indication.l3.m_ipv4->setSourceIp(ip_addr); + m_pkt_indication.l3.m_ipv4->updateCheckSum(); + } +} + +void CPcapLoader::clone_packet_into_stream(TrexStream * stream){ + + uint16_t pkt_size=m_raw.getTotalLen(); + + uint8_t *binary = new uint8_t[pkt_size]; + memcpy(binary,m_raw.raw,pkt_size); + stream->m_pkt.binary = binary; + stream->m_pkt.len = pkt_size; +} + + + + +CPcapLoader::CPcapLoader(){ + +} + +void CPcapLoader::dump_packet(){ + if (m_valid ) { + m_pkt_indication.Dump(stdout,1); + }else{ + fprintf(stdout," no packets were found \n"); + } +} + + + + +class basic_vm : public testing::Test { + protected: + virtual void SetUp() { + } + virtual void TearDown() { + } + public: +}; + + + + +TEST_F(basic_vm, pkt_size) { + + EXPECT_EQ(calc_writable_mbuf_size(36,62),62); + EXPECT_EQ(calc_writable_mbuf_size(63,62),62); + EXPECT_EQ(calc_writable_mbuf_size(45,65),65); + EXPECT_EQ(calc_writable_mbuf_size(66,65),65); + EXPECT_EQ(calc_writable_mbuf_size(62,128),128); + EXPECT_EQ(calc_writable_mbuf_size(62,252),61); + EXPECT_EQ(calc_writable_mbuf_size(121,252),120); + EXPECT_EQ(calc_writable_mbuf_size(253,252),252); + EXPECT_EQ(calc_writable_mbuf_size(250,252),252); + EXPECT_EQ(calc_writable_mbuf_size(184,252),183); +} + + +/* start/stop/stop back to back */ +TEST_F(basic_vm, vm0) { + + StreamVm vm; + + vm.add_instruction( new StreamVmInstructionFixChecksumIpv4(20) ); + vm.add_instruction( new StreamVmInstructionFlowMan( "var1",1, + StreamVmInstructionFlowMan::FLOW_VAR_OP_INC,0,1,7 ) + ); + vm.add_instruction( new StreamVmInstructionWriteToPkt( "var1",14, 0,true) + ); + + vm.Dump(stdout); +} + +TEST_F(basic_vm, vm1) { + + StreamVm vm; + + vm.add_instruction( new StreamVmInstructionFlowMan( "var1",1, + StreamVmInstructionFlowMan::FLOW_VAR_OP_INC,0,1,7 ) + ); + vm.add_instruction( new StreamVmInstructionWriteToPkt( "var1",26, 0,true) + ); + vm.add_instruction( new StreamVmInstructionFixChecksumIpv4(14) ); + + vm.set_packet_size(128); + + vm.compile(); + + + uint32_t program_size=vm.get_dp_instruction_buffer()->get_program_size(); + + printf (" program size : %lu \n",(ulong)program_size); + + + vm.Dump(stdout); + +} + +TEST_F(basic_vm, vm2) { + + StreamVm vm; + + vm.add_instruction( new StreamVmInstructionFlowMan( "var1",1, + StreamVmInstructionFlowMan::FLOW_VAR_OP_INC,4,1,7 ) + ); + vm.add_instruction( new StreamVmInstructionWriteToPkt( "var1",26, 0,true) + ); + //vm.add_instruction( new StreamVmInstructionFixChecksumIpv4(14) ); + + vm.set_packet_size(128); + + vm.compile(); + + + uint32_t program_size=vm.get_dp_instruction_buffer()->get_program_size(); + + printf (" program size : %lu \n",(ulong)program_size); + + + vm.Dump(stdout); + + uint8_t test_udp_pkt[14+20+4+4]={ + 0x00,0x00,0x00,0x01,0x00,0x00, + 0x00,0x00,0x00,0x01,0x00,0x00, + 0x08,0x00, + + 0x45,0x00,0x00,0x81, /*14 */ + 0xaf,0x7e,0x00,0x00, /*18 */ + 0x12,0x11,0xd9,0x23, /*22 */ + 0x01,0x01,0x01,0x01, /*26 */ + 0x3d,0xad,0x72,0x1b, /*30 */ + + 0x11,0x11, + 0x11,0x11, + + 0x00,0x6d, + 0x00,0x00, + }; + + + + StreamDPVmInstructionsRunner runner; + + uint8_t ex[]={5, + 6, + 7, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 1, + 2, + 3}; + + int i; + for (i=0; i<20; i++) { + runner.run(program_size, + vm.get_dp_instruction_buffer()->get_program(), + vm.get_bss_ptr(), + test_udp_pkt); + EXPECT_EQ(test_udp_pkt[26],ex[i]); + } + +} + + +TEST_F(basic_vm, vm3) { + + StreamVm vm; + + vm.add_instruction( new StreamVmInstructionFlowMan( "var1",4 /* size */, + StreamVmInstructionFlowMan::FLOW_VAR_OP_INC,4,1,7 ) + ); + vm.add_instruction( new StreamVmInstructionWriteToPkt( "var1",26, 0,true) + ); + //vm.add_instruction( new StreamVmInstructionFixChecksumIpv4(14) ); + + vm.set_packet_size(128); + + vm.compile(); + + + uint32_t program_size=vm.get_dp_instruction_buffer()->get_program_size(); + + printf (" program size : %lu \n",(ulong)program_size); + + + vm.Dump(stdout); + + #define PKT_TEST_SIZE (14+20+4+4) + uint8_t test_udp_pkt[PKT_TEST_SIZE]={ + 0x00,0x00,0x00,0x01,0x00,0x00, + 0x00,0x00,0x00,0x01,0x00,0x00, + 0x08,0x00, + + 0x45,0x00,0x00,0x81, /*14 */ + 0xaf,0x7e,0x00,0x00, /*18 */ + 0x12,0x11,0xd9,0x23, /*22 */ + 0x01,0x01,0x01,0x01, /*26 */ + 0x3d,0xad,0x72,0x1b, /*30 */ + + 0x11,0x11, + 0x11,0x11, + + 0x00,0x6d, + 0x00,0x00, + }; + + + + StreamDPVmInstructionsRunner runner; + + uint8_t ex[]={5, + 6, + 7, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 1, + 2, + 3}; + + int i; + for (i=0; i<20; i++) { + runner.run(program_size, + vm.get_dp_instruction_buffer()->get_program(), + vm.get_bss_ptr(), + test_udp_pkt); + + fprintf(stdout," %d \n",i); + //utl_DumpBuffer(stdout,test_udp_pkt,PKT_TEST_SIZE,0); + /* big */ + EXPECT_EQ(test_udp_pkt[29],ex[i]); + EXPECT_EQ(test_udp_pkt[28],0); + EXPECT_EQ(test_udp_pkt[27],0); + EXPECT_EQ(test_udp_pkt[26],0); + } + +} + +TEST_F(basic_vm, vm4) { + + StreamVm vm; + + vm.add_instruction( new StreamVmInstructionFlowMan( "var1",4 /* size */, + StreamVmInstructionFlowMan::FLOW_VAR_OP_INC,4,1,7 ) + ); + vm.add_instruction( new StreamVmInstructionWriteToPkt( "var1",26, 0,false) + ); + //vm.add_instruction( new StreamVmInstructionFixChecksumIpv4(14) ); + + vm.set_packet_size(128); + + vm.compile(); + + + uint32_t program_size=vm.get_dp_instruction_buffer()->get_program_size(); + + printf (" program size : %lu \n",(ulong)program_size); + + + vm.Dump(stdout); + + #define PKT_TEST_SIZE (14+20+4+4) + uint8_t test_udp_pkt[PKT_TEST_SIZE]={ + 0x00,0x00,0x00,0x01,0x00,0x00, + 0x00,0x00,0x00,0x01,0x00,0x00, + 0x08,0x00, + + 0x45,0x00,0x00,0x81, /*14 */ + 0xaf,0x7e,0x00,0x00, /*18 */ + 0x12,0x11,0xd9,0x23, /*22 */ + 0x01,0x01,0x01,0x01, /*26 */ + 0x3d,0xad,0x72,0x1b, /*30 */ + + 0x11,0x11, + 0x11,0x11, + + 0x00,0x6d, + 0x00,0x00, + }; + + + + StreamDPVmInstructionsRunner runner; + + uint8_t ex[]={5, + 6, + 7, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 1, + 2, + 3}; + + int i; + for (i=0; i<20; i++) { + runner.run(program_size, + vm.get_dp_instruction_buffer()->get_program(), + vm.get_bss_ptr(), + test_udp_pkt); + + fprintf(stdout," %d \n",i); + //utl_DumpBuffer(stdout,test_udp_pkt,PKT_TEST_SIZE,0); + /* not big */ + EXPECT_EQ(test_udp_pkt[29],0); + EXPECT_EQ(test_udp_pkt[28],0); + EXPECT_EQ(test_udp_pkt[27],0); + EXPECT_EQ(test_udp_pkt[26],ex[i]); + } + +} + + +/* two fields */ +TEST_F(basic_vm, vm5) { + + StreamVm vm; + + vm.add_instruction( new StreamVmInstructionFlowMan( "var1",4 /* size */, + StreamVmInstructionFlowMan::FLOW_VAR_OP_INC,4,1,7 ) + ); + + vm.add_instruction( new StreamVmInstructionFlowMan( "var2",1 /* size */, + StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC,25,23,27 ) ); + + /* src ip */ + vm.add_instruction( new StreamVmInstructionWriteToPkt( "var1",26, 0,true) + ); + + /* change TOS */ + vm.add_instruction( new StreamVmInstructionWriteToPkt( "var2",15, 0,true) + ); + + vm.add_instruction( new StreamVmInstructionFixChecksumIpv4(14) ); + + vm.set_packet_size(128); + + vm.compile(); + + + uint32_t program_size=vm.get_dp_instruction_buffer()->get_program_size(); + + printf (" program size : %lu \n",(ulong)program_size); + + + vm.Dump(stdout); + + #define PKT_TEST_SIZE (14+20+4+4) + uint8_t test_udp_pkt[PKT_TEST_SIZE]={ + 0x00,0x00,0x00,0x01,0x00,0x00, + 0x00,0x00,0x00,0x01,0x00,0x00, + 0x08,0x00, + + 0x45,0x00,0x00,0x81, /*14 */ + 0xaf,0x7e,0x00,0x00, /*18 */ + 0x12,0x11,0xd9,0x23, /*22 */ + 0x01,0x01,0x01,0x01, /*26 */ + 0x3d,0xad,0x72,0x1b, /*30 */ + + 0x11,0x11, /*34 */ + 0x11,0x11, + + 0x00,0x6d, + 0x00,0x00, + }; + + + + StreamDPVmInstructionsRunner runner; + + uint8_t ex[]={5, + 6, + 7, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 1, + 2, + 3}; + + uint8_t ex_tos[]={0x18, + 0x17, + 0x1b, + 0x1a, + 0x19, + 0x18, + 0x17, + 0x1b, + 0x1a, + 0x19, + 0x18, + 0x17, + 0x1b, + 0x1a, + 0x19, + 0x18, + 0x17, + 0x1b, + 0x1a, + 0x19, + 0x18, + 0x17, + + 0x1b, + 0x1a, + 0x19, + 0x18, + 0x17, + + 0x1b, + 0x1a, + 0x19, + 0x18, + 0x17, + + 0x1b, + 0x1a, + 0x19, + 0x18, + 0x17, + }; + + int i; + for (i=0; i<20; i++) { + runner.run(program_size, + vm.get_dp_instruction_buffer()->get_program(), + vm.get_bss_ptr(), + test_udp_pkt); + + fprintf(stdout," %d \n",i); + //utl_DumpBuffer(stdout,test_udp_pkt,PKT_TEST_SIZE,0); + /* not big */ + EXPECT_EQ(test_udp_pkt[29],ex[i]); + EXPECT_EQ(test_udp_pkt[28],0); + EXPECT_EQ(test_udp_pkt[27],0); + EXPECT_EQ(test_udp_pkt[26],0); + + /* check tos */ + EXPECT_EQ(test_udp_pkt[15],ex_tos[i]); + } +} + +/* -load file, write to file */ +TEST_F(basic_vm, vm6) { + + + + StreamVm vm; + + vm.add_instruction( new StreamVmInstructionFlowMan( "var1",4 /* size */, + StreamVmInstructionFlowMan::FLOW_VAR_OP_INC,0x10000001,0x10000001,0x100000fe) + ); + + vm.add_instruction( new StreamVmInstructionFlowMan( "var2",1 /* size */, + StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC,25,23,27 ) ); + + /* src ip */ + vm.add_instruction( new StreamVmInstructionWriteToPkt( "var1",26, 0,true) + ); + + /* change TOS */ + vm.add_instruction( new StreamVmInstructionWriteToPkt( "var2",15, 0,true) + ); + + vm.add_instruction( new StreamVmInstructionFixChecksumIpv4(14) ); + + vm.set_packet_size(128); + + vm.compile(); + + + uint32_t program_size=vm.get_dp_instruction_buffer()->get_program_size(); + + printf (" program size : %lu \n",(ulong)program_size); + + + vm.Dump(stdout); + + CPcapLoader pcap; + pcap.load_pcap_file("cap2/udp_64B.pcap",0); + + + + CFileWriterBase * lpWriter=CCapWriterFactory::CreateWriter(LIBPCAP,(char *)"exp/udp_64B_vm6.pcap"); + assert(lpWriter); + + + StreamDPVmInstructionsRunner runner; + + int i; + for (i=0; i<20; i++) { + runner.run(program_size, + vm.get_dp_instruction_buffer()->get_program(), + vm.get_bss_ptr(), + (uint8_t*)pcap.m_raw.raw); + + //utl_DumpBuffer(stdout,pcap.m_raw.raw,pcap.m_raw.pkt_len,0); + assert(lpWriter->write_packet(&pcap.m_raw)); + } + + delete lpWriter; + + CErfCmp cmp; + + bool res1=cmp.compare("exp/udp_64B_vm6.pcap","exp/udp_64B_vm6-ex.pcap"); + EXPECT_EQ(1, res1?1:0); +} + +/* test client command */ +TEST_F(basic_vm, vm7) { + + + + StreamVm vm; + + vm.add_instruction( new StreamVmInstructionFlowClient( "cl1", + 0x10000001, + 0x10000004, + 1025, + 1027, + 100, + 0) ); + + /* src ip */ + vm.add_instruction( new StreamVmInstructionWriteToPkt( "cl1.ip",26, 0,true) + ); + + vm.add_instruction( new StreamVmInstructionFixChecksumIpv4(14) ); + + /* src port */ + vm.add_instruction( new StreamVmInstructionWriteToPkt( "cl1.port",34, 0,true) + ); + + + vm.set_packet_size(128); + + vm.compile(); + + + uint32_t program_size=vm.get_dp_instruction_buffer()->get_program_size(); + + printf (" program size : %lu \n",(ulong)program_size); + + + vm.Dump(stdout); + + CPcapLoader pcap; + pcap.load_pcap_file("cap2/udp_64B.pcap",0); + + + + CFileWriterBase * lpWriter=CCapWriterFactory::CreateWriter(LIBPCAP,(char *)"exp/udp_64B_vm7.pcap"); + assert(lpWriter); + + + StreamDPVmInstructionsRunner runner; + + int i; + for (i=0; i<20; i++) { + runner.run(program_size, + vm.get_dp_instruction_buffer()->get_program(), + vm.get_bss_ptr(), + (uint8_t*)pcap.m_raw.raw); + + assert(lpWriter->write_packet(&pcap.m_raw)); + } + + delete lpWriter; + + CErfCmp cmp; + + bool res1=cmp.compare("exp/udp_64B_vm7.pcap","exp/udp_64B_vm7-ex.pcap"); + EXPECT_EQ(1, res1?1:0); +} + +TEST_F(basic_vm, vm8) { + + + + StreamVm vm; + + vm.add_instruction( new StreamVmInstructionFlowClient( "cl1", + 0x10000001, + 0x10000006, + 1025, + 1027, + 4, + 0) ); + + /* src ip */ + vm.add_instruction( new StreamVmInstructionWriteToPkt( "cl1.ip",26, 0,true) + ); + + vm.add_instruction( new StreamVmInstructionFixChecksumIpv4(14) ); + + /* src port */ + vm.add_instruction( new StreamVmInstructionWriteToPkt( "cl1.port",34, 0,true) + ); + + + vm.set_packet_size(128); + + vm.compile(); + + + uint32_t program_size=vm.get_dp_instruction_buffer()->get_program_size(); + + printf (" program size : %lu \n",(ulong)program_size); + + + vm.Dump(stdout); + + CPcapLoader pcap; + pcap.load_pcap_file("cap2/udp_64B.pcap",0); + + + + CFileWriterBase * lpWriter=CCapWriterFactory::CreateWriter(LIBPCAP,(char *)"exp/udp_64B_vm8.pcap"); + assert(lpWriter); + + + StreamDPVmInstructionsRunner runner; + + int i; + for (i=0; i<20; i++) { + runner.run(program_size, + vm.get_dp_instruction_buffer()->get_program(), + vm.get_bss_ptr(), + (uint8_t*)pcap.m_raw.raw); + + assert(lpWriter->write_packet(&pcap.m_raw)); + } + + delete lpWriter; + + CErfCmp cmp; + + bool res1=cmp.compare("exp/udp_64B_vm8.pcap","exp/udp_64B_vm8-ex.pcap"); + EXPECT_EQ(1, res1?1:0); +} + +static void vm_build_program_seq(StreamVm & vm, + uint16_t packet_size, + bool should_compile){ + + vm.add_instruction( new StreamVmInstructionFlowClient( "tuple_gen", + 0x10000001, + 0x10000006, + 1025, + 1027, + 20, + 0) ); + + /* src ip */ + vm.add_instruction( new StreamVmInstructionWriteToPkt( "tuple_gen.ip",26, 0,true) + ); + + vm.add_instruction( new StreamVmInstructionFixChecksumIpv4(14) ); + + /* src port */ + vm.add_instruction( new StreamVmInstructionWriteToPkt( "tuple_gen.port",34, 0,true) + ); + + + vm.set_packet_size(packet_size); + + if (should_compile) { + vm.compile(); + } +} + + +TEST_F(basic_vm, vm9) { + + + StreamVm vm; + + vm_build_program_seq(vm,128, true); + + printf(" max packet update %lu \n",(ulong)vm.get_max_packet_update_offset()); + + EXPECT_EQ(36,vm.get_max_packet_update_offset()); + + uint32_t program_size=vm.get_dp_instruction_buffer()->get_program_size(); + + printf (" program size : %lu \n",(ulong)program_size); + + + vm.Dump(stdout); + + CPcapLoader pcap; + pcap.load_pcap_file("cap2/udp_64B.pcap",0); + + + + CFileWriterBase * lpWriter=CCapWriterFactory::CreateWriter(LIBPCAP,(char *)"exp/udp_64B_vm9.pcap"); + assert(lpWriter); + + + StreamDPVmInstructionsRunner runner; + + int i; + for (i=0; i<30; i++) { + runner.run(program_size, + vm.get_dp_instruction_buffer()->get_program(), + vm.get_bss_ptr(), + (uint8_t*)pcap.m_raw.raw); + + assert(lpWriter->write_packet(&pcap.m_raw)); + } + + delete lpWriter; + + CErfCmp cmp; + + bool res1=cmp.compare("exp/udp_64B_vm9.pcap","exp/udp_64B_vm9-ex.pcap"); + EXPECT_EQ(1, res1?1:0); +} + + +/* test vmDP object */ +TEST_F(basic_vm, vm10) { + + StreamVm vm; + + vm_build_program_seq(vm,128, true); + + printf(" max packet update %lu \n",(ulong)vm.get_max_packet_update_offset()); + + EXPECT_EQ(36,vm.get_max_packet_update_offset()); + + StreamVmDp * lpDpVm =vm.cloneAsVmDp(); + + EXPECT_EQ(lpDpVm->get_bss_size(),vm.get_bss_size()); + + uint32_t program_size=vm.get_dp_instruction_buffer()->get_program_size(); + + printf (" program size : %lu \n",(ulong)program_size); + + + vm.Dump(stdout); + + CPcapLoader pcap; + pcap.load_pcap_file("cap2/udp_64B.pcap",0); + + + + CFileWriterBase * lpWriter=CCapWriterFactory::CreateWriter(LIBPCAP,(char *)"exp/udp_64B_vm9.pcap"); + assert(lpWriter); + + + StreamDPVmInstructionsRunner runner; + + int i; + for (i=0; i<30; i++) { + + runner.run(lpDpVm->get_program_size(), + lpDpVm->get_program(), + lpDpVm->get_bss(), + (uint8_t*)pcap.m_raw.raw); + + assert(lpWriter->write_packet(&pcap.m_raw)); + } + + delete lpWriter; + + CErfCmp cmp; + delete lpDpVm; + + bool res1=cmp.compare("exp/udp_64B_vm9.pcap","exp/udp_64B_vm9-ex.pcap"); + EXPECT_EQ(1, res1?1:0); +} + + + +////////////////////////////////////////////////////// + + #define EXPECT_EQ_UINT32(a,b) EXPECT_EQ((uint32_t)(a),(uint32_t)(b)) @@ -275,96 +1163,6 @@ public: }; -class CPcapLoader { -public: - CPcapLoader(); - ~CPcapLoader(); - - -public: - bool load_pcap_file(std::string file,int pkt_id=0); - void update_ip_src(uint32_t ip_addr); - void clone_packet_into_stream(TrexStream * stream); - void dump_packet(); - -public: - bool m_valid; - CCapPktRaw m_raw; - CPacketIndication m_pkt_indication; -}; - -CPcapLoader::~CPcapLoader(){ -} - -bool CPcapLoader::load_pcap_file(std::string cap_file,int pkt_id){ - m_valid=false; - CPacketParser parser; - - CCapReaderBase * lp=CCapReaderFactory::CreateReader((char *)cap_file.c_str(),0); - - if (lp == 0) { - printf(" ERROR file %s does not exist or not supported \n",(char *)cap_file.c_str()); - return false; - } - - int cnt=0; - bool found =false; - - - while ( true ) { - /* read packet */ - if ( lp->ReadPacket(&m_raw) ==false ){ - break; - } - if (cnt==pkt_id) { - found = true; - break; - } - cnt++; - } - if ( found ){ - if ( parser.ProcessPacket(&m_pkt_indication, &m_raw) ){ - m_valid = true; - } - } - - delete lp; - return (m_valid); -} - -void CPcapLoader::update_ip_src(uint32_t ip_addr){ - - if ( m_pkt_indication.l3.m_ipv4 ) { - m_pkt_indication.l3.m_ipv4->setSourceIp(ip_addr); - m_pkt_indication.l3.m_ipv4->updateCheckSum(); - } -} - -void CPcapLoader::clone_packet_into_stream(TrexStream * stream){ - - uint16_t pkt_size=m_raw.getTotalLen(); - - uint8_t *binary = new uint8_t[pkt_size]; - memcpy(binary,m_raw.raw,pkt_size); - stream->m_pkt.binary = binary; - stream->m_pkt.len = pkt_size; -} - - - - -CPcapLoader::CPcapLoader(){ - -} - -void CPcapLoader::dump_packet(){ - if (m_valid ) { - m_pkt_indication.Dump(stdout,1); - }else{ - fprintf(stdout," no packets were found \n"); - } -} - TEST_F(basic_stl, load_pcap_file) { printf (" stateles %d \n",(int)sizeof(CGenNodeStateless)); @@ -1215,6 +2013,100 @@ TEST_F(basic_stl, multi_pkt1) { } +class CEnableVm { +public: + void run(bool full_packet,double duration ); +public: + std::string m_input_packet; //"cap2/udp_64B.pcap" + std::string m_out_file; //"exp/stl_vm_enable0"; +}; + +void CEnableVm::run(bool full_packet,double duration=10.0){ + + CBasicStl t1; + CParserOption * po =&CGlobalInfo::m_options; + po->preview.setVMode(7); + po->preview.setFileWrite(true); + po->out_file =m_out_file; + + TrexStreamsCompiler compile; + + uint8_t port_id=0; + + std::vector<TrexStream *> streams; + + TrexStream * stream1 = new TrexStream(TrexStream::stCONTINUOUS,0,0); + + stream1->set_pps(1.0); + + stream1->m_enabled = true; + stream1->m_self_start = true; + stream1->m_port_id= port_id; + + CPcapLoader pcap; + pcap.load_pcap_file(m_input_packet,0); + pcap.update_ip_src(0x10000001); + pcap.clone_packet_into_stream(stream1); + + uint16_t pkt_size=pcap.m_raw.pkt_len; + + vm_build_program_seq(stream1->m_vm,pkt_size, false); + #if 0 + if ( full_packet ){ + EXPECT_EQ(stream1->m_vm_prefix_size,pkt_size); + }else{ + EXPECT_EQ(stream1->m_vm_prefix_size,35); + } + #endif + + + streams.push_back(stream1); + + // stream - clean + std::vector<TrexStreamsCompiledObj *> objs; + + assert(compile.compile(port_id,streams, objs) ); + + TrexStatelessDpStart * lpstart = new TrexStatelessDpStart(port_id, 0, objs[0], duration /*sec */ ); + + + t1.m_msg = lpstart; + + bool res=t1.init(); + + delete stream1 ; + + EXPECT_EQ_UINT32(1, res?1:0)<< "pass"; +} + + +TEST_F(basic_stl, vm_enable0) { + + CEnableVm vm_test; + vm_test.m_out_file = "exp/stl_vm_enable0"; + vm_test.m_input_packet = "cap2/udp_64B.pcap"; + vm_test.run(true); +} + + +TEST_F(basic_stl, vm_enable1) { + + CEnableVm vm_test; + vm_test.m_out_file = "exp/stl_vm_enable1"; + vm_test.m_input_packet = "stl/udp_594B_no_crc.pcap"; + vm_test.run(false); +} + + + +TEST_F(basic_stl, vm_enable2) { + + CEnableVm vm_test; + vm_test.m_out_file = "exp/stl_vm_enable2"; + vm_test.m_input_packet = "cap2/udp_64B.pcap"; + vm_test.run(true,50.0); +} + diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 441df5ce..8c9eb914 100755 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -1874,7 +1874,7 @@ public: bool process_rx_pkt(pkt_dir_t dir,rte_mbuf_t * m); - virtual int update_mac_addr_from_global_cfg(pkt_dir_t dir, rte_mbuf_t *m); + virtual int update_mac_addr_from_global_cfg(pkt_dir_t dir, uint8_t * p); virtual pkt_dir_t port_id_to_dir(uint8_t port_id); @@ -2077,6 +2077,8 @@ int CCoreEthIF::send_pkt(CCorePerPort * lp_port, CVirtualIFPerSideStats * lp_stats ){ + //rte_pktmbuf_dump(stdout,m, rte_pktmbuf_pkt_len(m)); + lp_stats->m_tx_pkt +=1; lp_stats->m_tx_bytes += (rte_pktmbuf_pkt_len(m)+4); @@ -2133,12 +2135,18 @@ int CCoreEthIFStateless::send_node(CGenNode * no){ /* check that we have mbuf */ rte_mbuf_t * m=node_sl->get_cache_mbuf(); - assert( m ); pkt_dir_t dir=(pkt_dir_t)node_sl->get_mbuf_cache_dir(); CCorePerPort * lp_port=&m_ports[dir]; CVirtualIFPerSideStats * lp_stats = &m_stats[dir]; - rte_pktmbuf_refcnt_update(m,1); + if (m) { + /* cache case */ + rte_pktmbuf_refcnt_update(m,1); + }else{ + m=node_sl->alloc_node_with_vm(); + assert(m); + } send_pkt(lp_port,m,lp_stats); + return (0); }; @@ -2237,14 +2245,12 @@ int CCoreEthIF::send_node(CGenNode * node){ } -int CCoreEthIF::update_mac_addr_from_global_cfg(pkt_dir_t dir, - rte_mbuf_t *m){ - assert(m); +int CCoreEthIF::update_mac_addr_from_global_cfg(pkt_dir_t dir, uint8_t * p){ + assert(p); assert(dir<2); + CCorePerPort * lp_port=&m_ports[dir]; - 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); return (0); } diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index 305ccc17..a1c3bb99 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -29,22 +29,6 @@ limitations under the License. using namespace std; -/** - * simple parser of string to number - * only difference is that it enforces whole number - * and not partial - * - */ -static uint64_t str2num(const string &str) { - size_t index; - - uint64_t num = std::stoull(str, &index, 0); - if (index != str.size()) { - throw invalid_argument("could not parse string to number"); - } - - return (num); -} /*************************** * add new stream @@ -213,31 +197,9 @@ TrexRpcCmdAddStream::parse_vm_instr_flow_var(const Json::Value &inst, TrexStream throw TrexRpcException("internal error"); } - std::string init_value_str = parse_string(inst, "init_value", result); - std::string min_value_str = parse_string(inst, "min_value", result); - std::string max_value_str = parse_string(inst, "max_value", result); - - uint64_t init_value = 0; - uint64_t min_value = 0; - uint64_t max_value = 0; - - try { - init_value = str2num(init_value_str); - } catch (invalid_argument) { - generate_parse_err(result, "failed to parse 'init_value' as a number"); - } - - try { - min_value = str2num(min_value_str); - } catch (invalid_argument) { - generate_parse_err(result, "failed to parse 'min_value' as a number"); - } - - try { - max_value = str2num(max_value_str); - } catch (invalid_argument) { - generate_parse_err(result, "failed to parse 'max_value' as a number"); - } + uint64_t init_value = parse_uint64(inst, "init_value", result); + uint64_t min_value = parse_uint64(inst, "min_value", result); + uint64_t max_value = parse_uint64(inst, "max_value", result); stream->m_vm.add_instruction(new StreamVmInstructionFlowMan(flow_var_name, flow_var_size, diff --git a/src/rpc-server/commands/trex_rpc_cmd_test.cpp b/src/rpc-server/commands/trex_rpc_cmd_test.cpp index 3cdddd31..ad4d3bb1 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_test.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_test.cpp @@ -21,6 +21,7 @@ limitations under the License. #include "trex_rpc_cmds.h" #include <iostream> #include <sstream> +#include <json/json.h> using namespace std; @@ -31,7 +32,7 @@ using namespace std; trex_rpc_cmd_rc_e TrexRpcCmdTestAdd::_run(const Json::Value ¶ms, Json::Value &result) { - result["result"] = parse_int(params, "x", result) + parse_int(params, "y", result); + result["result"] = Json::Value::UInt64(parse_uint64(params, "x", result) + parse_uint64(params, "y", result)); return (TREX_RPC_CMD_OK); } @@ -44,7 +45,7 @@ TrexRpcCmdTestAdd::_run(const Json::Value ¶ms, Json::Value &result) { trex_rpc_cmd_rc_e TrexRpcCmdTestSub::_run(const Json::Value ¶ms, Json::Value &result) { - result["result"] = parse_int(params, "x", result) - parse_int(params, "y", result); + result["result"] = Json::Value::UInt64(parse_uint64(params, "x", result) - parse_uint64(params, "y", result)); return (TREX_RPC_CMD_OK); } diff --git a/src/rpc-server/trex_rpc_cmd.cpp b/src/rpc-server/trex_rpc_cmd.cpp index 8f0ffbad..b5dd121c 100644 --- a/src/rpc-server/trex_rpc_cmd.cpp +++ b/src/rpc-server/trex_rpc_cmd.cpp @@ -98,6 +98,8 @@ TrexRpcCommand::type_to_str(field_type_e type) { return "uint16"; case FIELD_TYPE_UINT32: return "uint32"; + case FIELD_TYPE_UINT64: + return "uint64"; case FIELD_TYPE_BOOL: return "bool"; case FIELD_TYPE_INT: @@ -179,6 +181,18 @@ TrexRpcCommand::parse_uint32(const Json::Value &parent, int index, Json::Value & return parent[index].asUInt(); } +uint64_t +TrexRpcCommand::parse_uint64(const Json::Value &parent, const std::string &name, Json::Value &result) { + check_field_type(parent, name, FIELD_TYPE_UINT64, result); + return parent[name].asUInt64(); +} + +uint64_t +TrexRpcCommand::parse_uint64(const Json::Value &parent, int index, Json::Value &result) { + check_field_type(parent, index, FIELD_TYPE_UINT64, result); + return parent[index].asUInt64(); +} + int TrexRpcCommand::parse_int(const Json::Value &parent, const std::string &name, Json::Value &result) { check_field_type(parent, name, FIELD_TYPE_INT, result); @@ -286,6 +300,12 @@ TrexRpcCommand::check_field_type_common(const Json::Value &field, const std::str } break; + case FIELD_TYPE_UINT64: + if (!field.isInt64()) { + rc = false; + } + break; + case FIELD_TYPE_BOOL: if (!field.isBool()) { rc = false; diff --git a/src/rpc-server/trex_rpc_cmd_api.h b/src/rpc-server/trex_rpc_cmd_api.h index f81981d4..7cbdf4ff 100644 --- a/src/rpc-server/trex_rpc_cmd_api.h +++ b/src/rpc-server/trex_rpc_cmd_api.h @@ -100,6 +100,7 @@ protected: FIELD_TYPE_BYTE, FIELD_TYPE_UINT16, FIELD_TYPE_UINT32, + FIELD_TYPE_UINT64, FIELD_TYPE_INT, FIELD_TYPE_DOUBLE, FIELD_TYPE_BOOL, @@ -138,6 +139,7 @@ protected: uint8_t parse_byte(const Json::Value &parent, const std::string &name, Json::Value &result); uint16_t parse_uint16(const Json::Value &parent, const std::string &name, Json::Value &result); uint32_t parse_uint32(const Json::Value &parent, const std::string &name, Json::Value &result); + uint64_t parse_uint64(const Json::Value &parent, const std::string &name, Json::Value &result); int parse_int(const Json::Value &parent, const std::string &name, Json::Value &result); double parse_double(const Json::Value &parent, const std::string &name, Json::Value &result); bool parse_bool(const Json::Value &parent, const std::string &name, Json::Value &result); @@ -148,6 +150,7 @@ protected: uint8_t parse_byte(const Json::Value &parent, int index, Json::Value &result); uint16_t parse_uint16(const Json::Value &parent, int index, Json::Value &result); uint32_t parse_uint32(const Json::Value &parent, int index, Json::Value &result); + uint64_t parse_uint64(const Json::Value &parent, int index, Json::Value &result); int parse_int(const Json::Value &parent, int index, Json::Value &result); double parse_double(const Json::Value &parent, int index, Json::Value &result); bool parse_bool(const Json::Value &parent, int index, Json::Value &result); diff --git a/src/stateless/cp/trex_stream.cpp b/src/stateless/cp/trex_stream.cpp index cad603e2..72e72c7c 100644 --- a/src/stateless/cp/trex_stream.cpp +++ b/src/stateless/cp/trex_stream.cpp @@ -53,6 +53,33 @@ std::string TrexStream::get_stream_type_str(stream_type_t stream_type){ } +void +TrexStream::compile() { + + /* in case there are no instructions - nothing to do */ + if (m_vm.is_vm_empty()) { + m_has_vm = false; + return; + } + + m_has_vm = true; + + m_vm.set_packet_size(m_pkt.len); + + m_vm.compile(); + + m_vm_dp = m_vm.cloneAsVmDp(); + + + /* calc m_vm_prefix_size which is the size of the writable packet */ + uint16_t max_pkt_offset = m_vm_dp->get_max_packet_update_offset(); + uint16_t pkt_size = m_pkt.len; + + /* calculate the mbuf size that we should allocate */ + m_vm_prefix_size = calc_writable_mbuf_size(max_pkt_offset, pkt_size); +} + + void TrexStream::Dump(FILE *fd){ fprintf(fd,"\n"); @@ -103,6 +130,8 @@ TrexStream::TrexStream(uint8_t type, m_pkt.binary = NULL; m_pkt.len = 0; + m_has_vm = false; + m_vm_prefix_size = 0; m_rx_check.m_enable = false; @@ -111,12 +140,17 @@ TrexStream::TrexStream(uint8_t type, m_burst_total_pkts=0; m_num_bursts=1; m_ibg_usec=0.0; + m_vm_dp = NULL; } TrexStream::~TrexStream() { if (m_pkt.binary) { delete [] m_pkt.binary; } + if ( m_vm_dp ){ + delete m_vm_dp; + m_vm_dp=NULL; + } } void @@ -199,3 +233,4 @@ int TrexStreamTable::size() { return m_stream_table.size(); } + diff --git a/src/stateless/cp/trex_stream.h b/src/stateless/cp/trex_stream.h index b991b05f..720246f6 100644 --- a/src/stateless/cp/trex_stream.h +++ b/src/stateless/cp/trex_stream.h @@ -36,6 +36,54 @@ limitations under the License. class TrexRpcCmdAddStream; +static inline uint16_t get_log2_size(uint16_t size){ + + uint16_t _sizes[]={64,128,256,512,1024,2048}; + int i; + for (i=0; i<sizeof(_sizes)/sizeof(_sizes[0]); i++) { + if (size<=_sizes[i]) { + return (_sizes[i]); + } + } + assert(0); + return (0); +} + +/** + * calculate the size of writable mbuf in bytes. maximum size if packet size + * + * @param max_offset_writable + * the last byte that we don't write too. for example when 63 it means that bytes [62] in the array is written (zero base) + * @param pkt_size packet size in bytes + * + * @return the writable size of the first mbuf . the idea is to give at least 64 bytes const mbuf else all packet will be writeable + * + * examples: + * max_offset_writable =63 + * pkt_size =62 + * ==>62 + * + */ +static inline uint16_t calc_writable_mbuf_size(uint16_t max_offset_writable, + uint16_t pkt_size){ + + if ( pkt_size<=64 ){ + return (pkt_size); + } + if (pkt_size<=128) { + return (pkt_size); + } + + //pkt_size> 128 + uint16_t non_writable = pkt_size - (max_offset_writable -1) ; + if ( non_writable<64 ) { + return (pkt_size); + } + return(max_offset_writable-1); +} + + + struct CStreamPktData { uint8_t *binary; uint16_t len; @@ -132,10 +180,18 @@ public: TrexStream * clone_as_dp() const { TrexStream *dp = new TrexStream(m_type,m_port_id,m_stream_id); - - - dp->m_isg_usec = m_isg_usec; - dp->m_next_stream_id = m_next_stream_id; + dp->m_has_vm = m_has_vm; + if (m_vm_dp) { + /* should have vm */ + assert(m_has_vm); + dp->m_vm_dp = m_vm_dp->clone(); + }else{ + dp->m_vm_dp = NULL; + } + dp->m_vm_prefix_size = m_vm_prefix_size; + + dp->m_isg_usec = m_isg_usec; + dp->m_next_stream_id = m_next_stream_id; dp->m_enabled = m_enabled; dp->m_self_start = m_self_start; @@ -162,10 +218,28 @@ public: } void Dump(FILE *fd); + + bool is_vm(){ + return ( m_has_vm ); + } + + StreamVmDp * getDpVm(){ + return ( m_vm_dp); + } + + void post_vm_compile(); + + /** + * internal compilation of stream (for DP) + * + */ + void compile(); + public: /* basic */ uint8_t m_type; uint8_t m_port_id; + uint16_t m_vm_prefix_size; /* writeable mbuf size */ uint32_t m_stream_id; /* id from RPC can be anything */ @@ -176,6 +250,10 @@ public: /* indicators */ bool m_enabled; bool m_self_start; + bool m_has_vm; /* do we have instructions to run */ + + + StreamVmDp * m_vm_dp; /* compile VM */ CStreamPktData m_pkt; /* pkt */ diff --git a/src/stateless/cp/trex_stream_vm.cpp b/src/stateless/cp/trex_stream_vm.cpp index 2e760ae9..e10e1a81 100644 --- a/src/stateless/cp/trex_stream_vm.cpp +++ b/src/stateless/cp/trex_stream_vm.cpp @@ -1,5 +1,6 @@ /* Itay Marom + Hanoch Haim Cisco Systems, Inc. */ @@ -19,6 +20,108 @@ See the License for the specific language governing permissions and limitations under the License. */ #include <trex_stream_vm.h> +#include <sstream> +#include <assert.h> +#include <iostream> +#include <trex_stateless.h> +#include <common/Network/Packet/IPHeader.h> +#include <common/basic_utils.h> + + + + +void StreamVmInstructionFixChecksumIpv4::Dump(FILE *fd){ + fprintf(fd," fix_check_sum , %lu \n",(ulong)m_pkt_offset); +} + + +void StreamVmInstructionFlowMan::sanity_check_valid_size(uint32_t ins_id,StreamVm *lp){ + uint8_t valid[]={1,2,4,8}; + int i; + for (i=0; i<sizeof(valid)/sizeof(valid[0]); i++) { + if (valid[i]==m_size_bytes) { + return; + } + } + + std::stringstream ss; + + ss << "instruction id '" << ins_id << "' has non valid length " << m_size_bytes ; + + lp->err(ss.str()); +} + +void StreamVmInstructionFlowMan::sanity_check_valid_opt(uint32_t ins_id,StreamVm *lp){ + uint8_t valid[]={FLOW_VAR_OP_INC, + FLOW_VAR_OP_DEC, + FLOW_VAR_OP_RANDOM}; + int i; + for (i=0; i<sizeof(valid)/sizeof(valid[0]); i++) { + if (valid[i]==m_op) { + return; + } + } + + std::stringstream ss; + + ss << "instruction id '" << ins_id << "' has non valid op " << (int)m_op ; + + lp->err(ss.str()); +} + +void StreamVmInstructionFlowMan::sanity_check_valid_range(uint32_t ins_id,StreamVm *lp){ + //TBD check that init,min,max in valid range +} + + + +void StreamVmInstructionFlowMan::sanity_check(uint32_t ins_id,StreamVm *lp){ + + sanity_check_valid_size(ins_id,lp); + sanity_check_valid_opt(ins_id,lp); + sanity_check_valid_range(ins_id,lp); +} + + +void StreamVmInstructionFlowMan::Dump(FILE *fd){ + fprintf(fd," flow_var , %s ,%lu, ",m_var_name.c_str(),(ulong)m_size_bytes); + + switch (m_op) { + + case FLOW_VAR_OP_INC : + fprintf(fd," INC ,"); + break; + case FLOW_VAR_OP_DEC : + fprintf(fd," DEC ,"); + break; + case FLOW_VAR_OP_RANDOM : + fprintf(fd," RANDOM ,"); + break; + default: + fprintf(fd," UNKNOWN,"); + break; + }; + + fprintf(fd," (%lu:%lu:%lu) \n",m_init_value,m_min_value,m_max_value); +} + + +void StreamVmInstructionWriteToPkt::Dump(FILE *fd){ + + fprintf(fd," write_pkt , %s ,%lu, add, %ld, big, %lu \n",m_flow_var_name.c_str(),(ulong)m_pkt_offset,(long)m_add_value,(ulong)(m_is_big_endian?1:0)); +} + + + +void StreamVmInstructionFlowClient::Dump(FILE *fd){ + + fprintf(fd," client_var ,%s , ",m_var_name.c_str()); + + fprintf(fd," ip:(%x-%x) port:(%x-%x) flow_limit:%lu flags: %x\n",m_client_min,m_client_max, m_port_min,m_port_max,(ulong)m_limit_num_flows,m_flags); +} + + + /*************************** * StreamVmInstruction @@ -36,19 +139,714 @@ void StreamVm::add_instruction(StreamVmInstruction *inst) { m_inst_list.push_back(inst); } +StreamDPVmInstructions * +StreamVm::get_dp_instruction_buffer(){ + return &m_instructions; +} + + const std::vector<StreamVmInstruction *> & StreamVm::get_instruction_list() { return m_inst_list; } -bool StreamVm::compile() { - /* implement me */ - return (false); +void StreamVm::var_clear_table(){ + m_flow_var_offset.clear(); +} + +bool StreamVm::var_add(const std::string &var_name,VmFlowVarRec & var){ + m_flow_var_offset[var_name] = var; + return (true); +} + + +uint16_t StreamVm::get_var_offset(const std::string &var_name){ + VmFlowVarRec var; + bool res=var_lookup(var_name,var); + assert(res); + return (var.m_offset); +} + + +bool StreamVm::var_lookup(const std::string &var_name,VmFlowVarRec & var){ + auto search = m_flow_var_offset.find(var_name); + + if (search != m_flow_var_offset.end()) { + var = search->second; + return true; + } else { + return false; + } +} + + + +void StreamVm::err(const std::string &err){ + throw TrexException("*** error: " + err); +} + + +void StreamVm::build_flow_var_table() { + + var_clear_table(); + m_cur_var_offset=0; + uint32_t ins_id=0; + /* scan all flow var instruction and build */ + for (auto inst : m_inst_list) { + if ( inst->get_instruction_type() == StreamVmInstruction::itFLOW_MAN ){ + + StreamVmInstructionFlowMan * ins_man=(StreamVmInstructionFlowMan *)inst; + + /* check that instruction is valid */ + ins_man->sanity_check(ins_id,this); + + VmFlowVarRec var; + /* if this is the first time */ + if ( var_lookup( ins_man->m_var_name,var) == true){ + std::stringstream ss; + ss << "instruction id '" << ins_id << "' flow variable name " << ins_man->m_var_name << " already exists"; + err(ss.str()); + }else{ + var.m_offset=m_cur_var_offset; + var.m_ins.m_ins_flowv = ins_man; + var.m_size_bytes = ins_man->m_size_bytes; + var_add(ins_man->m_var_name,var); + m_cur_var_offset += ins_man->m_size_bytes; + + } + } + + if ( inst->get_instruction_type() == StreamVmInstruction::itFLOW_CLIENT ){ + StreamVmInstructionFlowClient * ins_man=(StreamVmInstructionFlowClient *)inst; + + VmFlowVarRec var; + /* if this is the first time */ + if ( var_lookup( ins_man->m_var_name+".ip",var) == true){ + std::stringstream ss; + ss << "instruction id '" << ins_id << "' client variable name " << ins_man->m_var_name << " already exists"; + err(ss.str()); + } + if ( var_lookup( ins_man->m_var_name+".port",var) == true){ + std::stringstream ss; + ss << "instruction id '" << ins_id << "' client variable name " << ins_man->m_var_name << " already exists"; + err(ss.str()); + } + + if ( var_lookup( ins_man->m_var_name+".flow_limit",var) == true){ + std::stringstream ss; + ss << "instruction id '" << ins_id << "' client variable name " << ins_man->m_var_name << " already exists"; + err(ss.str()); + } + + var.m_offset = m_cur_var_offset; + var.m_ins.m_ins_flow_client = ins_man; + var.m_size_bytes =4; + + VmFlowVarRec var_port; + + var_port.m_offset = m_cur_var_offset+4; + var_port.m_ins.m_ins_flow_client = ins_man; + var_port.m_size_bytes =2; + + VmFlowVarRec var_flow_limit; + + var_flow_limit.m_offset = m_cur_var_offset+6; + var_flow_limit.m_ins.m_ins_flow_client = ins_man; + var_flow_limit.m_size_bytes =4; + + + var_add(ins_man->m_var_name+".ip",var); + var_add(ins_man->m_var_name+".port",var_port); + var_add(ins_man->m_var_name+".flow_limit",var_flow_limit); + + m_cur_var_offset += StreamVmInstructionFlowClient::get_flow_var_size(); + + assert(sizeof(StreamDPFlowClient)==StreamVmInstructionFlowClient::get_flow_var_size()); + } + + /* limit the flow var size */ + if (m_cur_var_offset > StreamVm::svMAX_FLOW_VAR ) { + std::stringstream ss; + ss << "too many flow varibles current size is :" << m_cur_var_offset << " maximum support is " << StreamVm::svMAX_FLOW_VAR; + err(ss.str()); + } + ins_id++; + } + +} + +void StreamVm::alloc_bss(){ + free_bss(); + m_bss=(uint8_t *)malloc(m_cur_var_offset); +} + +void StreamVm::clean_max_field_cnt(){ + m_max_field_update=0; +} + +void StreamVm::add_field_cnt(uint16_t new_cnt){ + + if ( new_cnt > m_max_field_update) { + m_max_field_update = new_cnt; + } +} + + +void StreamVm::free_bss(){ + if (m_bss) { + free(m_bss); + m_bss=0; + } +} + + +void StreamVm::build_program(){ + + /* build the commands into a buffer */ + m_instructions.clear(); + clean_max_field_cnt(); + uint32_t ins_id=0; + + for (auto inst : m_inst_list) { + StreamVmInstruction::instruction_type_t ins_type=inst->get_instruction_type(); + + /* itFIX_IPV4_CS */ + if (ins_type == StreamVmInstruction::itFIX_IPV4_CS) { + StreamVmInstructionFixChecksumIpv4 *lpFix =(StreamVmInstructionFixChecksumIpv4 *)inst; + + add_field_cnt(lpFix->m_pkt_offset +12); + + if ( (lpFix->m_pkt_offset + IPV4_HDR_LEN) > m_pkt_size ) { + + std::stringstream ss; + ss << "instruction id '" << ins_id << "' fix ipv4 command offset " << lpFix->m_pkt_offset << " is too high relative to packet size "<< m_pkt_size; + err(ss.str()); + } + + StreamDPOpIpv4Fix ipv_fix; + ipv_fix.m_offset = lpFix->m_pkt_offset; + ipv_fix.m_op = StreamDPVmInstructions::ditFIX_IPV4_CS; + m_instructions.add_command(&ipv_fix,sizeof(ipv_fix)); + } + + + /* flow man */ + if (ins_type == StreamVmInstruction::itFLOW_MAN) { + StreamVmInstructionFlowMan *lpMan =(StreamVmInstructionFlowMan *)inst; + + + if (lpMan->m_size_bytes == 1 ){ + + uint8_t op=StreamDPVmInstructions::ditINC8; + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){ + op = StreamDPVmInstructions::ditINC8 ; + } + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){ + op = StreamDPVmInstructions::ditDEC8 ; + } + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ){ + op = StreamDPVmInstructions::ditRANDOM8 ; + } + + StreamDPOpFlowVar8 fv8; + fv8.m_op = op; + fv8.m_flow_offset = get_var_offset(lpMan->m_var_name); + fv8.m_min_val = (uint8_t)lpMan->m_min_value; + fv8.m_max_val = (uint8_t)lpMan->m_max_value; + m_instructions.add_command(&fv8,sizeof(fv8)); + } + + if (lpMan->m_size_bytes == 2 ){ + uint8_t op; + + op = StreamDPVmInstructions::ditINC16; + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){ + op = StreamDPVmInstructions::ditINC16 ; + } + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){ + op = StreamDPVmInstructions::ditDEC16 ; + } + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ){ + op = StreamDPVmInstructions::ditRANDOM16 ; + } + + StreamDPOpFlowVar16 fv16; + fv16.m_op = op; + fv16.m_flow_offset = get_var_offset(lpMan->m_var_name); + fv16.m_min_val = (uint16_t)lpMan->m_min_value; + fv16.m_max_val = (uint16_t)lpMan->m_max_value; + m_instructions.add_command(&fv16,sizeof(fv16)); + } + + if (lpMan->m_size_bytes == 4 ){ + uint8_t op; + + op = StreamDPVmInstructions::ditINC32; + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){ + op = StreamDPVmInstructions::ditINC32 ; + } + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){ + op = StreamDPVmInstructions::ditDEC32 ; + } + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ){ + op = StreamDPVmInstructions::ditRANDOM32 ; + } + + StreamDPOpFlowVar32 fv32; + fv32.m_op = op; + fv32.m_flow_offset = get_var_offset(lpMan->m_var_name); + fv32.m_min_val = (uint32_t)lpMan->m_min_value; + fv32.m_max_val = (uint32_t)lpMan->m_max_value; + m_instructions.add_command(&fv32,sizeof(fv32)); + } + + if (lpMan->m_size_bytes == 8 ){ + uint8_t op; + + op = StreamDPVmInstructions::ditINC64; + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){ + op = StreamDPVmInstructions::ditINC64 ; + } + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){ + op = StreamDPVmInstructions::ditDEC64 ; + } + + if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ){ + op = StreamDPVmInstructions::ditRANDOM64 ; + } + + StreamDPOpFlowVar32 fv64; + fv64.m_op = op; + fv64.m_flow_offset = get_var_offset(lpMan->m_var_name); + fv64.m_min_val = (uint64_t)lpMan->m_min_value; + fv64.m_max_val = (uint64_t)lpMan->m_max_value; + m_instructions.add_command(&fv64,sizeof(fv64)); + } + } + + if (ins_type == StreamVmInstruction::itPKT_WR) { + StreamVmInstructionWriteToPkt *lpPkt =(StreamVmInstructionWriteToPkt *)inst; + + VmFlowVarRec var; + if ( var_lookup(lpPkt->m_flow_var_name ,var) == false){ + + std::stringstream ss; + ss << "instruction id '" << ins_id << "' packet write with no valid flow varible name '" << lpPkt->m_flow_var_name << "'" ; + err(ss.str()); + } + + if (lpPkt->m_pkt_offset + var.m_size_bytes > m_pkt_size ) { + std::stringstream ss; + ss << "instruction id '" << ins_id << "' packet write with packet_offset " << lpPkt->m_pkt_offset + var.m_size_bytes << " bigger than packet size "<< m_pkt_size; + err(ss.str()); + } + add_field_cnt(lpPkt->m_pkt_offset + var.m_size_bytes); + + + uint8_t op_size=var.m_size_bytes; + bool is_big = lpPkt->m_is_big_endian; + uint8_t flags = (is_big?StreamDPOpPktWrBase::PKT_WR_IS_BIG:0); + uint8_t flow_offset = get_var_offset(lpPkt->m_flow_var_name); + + if (op_size == 1) { + StreamDPOpPktWr8 pw8; + pw8.m_op = StreamDPVmInstructions::itPKT_WR8; + pw8.m_flags =flags; + pw8.m_offset =flow_offset; + pw8.m_pkt_offset = lpPkt->m_pkt_offset; + pw8.m_val_offset = (int8_t)lpPkt->m_add_value; + m_instructions.add_command(&pw8,sizeof(pw8)); + } + + if (op_size == 2) { + StreamDPOpPktWr16 pw16; + pw16.m_op = StreamDPVmInstructions::itPKT_WR16; + pw16.m_flags =flags; + pw16.m_offset =flow_offset; + pw16.m_pkt_offset = lpPkt->m_pkt_offset; + pw16.m_val_offset = (int16_t)lpPkt->m_add_value; + m_instructions.add_command(&pw16,sizeof(pw16)); + } + + if (op_size == 4) { + StreamDPOpPktWr32 pw32; + pw32.m_op = StreamDPVmInstructions::itPKT_WR32; + pw32.m_flags =flags; + pw32.m_offset =flow_offset; + pw32.m_pkt_offset = lpPkt->m_pkt_offset; + pw32.m_val_offset = (int32_t)lpPkt->m_add_value; + m_instructions.add_command(&pw32,sizeof(pw32)); + } + + if (op_size == 8) { + StreamDPOpPktWr64 pw64; + pw64.m_op = StreamDPVmInstructions::itPKT_WR64; + pw64.m_flags =flags; + pw64.m_offset =flow_offset; + pw64.m_pkt_offset = lpPkt->m_pkt_offset; + pw64.m_val_offset = (int64_t)lpPkt->m_add_value; + m_instructions.add_command(&pw64,sizeof(pw64)); + } + + } + + + if (ins_type == StreamVmInstruction::itFLOW_CLIENT) { + StreamVmInstructionFlowClient *lpMan =(StreamVmInstructionFlowClient *)inst; + + if ( lpMan->is_unlimited_flows() ){ + StreamDPOpClientsUnLimit client_cmd; + client_cmd.m_op = StreamDPVmInstructions::itCLIENT_VAR_UNLIMIT; + + client_cmd.m_flow_offset = get_var_offset(lpMan->m_var_name+".ip"); /* start offset */ + client_cmd.m_flags = 0; /* not used */ + client_cmd.m_pad = 0; + client_cmd.m_min_ip = lpMan->m_client_min; + client_cmd.m_max_ip = lpMan->m_client_max; + m_instructions.add_command(&client_cmd,sizeof(client_cmd)); + + }else{ + StreamDPOpClientsLimit client_cmd; + client_cmd.m_op = StreamDPVmInstructions::itCLIENT_VAR; + + client_cmd.m_flow_offset = get_var_offset(lpMan->m_var_name+".ip"); /* start offset */ + client_cmd.m_flags = 0; /* not used */ + client_cmd.m_pad = 0; + client_cmd.m_min_port = lpMan->m_port_min; + client_cmd.m_max_port = lpMan->m_port_max; + client_cmd.m_min_ip = lpMan->m_client_min; + client_cmd.m_max_ip = lpMan->m_client_max; + client_cmd.m_limit_flows = lpMan->m_limit_num_flows; + m_instructions.add_command(&client_cmd,sizeof(client_cmd)); + } + } + + ins_id++; + } +} + + +void StreamVm::build_bss() { + alloc_bss(); + uint8_t * p=(uint8_t *)m_bss; + + for (auto inst : m_inst_list) { + + if ( inst->get_instruction_type() == StreamVmInstruction::itFLOW_MAN ){ + + StreamVmInstructionFlowMan * ins_man=(StreamVmInstructionFlowMan *)inst; + + switch (ins_man->m_size_bytes) { + case 1: + *p=(uint8_t)ins_man->m_init_value; + p+=1; + break; + case 2: + *((uint16_t*)p)=(uint16_t)ins_man->m_init_value; + p+=2; + break; + case 4: + *((uint32_t*)p)=(uint32_t)ins_man->m_init_value; + p+=4; + break; + case 8: + *((uint64_t*)p)=(uint64_t)ins_man->m_init_value; + p+=8; + break; + default: + assert(0); + } + } + + if ( inst->get_instruction_type() == StreamVmInstruction::itFLOW_CLIENT ){ + + StreamVmInstructionFlowClient * ins_man=(StreamVmInstructionFlowClient *)inst; + if (ins_man->m_client_min>0) { + *((uint32_t*)p)=(uint32_t)(ins_man->m_client_min-1); + }else{ + *((uint32_t*)p)=(uint32_t)ins_man->m_client_min; + } + p+=4; + + if (ins_man->is_unlimited_flows() ) { + *((uint16_t*)p)=StreamDPOpClientsUnLimit::CLIENT_UNLIMITED_MIN_PORT; + }else{ + *((uint16_t*)p)=(uint16_t)ins_man->m_port_min; + } + p+=2; + + *((uint32_t*)p)=0; + p+=4; + } + + } +} + + + +void StreamVm::compile() { + + /* build flow var offset table */ + build_flow_var_table() ; + + /* build init flow var memory */ + build_bss(); + + build_program(); + + if ( get_max_packet_update_offset() >svMAX_PACKET_OFFSET_CHANGE ){ + std::stringstream ss; + ss << "maximum offset is" << get_max_packet_update_offset() << " bigger than maximum " <<svMAX_PACKET_OFFSET_CHANGE; + err(ss.str()); + } } + StreamVm::~StreamVm() { for (auto inst : m_inst_list) { delete inst; + } + free_bss(); +} + + +void StreamVm::Dump(FILE *fd){ + fprintf(fd," instructions \n"); + uint32_t cnt=0; + for (auto inst : m_inst_list) { + fprintf(fd," [%04lu] : ",(ulong)cnt); + inst->Dump(fd); + cnt++; } + + if ( get_bss_size() ) { + fprintf(fd," BSS size %lu\n",(ulong)get_bss_size()); + utl_DumpBuffer(fd,get_bss_ptr(),get_bss_size(),0); + } + + if ( m_instructions.get_program_size() > 0 ){ + fprintf(fd," RAW instructions \n"); + m_instructions.Dump(fd); + } +} + + +void StreamDPVmInstructions::clear(){ + m_inst_list.clear(); +} + + +void StreamDPVmInstructions::add_command(void *buffer,uint16_t size){ + int i; + uint8_t *p= (uint8_t *)buffer; + /* push byte by byte */ + for (i=0; i<size; i++) { + m_inst_list.push_back(*p); + p++; + } +} + +uint8_t * StreamDPVmInstructions::get_program(){ + return (&m_inst_list[0]); +} + +uint32_t StreamDPVmInstructions::get_program_size(){ + return (m_inst_list.size()); +} + +void StreamDPVmInstructions::Dump(FILE *fd){ + + uint8_t * p=get_program(); + + + uint32_t program_size = get_program_size(); + uint8_t * p_end=p+program_size; + + StreamDPOpFlowVar8 *lpv8; + StreamDPOpFlowVar16 *lpv16; + StreamDPOpFlowVar32 *lpv32; + StreamDPOpFlowVar64 *lpv64; + StreamDPOpIpv4Fix *lpIpv4Fix; + StreamDPOpPktWr8 *lpw8; + StreamDPOpPktWr16 *lpw16; + StreamDPOpPktWr32 *lpw32; + StreamDPOpPktWr64 *lpw64; + StreamDPOpClientsLimit *lp_client; + StreamDPOpClientsUnLimit *lp_client_unlimited; + + + while ( p < p_end) { + uint8_t op_code=*p; + switch (op_code) { + + case ditINC8 : + lpv8 =(StreamDPOpFlowVar8 *)p; + lpv8->dump(fd,"INC8"); + p+=sizeof(StreamDPOpFlowVar8); + break; + case ditINC16 : + lpv16 =(StreamDPOpFlowVar16 *)p; + lpv16->dump(fd,"INC16"); + p+=sizeof(StreamDPOpFlowVar16); + break; + case ditINC32 : + lpv32 =(StreamDPOpFlowVar32 *)p; + lpv32->dump(fd,"INC32"); + p+=sizeof(StreamDPOpFlowVar32); + break; + case ditINC64 : + lpv64 =(StreamDPOpFlowVar64 *)p; + lpv64->dump(fd,"INC64"); + p+=sizeof(StreamDPOpFlowVar64); + break; + + case ditDEC8 : + lpv8 =(StreamDPOpFlowVar8 *)p; + lpv8->dump(fd,"DEC8"); + p+=sizeof(StreamDPOpFlowVar8); + break; + case ditDEC16 : + lpv16 =(StreamDPOpFlowVar16 *)p; + lpv16->dump(fd,"DEC16"); + p+=sizeof(StreamDPOpFlowVar16); + break; + case ditDEC32 : + lpv32 =(StreamDPOpFlowVar32 *)p; + lpv32->dump(fd,"DEC32"); + p+=sizeof(StreamDPOpFlowVar32); + break; + case ditDEC64 : + lpv64 =(StreamDPOpFlowVar64 *)p; + lpv64->dump(fd,"DEC64"); + p+=sizeof(StreamDPOpFlowVar64); + break; + + case ditRANDOM8 : + lpv8 =(StreamDPOpFlowVar8 *)p; + lpv8->dump(fd,"RAND8"); + p+=sizeof(StreamDPOpFlowVar8); + break; + case ditRANDOM16 : + lpv16 =(StreamDPOpFlowVar16 *)p; + lpv16->dump(fd,"RAND16"); + p+=sizeof(StreamDPOpFlowVar16); + break; + case ditRANDOM32 : + lpv32 =(StreamDPOpFlowVar32 *)p; + lpv32->dump(fd,"RAND32"); + p+=sizeof(StreamDPOpFlowVar32); + break; + case ditRANDOM64 : + lpv64 =(StreamDPOpFlowVar64 *)p; + lpv64->dump(fd,"RAND64"); + p+=sizeof(StreamDPOpFlowVar64); + break; + + case ditFIX_IPV4_CS : + lpIpv4Fix =(StreamDPOpIpv4Fix *)p; + lpIpv4Fix->dump(fd,"Ipv4Fix"); + p+=sizeof(StreamDPOpIpv4Fix); + break; + + case itPKT_WR8 : + lpw8 =(StreamDPOpPktWr8 *)p; + lpw8->dump(fd,"Wr8"); + p+=sizeof(StreamDPOpPktWr8); + break; + + case itPKT_WR16 : + lpw16 =(StreamDPOpPktWr16 *)p; + lpw16->dump(fd,"Wr16"); + p+=sizeof(StreamDPOpPktWr16); + break; + + case itPKT_WR32 : + lpw32 =(StreamDPOpPktWr32 *)p; + lpw32->dump(fd,"Wr32"); + p+=sizeof(StreamDPOpPktWr32); + break; + + case itPKT_WR64 : + lpw64 =(StreamDPOpPktWr64 *)p; + lpw64->dump(fd,"Wr64"); + p+=sizeof(StreamDPOpPktWr64); + break; + + case itCLIENT_VAR : + lp_client =(StreamDPOpClientsLimit *)p; + lp_client->dump(fd,"Client"); + p+=sizeof(StreamDPOpClientsLimit); + break; + + case itCLIENT_VAR_UNLIMIT : + lp_client_unlimited =(StreamDPOpClientsUnLimit *)p; + lp_client_unlimited->dump(fd,"ClientUnlimted"); + p+=sizeof(StreamDPOpClientsUnLimit); + break; + + + default: + assert(0); + } + }; +} + + +void StreamDPOpFlowVar8::dump(FILE *fd,std::string opt){ + fprintf(fd," %10s op:%lu, of:%lu, (%lu- %lu) \n", opt.c_str(),(ulong)m_op,(ulong)m_flow_offset,(ulong)m_min_val,(ulong)m_max_val); +} + +void StreamDPOpFlowVar16::dump(FILE *fd,std::string opt){ + fprintf(fd," %10s op:%lu, of:%lu, (%lu-%lu) \n", opt.c_str(),(ulong)m_op,(ulong)m_flow_offset,(ulong)m_min_val,(ulong)m_max_val); +} + +void StreamDPOpFlowVar32::dump(FILE *fd,std::string opt){ + fprintf(fd," %10s op:%lu, of:%lu, (%lu-%lu) \n", opt.c_str(),(ulong)m_op,(ulong)m_flow_offset,(ulong)m_min_val,(ulong)m_max_val); +} + +void StreamDPOpFlowVar64::dump(FILE *fd,std::string opt){ + fprintf(fd," %10s op:%lu, of:%lu, (%lu-%lu) \n", opt.c_str(),(ulong)m_op,(ulong)m_flow_offset,(ulong)m_min_val,(ulong)m_max_val); +} + +void StreamDPOpPktWr8::dump(FILE *fd,std::string opt){ + fprintf(fd," %10s op:%lu, flags:%lu, pkt_of:%lu, f_of:%lu \n", opt.c_str(),(ulong)m_op,(ulong)m_flags,(ulong)m_pkt_offset,(ulong)m_offset); } +void StreamDPOpPktWr16::dump(FILE *fd,std::string opt){ + fprintf(fd," %10s op:%lu, flags:%lu, pkt_of:%lu , f_of:%lu \n", opt.c_str(),(ulong)m_op,(ulong)m_flags,(ulong)m_pkt_offset,(ulong)m_offset); +} + +void StreamDPOpPktWr32::dump(FILE *fd,std::string opt){ + fprintf(fd," %10s op:%lu, flags:%lu, pkt_of:%lu , f_of:%lu \n", opt.c_str(),(ulong)m_op,(ulong)m_flags,(ulong)m_pkt_offset,(ulong)m_offset); +} + +void StreamDPOpPktWr64::dump(FILE *fd,std::string opt){ + fprintf(fd," %10s op:%lu, flags:%lu, pkt_of:%lu , f_of:%lu \n", opt.c_str(),(ulong)m_op,(ulong)m_flags,(ulong)m_pkt_offset,(ulong)m_offset); +} + + +void StreamDPOpIpv4Fix::dump(FILE *fd,std::string opt){ + fprintf(fd," %10s op:%lu, offset: %lu \n", opt.c_str(),(ulong)m_op,(ulong)m_offset); +} + + +void StreamDPOpClientsLimit::dump(FILE *fd,std::string opt){ + fprintf(fd," %10s op:%lu, flow_offset: %lu (%x-%x) (%x-%x) flow_limit :%lu flags:%x \n", opt.c_str(),(ulong)m_op,(ulong)m_flow_offset,m_min_ip,m_max_ip,m_min_port,m_max_port,(ulong)m_limit_flows,m_flags); +} + +void StreamDPOpClientsUnLimit::dump(FILE *fd,std::string opt){ + fprintf(fd," %10s op:%lu, flow_offset: %lu (%x-%x) flags:%x \n", opt.c_str(),(ulong)m_op,(ulong)m_flow_offset,m_min_ip,m_max_ip,m_flags); +} + + diff --git a/src/stateless/cp/trex_stream_vm.h b/src/stateless/cp/trex_stream_vm.h index 56edbcaf..e65a87e3 100644 --- a/src/stateless/cp/trex_stream_vm.h +++ b/src/stateless/cp/trex_stream_vm.h @@ -24,6 +24,526 @@ limitations under the License. #include <string> #include <stdint.h> #include <vector> +#include <unordered_map> +#include <assert.h> +#include <common/Network/Packet/IPHeader.h> +#include "pal_utl.h" +#include "mbuf.h" + + + + +class StreamVm; + + +/* in memory program */ + +struct StreamDPOpFlowVar8 { + uint8_t m_op; + uint8_t m_flow_offset; + uint8_t m_min_val; + uint8_t m_max_val; +public: + void dump(FILE *fd,std::string opt); + + inline void run_inc(uint8_t * flow_var) { + uint8_t * p=(flow_var+m_flow_offset); + *p=*p+1; + if (*p>m_max_val) { + *p=m_min_val; + } + } + + inline void run_dec(uint8_t * flow_var) { + uint8_t * p=(flow_var+m_flow_offset); + *p=*p-1; + if (*p<m_min_val) { + *p=m_max_val; + } + } + + inline void run_rand(uint8_t * flow_var) { + uint8_t * p=(flow_var+m_flow_offset); + *p= m_min_val + (rand() % (int)(m_max_val - m_min_val + 1)); + } + + +} __attribute__((packed)) ; + +struct StreamDPOpFlowVar16 { + uint8_t m_op; + uint8_t m_flow_offset; + uint16_t m_min_val; + uint16_t m_max_val; +public: + void dump(FILE *fd,std::string opt); + + inline void run_inc(uint8_t * flow_var) { + uint16_t * p=(uint16_t *)(flow_var+m_flow_offset); + *p=*p+1; + if (*p>m_max_val) { + *p=m_min_val; + } + } + + inline void run_dec(uint8_t * flow_var) { + uint16_t * p=(uint16_t *)(flow_var+m_flow_offset); + *p=*p-1; + if (*p<m_min_val) { + *p=m_max_val; + } + } + + inline void run_rand(uint8_t * flow_var) { + uint16_t * p=(uint16_t *)(flow_var+m_flow_offset); + *p= m_min_val + (rand() % (int)(m_max_val - m_min_val + 1)); + } + + + +} __attribute__((packed)) ; + +struct StreamDPOpFlowVar32 { + uint8_t m_op; + uint8_t m_flow_offset; + uint32_t m_min_val; + uint32_t m_max_val; +public: + void dump(FILE *fd,std::string opt); + + inline void run_inc(uint8_t * flow_var) { + uint32_t * p=(uint32_t *)(flow_var+m_flow_offset); + *p=*p+1; + if (*p>m_max_val) { + *p=m_min_val; + } + } + + inline void run_dec(uint8_t * flow_var) { + uint32_t * p=(uint32_t *)(flow_var+m_flow_offset); + *p=*p-1; + if (*p<m_min_val) { + *p=m_max_val; + } + } + + inline void run_rand(uint8_t * flow_var) { + uint32_t * p=(uint32_t *)(flow_var+m_flow_offset); + *p= m_min_val + (rand() % (int)(m_max_val - m_min_val + 1)); + } + +} __attribute__((packed)) ; + +struct StreamDPOpFlowVar64 { + uint8_t m_op; + uint8_t m_flow_offset; + uint64_t m_min_val; + uint64_t m_max_val; +public: + void dump(FILE *fd,std::string opt); + + inline void run_inc(uint8_t * flow_var) { + uint64_t * p=(uint64_t *)(flow_var+m_flow_offset); + *p=*p+1; + if (*p>m_max_val) { + *p=m_min_val; + } + } + + inline void run_dec(uint8_t * flow_var) { + uint64_t * p=(uint64_t *)(flow_var+m_flow_offset); + *p=*p-1; + if (*p<m_min_val) { + *p=m_max_val; + } + } + + inline void run_rand(uint8_t * flow_var) { + uint64_t * p=(uint64_t *)(flow_var+m_flow_offset); + *p= m_min_val + (rand() % (int)(m_max_val - m_min_val + 1)); + } + + +} __attribute__((packed)) ; + + +struct StreamDPOpPktWrBase { + enum { + PKT_WR_IS_BIG = 1 + }; /* for flags */ + + uint8_t m_op; + uint8_t m_flags; + uint8_t m_offset; + +public: + bool is_big(){ + return ( (m_flags &StreamDPOpPktWrBase::PKT_WR_IS_BIG) == StreamDPOpPktWrBase::PKT_WR_IS_BIG ?true:false); + } + +} __attribute__((packed)) ; + + +struct StreamDPOpPktWr8 : public StreamDPOpPktWrBase { + int8_t m_val_offset; + uint16_t m_pkt_offset; + +public: + void dump(FILE *fd,std::string opt); + + inline void wr(uint8_t * flow_var_base,uint8_t * pkt_base) { + uint8_t * p_pkt = (pkt_base+m_pkt_offset); + uint8_t * p_flow_var = (flow_var_base+m_offset); + *p_pkt=(*p_flow_var+m_val_offset); + + } + + +} __attribute__((packed)) ; + + +struct StreamDPOpPktWr16 : public StreamDPOpPktWrBase { + uint16_t m_pkt_offset; + int16_t m_val_offset; +public: + void dump(FILE *fd,std::string opt); + + inline void wr(uint8_t * flow_var_base,uint8_t * pkt_base) { + uint16_t * p_pkt = (uint16_t*)(pkt_base+m_pkt_offset); + uint16_t * p_flow_var = (uint16_t*)(flow_var_base+m_offset); + + if ( likely(is_big())){ + *p_pkt=PKT_HTONS((*p_flow_var+m_val_offset)); + }else{ + *p_pkt=(*p_flow_var+m_val_offset); + } + + } +} __attribute__((packed)); + +struct StreamDPOpPktWr32 : public StreamDPOpPktWrBase { + uint16_t m_pkt_offset; + int32_t m_val_offset; +public: + void dump(FILE *fd,std::string opt); + + inline void wr(uint8_t * flow_var_base,uint8_t * pkt_base) { + uint32_t * p_pkt = (uint32_t*)(pkt_base+m_pkt_offset); + uint32_t * p_flow_var = (uint32_t*)(flow_var_base+m_offset); + if ( likely(is_big())){ + *p_pkt=PKT_HTONL((*p_flow_var+m_val_offset)); + }else{ + *p_pkt=(*p_flow_var+m_val_offset); + } + } + +} __attribute__((packed)); + +struct StreamDPOpPktWr64 : public StreamDPOpPktWrBase { + uint16_t m_pkt_offset; + int64_t m_val_offset; + +public: + void dump(FILE *fd,std::string opt); + + inline void wr(uint8_t * flow_var_base,uint8_t * pkt_base) { + uint64_t * p_pkt = (uint64_t*)(pkt_base+m_pkt_offset); + uint64_t * p_flow_var = (uint64_t*)(flow_var_base+m_offset); + if ( likely(is_big())){ + *p_pkt=pal_ntohl64((*p_flow_var+m_val_offset)); + }else{ + *p_pkt=(*p_flow_var+m_val_offset); + } + } + + +} __attribute__((packed)); + +struct StreamDPOpIpv4Fix { + uint8_t m_op; + uint16_t m_offset; +public: + void dump(FILE *fd,std::string opt); + void run(uint8_t * pkt_base){ + IPHeader * ipv4= (IPHeader *)(pkt_base+m_offset); + ipv4->updateCheckSum(); + } + +} __attribute__((packed)); + + +/* flow varible of Client command */ +struct StreamDPFlowClient { + uint32_t cur_ip; + uint16_t cur_port; + uint32_t cur_flow_id; +} __attribute__((packed)); + + +struct StreamDPOpClientsLimit { + uint8_t m_op; + uint8_t m_flow_offset; /* offset into the flow var bytes */ + uint8_t m_flags; + uint8_t m_pad; + uint16_t m_min_port; + uint16_t m_max_port; + + uint32_t m_min_ip; + uint32_t m_max_ip; + uint32_t m_limit_flows; /* limit the number of flows */ + +public: + void dump(FILE *fd,std::string opt); + inline void run(uint8_t * flow_var_base) { + StreamDPFlowClient * lp= (StreamDPFlowClient *)(flow_var_base+m_flow_offset); + lp->cur_ip++; + if (lp->cur_ip > m_max_ip ) { + lp->cur_ip= m_min_ip; + lp->cur_port++; + if (lp->cur_port > m_max_port) { + lp->cur_port = m_min_port; + } + } + + if (m_limit_flows) { + lp->cur_flow_id++; + if ( lp->cur_flow_id > m_limit_flows ){ + /* reset to the first flow */ + lp->cur_flow_id = 1; + lp->cur_ip = m_min_ip; + lp->cur_port = m_min_port; + } + } + } + + +} __attribute__((packed)); + +struct StreamDPOpClientsUnLimit { + enum __MIN_PORT { + CLIENT_UNLIMITED_MIN_PORT = 1025 + }; + + uint8_t m_op; + uint8_t m_flow_offset; /* offset into the flow var bytes */ + uint8_t m_flags; + uint8_t m_pad; + uint32_t m_min_ip; + uint32_t m_max_ip; + +public: + void dump(FILE *fd,std::string opt); + inline void run(uint8_t * flow_var_base) { + StreamDPFlowClient * lp= (StreamDPFlowClient *)(flow_var_base+m_flow_offset); + lp->cur_ip++; + if (lp->cur_ip > m_max_ip ) { + lp->cur_ip= m_min_ip; + lp->cur_port++; + if (lp->cur_port == 0) { + lp->cur_port = StreamDPOpClientsUnLimit::CLIENT_UNLIMITED_MIN_PORT; + } + } + } + +} __attribute__((packed)); + + +/* datapath instructions */ +class StreamDPVmInstructions { +public: + enum INS_TYPE { + ditINC8 , + ditINC16 , + ditINC32 , + ditINC64 , + + ditDEC8 , + ditDEC16 , + ditDEC32 , + ditDEC64 , + + ditRANDOM8 , + ditRANDOM16 , + ditRANDOM32 , + ditRANDOM64 , + + ditFIX_IPV4_CS , + + itPKT_WR8 , + itPKT_WR16 , + itPKT_WR32 , + itPKT_WR64 , + itCLIENT_VAR , + itCLIENT_VAR_UNLIMIT + }; + + +public: + void clear(); + void add_command(void *buffer,uint16_t size); + uint8_t * get_program(); + uint32_t get_program_size(); + + + void Dump(FILE *fd); + + +private: + std::vector<uint8_t> m_inst_list; +}; + + +class StreamDPVmInstructionsRunner { +public: + inline void run(uint32_t program_size, + uint8_t * program, /* program */ + uint8_t * flow_var, /* flow var */ + uint8_t * pkt); /* pkt */ + +}; + + +inline void StreamDPVmInstructionsRunner::run(uint32_t program_size, + uint8_t * program, /* program */ + uint8_t * flow_var, /* flow var */ + uint8_t * pkt){ + + + uint8_t * p=program; + uint8_t * p_end=p+program_size; + + + union ua_ { + StreamDPOpFlowVar8 *lpv8; + StreamDPOpFlowVar16 *lpv16; + StreamDPOpFlowVar32 *lpv32; + StreamDPOpFlowVar64 *lpv64; + StreamDPOpIpv4Fix *lpIpv4Fix; + StreamDPOpPktWr8 *lpw8; + StreamDPOpPktWr16 *lpw16; + StreamDPOpPktWr32 *lpw32; + StreamDPOpPktWr64 *lpw64; + StreamDPOpClientsLimit *lpcl; + StreamDPOpClientsUnLimit *lpclu; + } ua ; + + while ( p < p_end) { + uint8_t op_code=*p; + switch (op_code) { + + case StreamDPVmInstructions::itCLIENT_VAR : + ua.lpcl =(StreamDPOpClientsLimit *)p; + ua.lpcl->run(flow_var); + p+=sizeof(StreamDPOpClientsLimit); + break; + + case StreamDPVmInstructions::itCLIENT_VAR_UNLIMIT : + ua.lpclu =(StreamDPOpClientsUnLimit *)p; + ua.lpclu->run(flow_var); + p+=sizeof(StreamDPOpClientsUnLimit); + break; + + case StreamDPVmInstructions::ditINC8 : + ua.lpv8 =(StreamDPOpFlowVar8 *)p; + ua.lpv8->run_inc(flow_var); + p+=sizeof(StreamDPOpFlowVar8); + break; + + case StreamDPVmInstructions::ditINC16 : + ua.lpv16 =(StreamDPOpFlowVar16 *)p; + ua.lpv16->run_inc(flow_var); + p+=sizeof(StreamDPOpFlowVar16); + break; + case StreamDPVmInstructions::ditINC32 : + ua.lpv32 =(StreamDPOpFlowVar32 *)p; + ua.lpv32->run_inc(flow_var); + p+=sizeof(StreamDPOpFlowVar32); + break; + case StreamDPVmInstructions::ditINC64 : + ua.lpv64 =(StreamDPOpFlowVar64 *)p; + ua.lpv64->run_inc(flow_var); + p+=sizeof(StreamDPOpFlowVar64); + break; + + case StreamDPVmInstructions::ditDEC8 : + ua.lpv8 =(StreamDPOpFlowVar8 *)p; + ua.lpv8->run_dec(flow_var); + p+=sizeof(StreamDPOpFlowVar8); + break; + case StreamDPVmInstructions::ditDEC16 : + ua.lpv16 =(StreamDPOpFlowVar16 *)p; + ua.lpv16->run_dec(flow_var); + p+=sizeof(StreamDPOpFlowVar16); + break; + case StreamDPVmInstructions::ditDEC32 : + ua.lpv32 =(StreamDPOpFlowVar32 *)p; + ua.lpv32->run_dec(flow_var); + p+=sizeof(StreamDPOpFlowVar32); + break; + case StreamDPVmInstructions::ditDEC64 : + ua.lpv64 =(StreamDPOpFlowVar64 *)p; + ua.lpv64->run_dec(flow_var); + p+=sizeof(StreamDPOpFlowVar64); + break; + + case StreamDPVmInstructions::ditRANDOM8 : + ua.lpv8 =(StreamDPOpFlowVar8 *)p; + ua.lpv8->run_rand(flow_var); + p+=sizeof(StreamDPOpFlowVar8); + break; + case StreamDPVmInstructions::ditRANDOM16 : + ua.lpv16 =(StreamDPOpFlowVar16 *)p; + ua.lpv16->run_rand(flow_var); + p+=sizeof(StreamDPOpFlowVar16); + break; + case StreamDPVmInstructions::ditRANDOM32 : + ua.lpv32 =(StreamDPOpFlowVar32 *)p; + ua.lpv32->run_rand(flow_var); + p+=sizeof(StreamDPOpFlowVar32); + break; + case StreamDPVmInstructions::ditRANDOM64 : + ua.lpv64 =(StreamDPOpFlowVar64 *)p; + ua.lpv64->run_rand(flow_var); + p+=sizeof(StreamDPOpFlowVar64); + break; + + case StreamDPVmInstructions::ditFIX_IPV4_CS : + ua.lpIpv4Fix =(StreamDPOpIpv4Fix *)p; + ua.lpIpv4Fix->run(pkt); + p+=sizeof(StreamDPOpIpv4Fix); + break; + + case StreamDPVmInstructions::itPKT_WR8 : + ua.lpw8 =(StreamDPOpPktWr8 *)p; + ua.lpw8->wr(flow_var,pkt); + p+=sizeof(StreamDPOpPktWr8); + break; + + case StreamDPVmInstructions::itPKT_WR16 : + ua.lpw16 =(StreamDPOpPktWr16 *)p; + ua.lpw16->wr(flow_var,pkt); + p+=sizeof(StreamDPOpPktWr16); + break; + + case StreamDPVmInstructions::itPKT_WR32 : + ua.lpw32 =(StreamDPOpPktWr32 *)p; + ua.lpw32->wr(flow_var,pkt); + p+=sizeof(StreamDPOpPktWr32); + break; + + case StreamDPVmInstructions::itPKT_WR64 : + ua.lpw64 =(StreamDPOpPktWr64 *)p; + ua.lpw64->wr(flow_var,pkt); + p+=sizeof(StreamDPOpPktWr64); + break; + default: + assert(0); + } + }; +}; + + + /** * interface for stream VM instruction @@ -32,8 +552,24 @@ limitations under the License. class StreamVmInstruction { public: + enum INS_TYPE { + itNONE = 0, + itFIX_IPV4_CS = 4, + itFLOW_MAN = 5, + itPKT_WR = 6, + itFLOW_CLIENT = 7 + + }; + + typedef uint8_t instruction_type_t ; + + virtual instruction_type_t get_instruction_type()=0; + virtual ~StreamVmInstruction(); + virtual void Dump(FILE *fd)=0; + + private: static const std::string m_name; }; @@ -48,8 +584,14 @@ public: } -private: - uint16_t m_pkt_offset; + virtual instruction_type_t get_instruction_type(){ + return ( StreamVmInstruction::itFIX_IPV4_CS); + } + + virtual void Dump(FILE *fd); + +public: + uint16_t m_pkt_offset; /* the offset of IPv4 header from the start of the packet */ }; /** @@ -61,6 +603,10 @@ class StreamVmInstructionFlowMan : public StreamVmInstruction { public: + virtual instruction_type_t get_instruction_type(){ + return ( StreamVmInstruction::itFLOW_MAN); + } + /** * different types of operations on the object */ @@ -85,7 +631,16 @@ public: } + virtual void Dump(FILE *fd); + + void sanity_check(uint32_t ins_id,StreamVm *lp); + private: + void sanity_check_valid_range(uint32_t ins_id,StreamVm *lp); + void sanity_check_valid_size(uint32_t ins_id,StreamVm *lp); + void sanity_check_valid_opt(uint32_t ins_id,StreamVm *lp); + +public: /* flow var name */ @@ -105,6 +660,83 @@ private: }; + +/** + * flow client instruction - save state for client range and port range + * + * @author hhaim + */ +class StreamVmInstructionFlowClient : public StreamVmInstruction { + +public: + enum client_flags_e { + CLIENT_F_UNLIMITED_FLOWS=1, /* unlimited number of flow, don't care about ports */ + }; + + + virtual instruction_type_t get_instruction_type(){ + return ( StreamVmInstruction::itFLOW_CLIENT); + } + + + StreamVmInstructionFlowClient(const std::string &var_name, + uint32_t client_min_value, + uint32_t client_max_value, + uint16_t port_min, + uint16_t port_max, + uint32_t limit_num_flows, /* zero means don't limit */ + uint16_t flags + ) { + m_var_name = var_name; + m_client_min = client_min_value; + m_client_max = client_max_value; + + m_port_min = port_min; + m_port_max = port_max; + + m_limit_num_flows = limit_num_flows; + m_flags = flags; + } + + virtual void Dump(FILE *fd); + + static uint8_t get_flow_var_size(){ + return (4+2+4); + } + + bool is_unlimited_flows(){ + return ( (m_flags & StreamVmInstructionFlowClient::CLIENT_F_UNLIMITED_FLOWS ) == + StreamVmInstructionFlowClient::CLIENT_F_UNLIMITED_FLOWS ); + } +public: + + + /* flow var name */ + std::string m_var_name; + + uint32_t m_client_min; // min ip + uint32_t m_client_max; // max ip + uint16_t m_port_min; // start port + uint16_t m_port_max; // start port + uint32_t m_limit_num_flows; // number of flows + uint16_t m_flags; +}; + + + +class VmFlowVarRec { +public: + uint32_t m_offset; + union { + StreamVmInstructionFlowMan * m_ins_flowv; + StreamVmInstructionFlowClient * m_ins_flow_client; + } m_ins; + uint8_t m_size_bytes; +}; + + + + /** * write flow var to packet * @@ -121,7 +753,14 @@ public: m_pkt_offset(pkt_offset), m_add_value(add_value), m_is_big_endian(is_big_endian) {} -private: + + virtual instruction_type_t get_instruction_type(){ + return ( StreamVmInstruction::itPKT_WR); + } + + virtual void Dump(FILE *fd); + +public: /* flow var name to write */ std::string m_flow_var_name; @@ -136,12 +775,161 @@ private: bool m_is_big_endian; }; + +/** + * describes a VM program for DP + * + */ + +class StreamVmDp { +public: + StreamVmDp(){ + m_bss_ptr=NULL; + m_program_ptr =NULL ; + m_bss_size=0; + m_program_size=0; + m_max_pkt_offset_change=0; + } + + StreamVmDp( uint8_t * bss, + uint16_t bss_size, + uint8_t * prog, + uint16_t prog_size, + uint16_t max_pkt_offset + ){ + + if (bss) { + assert(bss_size); + m_bss_ptr=(uint8_t*)malloc(bss_size); + assert(m_bss_ptr); + memcpy(m_bss_ptr,bss,bss_size); + m_bss_size=bss_size; + }else{ + m_bss_ptr=NULL; + m_bss_size=0; + } + + if (prog) { + assert(prog_size); + m_program_ptr=(uint8_t*)malloc(prog_size); + memcpy(m_program_ptr,prog,prog_size); + m_program_size = prog_size; + }else{ + m_program_ptr = NULL; + m_program_size=0; + } + m_max_pkt_offset_change =max_pkt_offset; + } + + ~StreamVmDp(){ + if (m_bss_ptr) { + free(m_bss_ptr); + m_bss_ptr=0; + m_bss_size=0; + } + if (m_program_ptr) { + free(m_program_ptr); + m_program_size=0; + m_program_ptr=0; + } + } + + StreamVmDp * clone() const { + StreamVmDp * lp= new StreamVmDp(m_bss_ptr, + m_bss_size, + m_program_ptr, + m_program_size, + m_max_pkt_offset_change + ); + assert(lp); + return (lp); + } + + uint8_t* clone_bss(){ + assert(m_bss_size>0); + uint8_t *p=(uint8_t *)malloc(m_bss_size); + assert(p); + memcpy(p,m_bss_ptr,m_bss_size); + return (p); + } + + uint16_t get_bss_size(){ + return(m_bss_size); + } + + uint8_t* get_bss(){ + return (m_bss_ptr); + } + + uint8_t* get_program(){ + return (m_program_ptr); + } + + uint16_t get_program_size(){ + return (m_program_size); + } + + uint16_t get_max_packet_update_offset(){ + return (m_max_pkt_offset_change); + } + +private: + uint8_t * m_bss_ptr; /* pointer to the data section */ + uint8_t * m_program_ptr; /* pointer to the program */ + uint16_t m_bss_size; + uint16_t m_program_size; /* program size*/ + uint16_t m_max_pkt_offset_change; + +}; + + /** * describes a VM program * */ class StreamVm { + public: + enum STREAM_VM { + svMAX_FLOW_VAR = 64, /* maximum flow varible */ + svMAX_PACKET_OFFSET_CHANGE = 512 + }; + + + + StreamVm(){ + m_bss=0; + m_pkt_size=0; + m_cur_var_offset=0; + } + + + /* set packet size */ + void set_packet_size(uint16_t pkt_size){ + m_pkt_size = pkt_size; + } + + uint16_t get_packet_size(){ + return ( m_pkt_size); + } + + + StreamVmDp * cloneAsVmDp(){ + + StreamVmDp * lp= new StreamVmDp(get_bss_ptr(), + get_bss_size(), + get_dp_instruction_buffer()->get_program(), + get_dp_instruction_buffer()->get_program_size(), + get_max_packet_update_offset() + ); + assert(lp); + return (lp); + + } + + bool is_vm_empty() { + return (m_inst_list.size() == 0); + } /** * add new instruction to the VM @@ -155,17 +943,75 @@ public: */ const std::vector<StreamVmInstruction *> & get_instruction_list(); + StreamDPVmInstructions * get_dp_instruction_buffer(); + + uint16_t get_bss_size(){ + return (m_cur_var_offset ); + } + + uint8_t * get_bss_ptr(){ + return (m_bss ); + } + + + uint16_t get_max_packet_update_offset(){ + return ( m_max_field_update ); + } + + + /** * compile the VM * return true of success, o.w false * */ - bool compile(); + void compile(); ~StreamVm(); + void Dump(FILE *fd); + + /* raise exception */ + void err(const std::string &err); + private: + + /* lookup for varible offset, */ + bool var_lookup(const std::string &var_name,VmFlowVarRec & var); + + void var_clear_table(); + + bool var_add(const std::string &var_name,VmFlowVarRec & var); + + uint16_t get_var_offset(const std::string &var_name); + + void build_flow_var_table() ; + + void build_bss(); + + void build_program(); + + void alloc_bss(); + + void free_bss(); + +private: + + void clean_max_field_cnt(); + + void add_field_cnt(uint16_t new_cnt); + +private: + uint16_t m_pkt_size; + uint16_t m_cur_var_offset; + uint16_t m_max_field_update; /* the location of the last byte that is going to be changed in the packet */ + std::vector<StreamVmInstruction *> m_inst_list; + std::unordered_map<std::string, VmFlowVarRec> m_flow_var_offset; + uint8_t * m_bss; + + StreamDPVmInstructions m_instructions; + }; #endif /* __TREX_STREAM_VM_API_H__ */ diff --git a/src/stateless/cp/trex_streams_compiler.cpp b/src/stateless/cp/trex_streams_compiler.cpp index 478e09f8..c4900e66 100644 --- a/src/stateless/cp/trex_streams_compiler.cpp +++ b/src/stateless/cp/trex_streams_compiler.cpp @@ -376,6 +376,26 @@ TrexStreamsCompiler::compile(uint8_t port_id, double factor, std::string *fail_msg) { + try { + return compile_internal(port_id,streams,objs,dp_core_count,factor,fail_msg); + } catch (const TrexException &ex) { + if (fail_msg) { + *fail_msg = ex.what(); + } else { + std::cout << ex.what(); + } + return false; + } + +} +bool +TrexStreamsCompiler::compile_internal(uint8_t port_id, + const std::vector<TrexStream *> &streams, + std::vector<TrexStreamsCompiledObj *> &objs, + uint8_t dp_core_count, + double factor, + std::string *fail_msg) { + #if 0 for (auto stream : streams) { stream->Dump(stdout); @@ -387,16 +407,7 @@ TrexStreamsCompiler::compile(uint8_t port_id, /* compile checks */ - try { - pre_compile_check(streams, nodes); - } catch (const TrexException &ex) { - if (fail_msg) { - *fail_msg = ex.what(); - } else { - std::cout << ex.what(); - } - return false; - } + pre_compile_check(streams, nodes); /* check if all are cont. streams */ bool all_continues = true; @@ -424,7 +435,6 @@ TrexStreamsCompiler::compile(uint8_t port_id, /* compile a single stream to all cores */ compile_stream(stream, factor, dp_core_count, objs, nodes); - } return true; @@ -457,6 +467,10 @@ TrexStreamsCompiler::compile_stream(const TrexStream *stream, double per_core_rate = (stream->m_pps * (factor / dp_core_count)); int per_core_burst_total_pkts = (stream->m_burst_total_pkts / dp_core_count); + /* compile VM */ + /* fix this const away problem */ + ((TrexStream *)stream)->compile(); + std::vector<TrexStream *> per_core_streams(dp_core_count); /* for each core - creates its own version of the stream */ @@ -486,6 +500,7 @@ TrexStreamsCompiler::compile_stream(const TrexStream *stream, } + /************************************** * streams graph *************************************/ diff --git a/src/stateless/cp/trex_streams_compiler.h b/src/stateless/cp/trex_streams_compiler.h index 7fe2dbf2..d2b0cd1d 100644 --- a/src/stateless/cp/trex_streams_compiler.h +++ b/src/stateless/cp/trex_streams_compiler.h @@ -103,6 +103,13 @@ public: private: + bool compile_internal(uint8_t port_id, + const std::vector<TrexStream *> &streams, + std::vector<TrexStreamsCompiledObj *> &objs, + uint8_t dp_core_count, + double factor, + std::string *fail_msg); + void pre_compile_check(const std::vector<TrexStream *> &streams, GraphNodeMap & nodes); void allocate_pass(const std::vector<TrexStream *> &streams, GraphNodeMap *nodes); @@ -118,6 +125,8 @@ private: std::vector<TrexStreamsCompiledObj *> &objs, GraphNodeMap &nodes); + void compile_stream_vm(TrexStream *stream); + std::vector<std::string> m_warnings; }; diff --git a/src/stateless/dp/trex_stateless_dp_core.cpp b/src/stateless/dp/trex_stateless_dp_core.cpp index 22ca922d..585ff2c7 100644 --- a/src/stateless/dp/trex_stateless_dp_core.cpp +++ b/src/stateless/dp/trex_stateless_dp_core.cpp @@ -106,12 +106,56 @@ std::string CGenNodeStateless::get_stream_state_str(stream_state_t stream_state) } +rte_mbuf_t * CGenNodeStateless::alloc_node_with_vm(){ + + rte_mbuf_t * m; + /* alloc small packet buffer*/ + uint16_t prefix_size = prefix_header_size(); + m = CGlobalInfo::pktmbuf_alloc( get_socket_id(), prefix_size ); + if (m==0) { + return (m); + } + /* TBD remove this, should handle cases of error */ + assert(m); + char *p=rte_pktmbuf_append(m, prefix_size); + memcpy( p ,m_original_packet_data_prefix, prefix_size); + + + /* run the VM program */ + StreamDPVmInstructionsRunner runner; + + runner.run( m_vm_program_size, + m_vm_program, + m_vm_flow_var, + (uint8_t*)p); + + + rte_mbuf_t * m_const = get_const_mbuf(); + if ( m_const != NULL) { + utl_rte_pktmbuf_add_after(m,m_const); + } + return (m); +} + + void CGenNodeStateless::free_stl_node(){ /* if we have cache mbuf free it */ rte_mbuf_t * m=get_cache_mbuf(); if (m) { rte_pktmbuf_free(m); m_cache_mbuf=0; + }else{ + /* non cache - must have an header */ + m=get_const_mbuf(); + if (m) { + rte_pktmbuf_free(m); /* reduce the ref counter */ + } + free_prefix_header(); + } + if (m_vm_flow_var) { + /* free flow var */ + free(m_vm_flow_var); + m_vm_flow_var=0; } } @@ -423,6 +467,7 @@ TrexStatelessDpCore::add_stream(TrexStatelessDpPerPort * lp_port, CGenNodeStateless *node = m_core->create_node_sl(); /* add periodic */ + node->m_cache_mbuf=0; node->m_type = CGenNode::STATELESS_PKT; node->m_ref_stream_info = stream->clone_as_dp(); @@ -442,6 +487,10 @@ TrexStatelessDpCore::add_stream(TrexStatelessDpPerPort * lp_port, pkt_dir_t dir = m_core->m_node_gen.m_v_if->port_id_to_dir(stream->m_port_id); node->m_flags = 0; + node->m_src_port =0; + node->m_original_packet_data_prefix = 0; + + /* set socket id */ node->set_socket_id(m_core->m_node_gen.m_socket_id); @@ -486,23 +535,77 @@ TrexStatelessDpCore::add_stream(TrexStatelessDpPerPort * lp_port, node->m_port_id = stream->m_port_id; - /* allocate const mbuf */ - rte_mbuf_t *m = CGlobalInfo::pktmbuf_alloc(node->get_socket_id(), pkt_size); - assert(m); - - char *p = rte_pktmbuf_append(m, pkt_size); - assert(p); - /* copy the packet */ - memcpy(p,stream_pkt,pkt_size); - /* set dir 0 or 1 client or server */ node->set_mbuf_cache_dir(dir); - /* TBD repace the mac if req we should add flag */ - m_core->m_node_gen.m_v_if->update_mac_addr_from_global_cfg(dir, m); - /* set the packet as a readonly */ - node->set_cache_mbuf(m); + if (stream->is_vm() == false ) { + /* no VM */ + + node->m_vm_flow_var = NULL; + node->m_vm_program = NULL; + node->m_vm_program_size =0; + + /* allocate const mbuf */ + rte_mbuf_t *m = CGlobalInfo::pktmbuf_alloc(node->get_socket_id(), pkt_size); + assert(m); + + char *p = rte_pktmbuf_append(m, pkt_size); + assert(p); + /* copy the packet */ + memcpy(p,stream_pkt,pkt_size); + + /* TBD repace the mac if req we should add flag */ + m_core->m_node_gen.m_v_if->update_mac_addr_from_global_cfg(dir,(uint8_t*) p); + + /* set the packet as a readonly */ + node->set_cache_mbuf(m); + + node->m_original_packet_data_prefix =0; + }else{ + + /* set the program */ + TrexStream * local_mem_stream = node->m_ref_stream_info; + + StreamVmDp * lpDpVm = local_mem_stream->getDpVm(); + + node->m_vm_flow_var = lpDpVm->clone_bss(); /* clone the flow var */ + node->m_vm_program = lpDpVm->get_program(); /* same ref to the program */ + node->m_vm_program_size =lpDpVm->get_program_size(); + + + /* we need to copy the object */ + if ( pkt_size > stream->m_vm_prefix_size ) { + /* we need const packet */ + uint16_t const_pkt_size = pkt_size - stream->m_vm_prefix_size ; + rte_mbuf_t *m = CGlobalInfo::pktmbuf_alloc(node->get_socket_id(), const_pkt_size ); + assert(m); + + char *p = rte_pktmbuf_append(m, const_pkt_size); + assert(p); + + /* copy packet data */ + memcpy(p,(stream_pkt+ stream->m_vm_prefix_size),const_pkt_size); + + node->set_const_mbuf(m); + } + + + if (stream->m_vm_prefix_size > pkt_size ) { + stream->m_vm_prefix_size = pkt_size; + } + /* copy the headr */ + uint16_t header_size = stream->m_vm_prefix_size; + assert(header_size); + node->alloc_prefix_header(header_size); + uint8_t *p=node->m_original_packet_data_prefix; + assert(p); + + memcpy(p,stream_pkt , header_size); + /* TBD repace the mac if req we should add flag */ + m_core->m_node_gen.m_v_if->update_mac_addr_from_global_cfg(dir, p); + } + CDpOneStream one_stream; diff --git a/src/stateless/dp/trex_stream_node.h b/src/stateless/dp/trex_stream_node.h index 111af845..d33785fe 100644 --- a/src/stateless/dp/trex_stream_node.h +++ b/src/stateless/dp/trex_stream_node.h @@ -54,6 +54,16 @@ struct CGenNodeStateless : public CGenNodeBase { friend class TrexStatelessDpCore; public: + + /* flags MASKS*/ + enum { + SL_NODE_FLAGS_DIR =1, //USED by master + SL_NODE_FLAGS_MBUF_CACHE =2, //USED by master + + SL_NODE_CONST_MBUF =4 + + }; + enum { ss_FREE_RESUSE =1, /* should be free by scheduler */ ss_INACTIVE =2, /* will be active by other stream or stopped */ @@ -83,13 +93,20 @@ private: uint32_t m_multi_bursts; /* in case of multi_burst how many bursts */ /* cache line 1 */ - TrexStream * m_ref_stream_info; /* the stream info */ + TrexStream * m_ref_stream_info; /* the stream info */ CGenNodeStateless * m_next_stream; - /* pad to match the size of CGenNode */ - uint8_t m_pad_end[56]; + uint8_t * m_original_packet_data_prefix; /* pointer to the original first pointer 64/128/512 */ + + /* Fast Field VM section */ + uint8_t * m_vm_flow_var; /* pointer to the vm flow var */ + uint8_t * m_vm_program; /* pointer to the program */ + uint16_t m_vm_program_size; /* up to 64K op codes */ + /* End Fast Field VM Section */ + /* pad to match the size of CGenNode */ + uint8_t m_pad_end[30]; public: @@ -256,13 +273,51 @@ public: } inline rte_mbuf_t * get_cache_mbuf(){ - if ( m_flags &NODE_FLAGS_MBUF_CACHE ) { + if ( m_flags & NODE_FLAGS_MBUF_CACHE ) { + return ((rte_mbuf_t *)m_cache_mbuf); + }else{ + return ((rte_mbuf_t *)0); + } + } + + inline void set_const_mbuf(rte_mbuf_t * m){ + m_cache_mbuf=(void *)m; + m_flags |= SL_NODE_CONST_MBUF; + } + + inline rte_mbuf_t * get_const_mbuf(){ + if ( m_flags &SL_NODE_CONST_MBUF ) { return ((rte_mbuf_t *)m_cache_mbuf); }else{ return ((rte_mbuf_t *)0); } } + /* prefix header exits only in non cache mode size is 64/128/512 other are not possible right now */ + inline void alloc_prefix_header(uint16_t size){ + set_prefix_header_size(size); + m_original_packet_data_prefix = (uint8_t *)malloc(size); + assert(m_original_packet_data_prefix); + } + + inline void free_prefix_header(){ + if (m_original_packet_data_prefix) { + free(m_original_packet_data_prefix); + } + } + + /* prefix headr could be 64/128/512 */ + inline void set_prefix_header_size(uint16_t size){ + m_src_port=size; + } + + inline uint16_t prefix_header_size(){ + return (m_src_port); + } + + + rte_mbuf_t * alloc_node_with_vm(); + void free_stl_node(); public: |