summaryrefslogtreecommitdiffstats
path: root/src/stateless/cp/trex_stream_vm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/stateless/cp/trex_stream_vm.cpp')
-rw-r--r--src/stateless/cp/trex_stream_vm.cpp1710
1 files changed, 1710 insertions, 0 deletions
diff --git a/src/stateless/cp/trex_stream_vm.cpp b/src/stateless/cp/trex_stream_vm.cpp
new file mode 100644
index 00000000..716ae5d4
--- /dev/null
+++ b/src/stateless/cp/trex_stream_vm.cpp
@@ -0,0 +1,1710 @@
+/*
+ Itay Marom
+ Hanoch Haim
+ Cisco Systems, Inc.
+*/
+
+/*
+Copyright (c) 2015-2015 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 <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>
+#include <inttypes.h>
+
+/**
+ * provides some tools for the fast rand function
+ * that is used by the datapath
+ * some features of this function is different
+ * from a regular random
+ * (such as average can be off by few percents)
+ *
+ * @author imarom (7/24/2016)
+ */
+class FastRandUtils {
+public:
+
+ /**
+ * searches the target in the cache
+ * if not found iterativly calculate it
+ * and add it to the cache
+ *
+ */
+ double calc_fastrand_avg(uint16_t target) {
+ auto search = m_avg_cache.find(target);
+ if (search != m_avg_cache.end()) {
+ return search->second;
+ }
+
+ /* not found - calculate it */
+ double avg = iterate_calc(target);
+
+ /* if there is enough space - to the cache */
+ if (m_avg_cache.size() <= G_MAX_CACHE_SIZE) {
+ m_avg_cache[target] = avg;
+ }
+
+ return avg;
+ }
+
+private:
+
+ /**
+ * hard calculate a value using iterations
+ *
+ */
+ double iterate_calc(uint16_t target) {
+ const int num_samples = 10000;
+ uint64_t tmp = 0;
+ uint32_t seed = 1;
+
+ for (int i = 0; i < num_samples; i++) {
+ tmp += fastrand(seed) % (target + 1);
+ }
+
+ return (tmp / double(num_samples));
+ }
+
+ std::unordered_map<uint16_t, double> m_avg_cache;
+ static const uint16_t G_MAX_CACHE_SIZE = 9230;
+};
+
+static FastRandUtils g_fastrand_util;
+
+
+void StreamVmInstructionFixChecksumIpv4::Dump(FILE *fd){
+ fprintf(fd," fix_check_sum , %lu \n",(ulong)m_pkt_offset);
+}
+
+void StreamVmInstructionFixHwChecksum::Dump(FILE *fd){
+ fprintf(fd," fix_hw_cs %lu:%lu \n",(ulong)m_l2_len,(ulong)m_l3_len);
+}
+
+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
+}
+
+
+
+uint8_t StreamVmInstructionFlowMan::set_bss_init_value(uint8_t *p) {
+
+ uint64_t prev = peek_prev();
+
+ switch (m_size_bytes) {
+ case 1:
+ *p=(uint8_t)prev;
+ return 1;
+ case 2:
+ *((uint16_t*)p)=(uint16_t)prev;
+ return 2;
+ case 4:
+ *((uint32_t*)p)=(uint32_t)prev;
+ return 4;
+ case 8:
+ *((uint64_t*)p)=(uint64_t)prev;
+ return 8;
+ default:
+ assert(0);
+ return(0);
+ }
+}
+
+
+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 StreamVmInstructionFlowRandLimit::Dump(FILE *fd){
+ fprintf(fd," flow_var_rand_limit , %s ,%lu, ",m_var_name.c_str(),(ulong)m_size_bytes);
+ fprintf(fd," (%lu:%lu:%lu) (min:%lu,max:%lu) \n",m_limit,(ulong)m_size_bytes,(ulong)m_seed,m_min_value,m_max_value);
+}
+
+void StreamVmInstructionFlowRandLimit::sanity_check(uint32_t ins_id,StreamVm *lp){
+ sanity_check_valid_size(ins_id,lp);
+}
+
+
+uint8_t StreamVmInstructionFlowRandLimit::set_bss_init_value(uint8_t *p){
+ uint8_t res;
+
+ typedef union ua_ {
+ RandMemBss8 *lpv8;
+ RandMemBss16 *lpv16;
+ RandMemBss32 *lpv32;
+ RandMemBss64 *lpv64;
+ } ua_t ;
+
+ ua_t u;
+
+
+ switch (m_size_bytes) {
+ case 1:
+ u.lpv8=(RandMemBss8 *)p;
+ u.lpv8->m_seed=m_seed;
+ u.lpv8->m_cnt=0;
+ u.lpv8->m_val=0;
+ res=sizeof(RandMemBss8);
+ break;
+ case 2:
+ u.lpv16=(RandMemBss16 *)p;
+ u.lpv16->m_seed=m_seed;
+ u.lpv16->m_cnt=0;
+ u.lpv16->m_val=0;
+ res=sizeof(RandMemBss16);
+ break;
+ case 4:
+ u.lpv32=(RandMemBss32 *)p;
+ u.lpv32->m_seed=m_seed;
+ u.lpv32->m_cnt=0;
+ u.lpv32->m_val=0;
+ res=sizeof(RandMemBss32);
+ break;
+ case 8:
+ u.lpv64=(RandMemBss64 *)p;
+ u.lpv64->m_seed=m_seed;
+ u.lpv64->m_cnt=0;
+ u.lpv64->m_val=0;
+ res=sizeof(RandMemBss64);
+ break;
+ default:
+ assert(0);
+ }
+ return (res);
+}
+
+
+void StreamVmInstructionFlowRandLimit::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) {
+ uint64_t limit = (1ULL<<((i+1)*8))-1;
+ /* check limit */
+ if ( m_limit == 0) {
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' limit " << m_limit << " can't be zero " ;
+ lp->err(ss.str());
+ }
+
+ if ( m_limit > limit) {
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' limit " << m_limit << " is bigger than size " << m_size_bytes ;
+ lp->err(ss.str());
+ }
+ return;
+ }
+ }
+
+ std::stringstream ss;
+
+ ss << "instruction id '" << ins_id << "' has non valid length " << m_size_bytes ;
+
+ lp->err(ss.str());
+}
+
+
+
+void StreamVmInstructionWriteMaskToPkt::Dump(FILE *fd){
+ fprintf(fd," flow_var:%s, offset:%lu, cast_size:%lu, mask:0x%lx, shift:%ld, add:%ld, is_big:%lu \n",m_flow_var_name.c_str(),(ulong)m_pkt_offset,(ulong)m_pkt_cast_size,(ulong)m_mask,(long)m_shift,(long)m_add_value,(ulong)(m_is_big_endian?1:0));
+}
+
+
+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:%lu) \n",m_init_value,m_min_value,m_max_value,m_step);
+}
+
+
+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 StreamVmInstructionChangePktSize::Dump(FILE *fd){
+ fprintf(fd," pkt_size_change , %s \n",m_flow_var_name.c_str() );
+}
+
+
+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);
+}
+
+
+uint8_t StreamVmInstructionFlowClient::set_bss_init_value(uint8_t *p) {
+
+ uint32_t bss_ip;
+ uint16_t bss_port;
+
+ /* fetch the previous values by 1 */
+ peek_prev(bss_ip, bss_port, 1);
+
+ /* ip */
+ *((uint32_t*)p) = bss_ip;
+ p += 4;
+
+ /* port */
+ *((uint16_t*)p) = bss_port;
+ p += 2;
+
+ /* reserve */
+ *((uint32_t*)p) = 0;
+ p += 4;
+
+ return (get_flow_var_size());
+}
+
+
+
+
+/***************************
+ * StreamVmInstruction
+ *
+ **************************/
+StreamVmInstruction::~StreamVmInstruction() {
+
+}
+
+/***************************
+ * StreamVm
+ *
+ **************************/
+void StreamVm::add_instruction(StreamVmInstruction *inst) {
+
+ if (inst->get_instruction_type() == StreamVmInstruction::itFLOW_MAN) {
+ StreamVmInstructionFlowMan * ins_man=(StreamVmInstructionFlowMan *)inst;
+ if (ins_man->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM) {
+ m_is_random_var = true;
+ }
+ }
+
+ if (inst->get_instruction_type() == StreamVmInstruction::itPKT_SIZE_CHANGE) {
+ m_is_change_pkt_size = true;
+ }
+
+ if (inst->need_split()) {
+ m_is_split_needed = true;
+ }
+
+ 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;
+}
+
+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 */
+
+ /* if we found allocate BSS +4 bytes */
+ if ( m_is_random_var ){
+ VmFlowVarRec var;
+
+ var.m_offset = m_cur_var_offset;
+ var.m_ins.m_ins_flowv = NULL;
+ var.m_size_bytes = sizeof(uint32_t);
+ var_add("___random___",var);
+ m_cur_var_offset += sizeof(uint32_t);
+ }
+
+ 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_RAND_LIMIT ){
+
+ StreamVmInstructionFlowRandLimit * ins_man=(StreamVmInstructionFlowRandLimit *)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_flow_rand_limit = ins_man;
+ var.m_size_bytes = ins_man->m_size_bytes; /* used for write*/
+ var_add(ins_man->m_var_name,var);
+ m_cur_var_offset += ins_man->m_size_bytes*2 + sizeof(uint32_t) ; /* see RandMemBss8 types */
+ }
+ }
+
+
+ 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 variables current size is :" << m_cur_var_offset << " maximum support is " << StreamVm::svMAX_FLOW_VAR;
+ err(ss.str());
+ }
+ ins_id++;
+ }
+
+
+ ins_id=0;
+
+ /* second interation for sanity check and fixups*/
+ for (auto inst : m_inst_list) {
+
+
+ if (inst->get_instruction_type() == StreamVmInstruction::itPKT_SIZE_CHANGE ) {
+ StreamVmInstructionChangePktSize *lpPkt =(StreamVmInstructionChangePktSize *)inst;
+
+ VmFlowVarRec var;
+ if ( var_lookup(lpPkt->m_flow_var_name ,var) == false){
+
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' packet size with no valid flow variable name '" << lpPkt->m_flow_var_name << "'" ;
+ err(ss.str());
+ }
+
+ if ( var.m_size_bytes != 2 ) {
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' packet size change should point to a flow variable with size 2 ";
+ err(ss.str());
+ }
+
+ if ( var.m_ins.m_ins_flowv->get_instruction_type() != StreamVmInstruction::itFLOW_MAN ){
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' packet size change should point to a simple flow variable type (Random/Client) types are not supported ";
+ err(ss.str());
+
+ }
+
+ if ( var.m_ins.m_ins_flowv->m_max_value > m_pkt_size) {
+ var.m_ins.m_ins_flowv->m_max_value =m_pkt_size;
+ }
+
+ if (var.m_ins.m_ins_flowv->m_min_value > m_pkt_size) {
+ var.m_ins.m_ins_flowv->m_min_value = m_pkt_size;
+ }
+
+
+ if ( var.m_ins.m_ins_flowv->m_min_value >= var.m_ins.m_ins_flowv->m_max_value ) {
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' min packet size " << var.m_ins.m_ins_flowv->m_min_value << " is bigger or eq to max packet size " << var.m_ins.m_ins_flowv->m_max_value;
+ err(ss.str());
+ }
+
+ if ( var.m_ins.m_ins_flowv->m_min_value < 60) {
+ var.m_ins.m_ins_flowv->m_min_value =60;
+ }
+
+ /* expected packet size calculation */
+
+ /* for random packet size - we need to find the average */
+ if (var.m_ins.m_ins_flowv->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM) {
+ uint16_t range = var.m_ins.m_ins_flowv->m_max_value - var.m_ins.m_ins_flowv->m_min_value;
+ m_expected_pkt_size = var.m_ins.m_ins_flowv->m_min_value + g_fastrand_util.calc_fastrand_avg(range);
+ } else {
+ m_expected_pkt_size = (var.m_ins.m_ins_flowv->m_min_value + var.m_ins.m_ins_flowv->m_max_value) / 2.0;
+ }
+
+ }
+ }
+
+}
+
+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();
+ int var_cnt=0;
+ 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();
+
+ if (ins_type == StreamVmInstruction::itFIX_HW_CS) {
+ StreamVmInstructionFixHwChecksum *lpFix =(StreamVmInstructionFixHwChecksum *)inst;
+ if (lpFix->m_l2_len < 14 ) {
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' fix hw offset l2 " << lpFix->m_l2_len << " is lower than 14 ";
+ err(ss.str());
+ }
+
+ if (lpFix->m_l3_len < 20 ) {
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' fix hw offset l3 " << lpFix->m_l3_len << " is lower than 20 ";
+ err(ss.str());
+ }
+
+ uint16_t total_l4_offset = lpFix->m_l2_len + lpFix->m_l3_len;
+ uint16_t l4_header_size =0;
+
+
+ if ( m_pkt ) {
+ /* not pre compile for estimation of average packet size */
+ bool packet_is_ipv4=true;
+ IPHeader * ipv4= (IPHeader *)(m_pkt+lpFix->m_l2_len);
+ if (ipv4->getVersion() ==4 ) {
+ packet_is_ipv4=true;
+ if (ipv4->getSize() != lpFix->m_l3_len ) {
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' fix hw command IPv4 header size is not valid " << ipv4->getSize() ;
+ err(ss.str());
+ }
+ if ( !((ipv4->getNextProtocol() == IPHeader::Protocol::TCP) ||
+ (ipv4->getNextProtocol() == IPHeader::Protocol::UDP) ) ) {
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' fix hw command L4 should be TCP or UDP " << ipv4->getSize() ;
+ err(ss.str());
+ }
+ }else{
+ if (ipv4->getVersion() ==6) {
+ packet_is_ipv4=false;
+ }else{
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' fix hw command should work on IPv4 or IPv6 " ;
+ err(ss.str());
+ }
+ }
+
+ StreamDPOpHwCsFix ipv_fix;
+ ipv_fix.m_l2_len = lpFix->m_l2_len;
+ ipv_fix.m_l3_len = lpFix->m_l3_len;
+ ipv_fix.m_op = StreamDPVmInstructions::ditFIX_HW_CS;
+
+ if (packet_is_ipv4) {
+ if ( ipv4->getNextProtocol() == IPHeader::Protocol::TCP ){
+ /* Ipv4 TCP */
+ ipv_fix.m_ol_flags = (PKT_TX_IPV4 | PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM);
+ l4_header_size = TCP_HEADER_LEN;
+ }else{
+ assert( ipv4->getNextProtocol() == IPHeader::Protocol::UDP );
+ /* Ipv4 UDP */
+ ipv_fix.m_ol_flags = (PKT_TX_IPV4 | PKT_TX_IP_CKSUM | PKT_TX_UDP_CKSUM);
+ l4_header_size = UDP_HEADER_LEN;
+ }
+ }else{
+ /* Ipv6*/
+ /* in this case we need to scan the Ipv6 headers */
+ /* TBD replace with parser of IPv6 function */
+ if ( lpFix->m_l4_type==StreamVmInstructionFixHwChecksum::L4_TYPE_TCP ){
+ ipv_fix.m_ol_flags = (PKT_TX_IPV6 | PKT_TX_TCP_CKSUM);
+ l4_header_size = TCP_HEADER_LEN;
+ }else{
+ if ( lpFix->m_l4_type==StreamVmInstructionFixHwChecksum::L4_TYPE_UDP ){
+ ipv_fix.m_ol_flags = (PKT_TX_IPV6 | PKT_TX_UDP_CKSUM);
+ l4_header_size = UDP_HEADER_LEN;
+ }else{
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' fix hw command offsets should be TCP or UDP ";
+ err(ss.str());
+ }
+ }
+ }
+
+ if ( (total_l4_offset + l4_header_size) > m_pkt_size ) {
+
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' fix hw command offsets " << (total_l4_offset + l4_header_size) << " is too high relative to packet size "<< m_pkt_size;
+ err(ss.str());
+ }
+
+ /* add the instruction*/
+ m_instructions.add_command(&ipv_fix,sizeof(ipv_fix));
+
+ /* mark R/W of the packet */
+ add_field_cnt(total_l4_offset + l4_header_size);
+ }
+ }
+
+ /* itFIX_IPV4_CS */
+ if (ins_type == StreamVmInstruction::itFIX_IPV4_CS) {
+ StreamVmInstructionFixChecksumIpv4 *lpFix =(StreamVmInstructionFixChecksumIpv4 *)inst;
+
+ 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());
+ }
+
+ uint16_t offset_next_layer = IPV4_HDR_LEN;
+
+ if ( m_pkt ){
+ IPHeader * ipv4= (IPHeader *)(m_pkt+lpFix->m_pkt_offset);
+ offset_next_layer = ipv4->getSize();
+ }
+
+ if (offset_next_layer<IPV4_HDR_LEN) {
+ offset_next_layer=IPV4_HDR_LEN;
+ }
+
+ if ( (lpFix->m_pkt_offset + offset_next_layer) > m_pkt_size ) {
+
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' fix ipv4 command offset " << lpFix->m_pkt_offset << "plus "<<offset_next_layer<< " is too high relative to packet size "<< m_pkt_size;
+ err(ss.str());
+ }
+ /* calculate this offset from the packet */
+ add_field_cnt(lpFix->m_pkt_offset + offset_next_layer);
+
+ 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));
+ }
+
+ if (ins_type == StreamVmInstruction::itFLOW_RAND_LIMIT) {
+ StreamVmInstructionFlowRandLimit *lpMan =(StreamVmInstructionFlowRandLimit *)inst;
+ var_cnt++;
+
+ if (lpMan->m_size_bytes == 1 ){
+ StreamDPOpFlowRandLimit8 fv8;
+ fv8.m_op = StreamDPVmInstructions::ditRAND_LIMIT8 ;
+ fv8.m_flow_offset = get_var_offset(lpMan->m_var_name);
+ fv8.m_limit = (uint8_t)lpMan->m_limit;
+ fv8.m_seed = (uint32_t)lpMan->m_seed;
+ 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 ){
+ StreamDPOpFlowRandLimit16 fv16;
+ fv16.m_op = StreamDPVmInstructions::ditRAND_LIMIT16 ;
+ fv16.m_flow_offset = get_var_offset(lpMan->m_var_name);
+ fv16.m_limit = (uint16_t)lpMan->m_limit;
+ fv16.m_seed = (uint32_t)lpMan->m_seed;
+ 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 ){
+ StreamDPOpFlowRandLimit32 fv32;
+ fv32.m_op = StreamDPVmInstructions::ditRAND_LIMIT32 ;
+ fv32.m_flow_offset = get_var_offset(lpMan->m_var_name);
+ fv32.m_limit = (uint32_t)lpMan->m_limit;
+ fv32.m_seed = (uint32_t)lpMan->m_seed;
+ 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 ){
+ StreamDPOpFlowRandLimit64 fv64;
+ fv64.m_op = StreamDPVmInstructions::ditRAND_LIMIT64 ;
+ fv64.m_flow_offset = get_var_offset(lpMan->m_var_name);
+ fv64.m_limit = lpMan->m_limit;
+ fv64.m_seed = (uint32_t)lpMan->m_seed;
+ fv64.m_min_val = lpMan->m_min_value;
+ fv64.m_max_val = lpMan->m_max_value;
+ m_instructions.add_command(&fv64,sizeof(fv64));
+ }
+ }
+
+ /* flow man */
+ if (ins_type == StreamVmInstruction::itFLOW_MAN) {
+ StreamVmInstructionFlowMan *lpMan =(StreamVmInstructionFlowMan *)inst;
+
+ var_cnt++;
+
+ /* flow var size 1 */
+ if (lpMan->m_size_bytes == 1 ) {
+ uint8_t op = StreamDPVmInstructions::ditINC8_STEP;
+
+ switch (lpMan->m_op) {
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_INC:
+ op = StreamDPVmInstructions::ditINC8_STEP;
+ break;
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC:
+ op = StreamDPVmInstructions::ditDEC8_STEP;
+ break;
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM:
+ op = StreamDPVmInstructions::ditRANDOM8;
+ break;
+ default:
+ assert(0);
+ }
+
+ StreamDPOpFlowVar8Step 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;
+ fv8.m_step = (uint8_t)lpMan->m_step;
+ m_instructions.add_command(&fv8,sizeof(fv8));
+ }
+
+ /* flow var size 2 */
+ if (lpMan->m_size_bytes == 2) {
+ uint8_t op = StreamDPVmInstructions::ditINC16_STEP;
+
+ switch (lpMan->m_op) {
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_INC:
+ op = StreamDPVmInstructions::ditINC16_STEP;
+ break;
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC:
+ op = StreamDPVmInstructions::ditDEC16_STEP;
+ break;
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM:
+ op = StreamDPVmInstructions::ditRANDOM16;
+ break;
+ default:
+ assert(0);
+ }
+
+ StreamDPOpFlowVar16Step 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;
+ fv16.m_step = (uint16_t)lpMan->m_step;
+
+ m_instructions.add_command(&fv16,sizeof(fv16));
+ }
+
+
+ /* flow var size 4 */
+ if (lpMan->m_size_bytes == 4) {
+ uint8_t op = StreamDPVmInstructions::ditINC32_STEP;
+
+ switch (lpMan->m_op) {
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_INC:
+ op = StreamDPVmInstructions::ditINC32_STEP;
+ break;
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC:
+ op = StreamDPVmInstructions::ditDEC32_STEP;
+ break;
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM:
+ op = StreamDPVmInstructions::ditRANDOM32;
+ break;
+ default:
+ assert(0);
+ }
+
+ StreamDPOpFlowVar32Step 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;
+ fv32.m_step = (uint32_t)lpMan->m_step;
+ m_instructions.add_command(&fv32,sizeof(fv32));
+
+ }
+
+ /* flow var size 8 */
+ if (lpMan->m_size_bytes == 8) {
+ uint8_t op = StreamDPVmInstructions::ditINC64_STEP;
+
+ switch (lpMan->m_op) {
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_INC:
+ op = StreamDPVmInstructions::ditINC64_STEP;
+ break;
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC:
+ op = StreamDPVmInstructions::ditDEC64_STEP;
+ break;
+ case StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM:
+ op = StreamDPVmInstructions::ditRANDOM64;
+ break;
+ default:
+ assert(0);
+ }
+
+ StreamDPOpFlowVar64Step 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;
+ fv64.m_step = (uint64_t)lpMan->m_step;
+ 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 variable 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::itPKT_WR_MASK) {
+ StreamVmInstructionWriteMaskToPkt *lpPkt =(StreamVmInstructionWriteMaskToPkt *)inst;
+
+ VmFlowVarRec var;
+
+ uint8_t cast_size = lpPkt->m_pkt_cast_size;
+ if (!((cast_size==4)||(cast_size==2)||(cast_size==1))){
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << " cast size should be 1,2,4 it is "<<lpPkt->m_pkt_cast_size;
+ err(ss.str());
+ }
+
+ if ( var_lookup(lpPkt->m_flow_var_name ,var) == false){
+
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' packet write with no valid flow variable name '" << lpPkt->m_flow_var_name << "'" ;
+ err(ss.str());
+ }
+
+ if (lpPkt->m_pkt_offset + lpPkt->m_pkt_cast_size > m_pkt_size ) {
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' packet write with packet_offset " << (lpPkt->m_pkt_offset + lpPkt->m_pkt_cast_size) << " bigger than packet size "<< m_pkt_size;
+ err(ss.str());
+ }
+
+
+ add_field_cnt(lpPkt->m_pkt_offset + lpPkt->m_pkt_cast_size);
+
+
+ uint8_t op_size = var.m_size_bytes;
+ bool is_big = lpPkt->m_is_big_endian;
+ uint8_t flags = (is_big?StreamDPOpPktWrMask::MASK_PKT_WR_IS_BIG:0);
+ uint8_t flow_offset = get_var_offset(lpPkt->m_flow_var_name);
+
+ /* read LSB in case of 64bit variable */
+ if (op_size == 8) {
+ op_size = 4;
+ if ( is_big ) {
+ flow_offset +=4;
+ }
+ }
+
+ StreamDPOpPktWrMask pmask;
+ pmask.m_op = StreamDPVmInstructions::itPKT_WR_MASK;
+ pmask.m_flags = flags;
+ pmask.m_var_offset = flow_offset;
+ pmask.m_shift = lpPkt->m_shift;
+ pmask.m_add_value = lpPkt->m_add_value;
+ pmask.m_pkt_cast_size = cast_size;
+ pmask.m_flowv_cast_size = op_size;
+ pmask.m_pkt_offset = lpPkt->m_pkt_offset;
+ pmask.m_mask = lpPkt->m_mask;
+
+ m_instructions.add_command(&pmask,sizeof(pmask));
+ }
+
+
+ if (ins_type == StreamVmInstruction::itFLOW_CLIENT) {
+ var_cnt++;
+ 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_ip.m_min_value;
+ client_cmd.m_max_ip = lpMan->m_ip.m_max_value;
+ 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.m_min_value;
+ client_cmd.m_max_port = lpMan->m_port.m_max_value;
+ client_cmd.m_step_port = lpMan->m_port.m_step;
+
+ client_cmd.m_min_ip = lpMan->m_ip.m_min_value;
+ client_cmd.m_max_ip = lpMan->m_ip.m_max_value;
+ client_cmd.m_step_ip = lpMan->m_ip.m_step;
+
+ client_cmd.m_init_ip = lpMan->m_ip.m_init_value;
+ client_cmd.m_init_port = lpMan->m_port.m_init_value;
+
+ client_cmd.m_limit_flows = lpMan->m_limit_num_flows;
+ m_instructions.add_command(&client_cmd,sizeof(client_cmd));
+ }
+ }
+
+
+ if (ins_type == StreamVmInstruction::itPKT_SIZE_CHANGE ) {
+ StreamVmInstructionChangePktSize *lpPkt =(StreamVmInstructionChangePktSize *)inst;
+
+ VmFlowVarRec var;
+ if ( var_lookup(lpPkt->m_flow_var_name ,var) == false){
+
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' packet size with no valid flow variable name '" << lpPkt->m_flow_var_name << "'" ;
+ err(ss.str());
+ }
+
+ if ( var.m_size_bytes != 2 ) {
+ std::stringstream ss;
+ ss << "instruction id '" << ins_id << "' packet size change should point to a flow variable with size 2 ";
+ err(ss.str());
+ }
+
+ uint8_t flow_offset = get_var_offset(lpPkt->m_flow_var_name);
+
+ StreamDPOpPktSizeChange pkt_size_ch;
+ pkt_size_ch.m_op =StreamDPVmInstructions::itPKT_SIZE_CHANGE;
+ pkt_size_ch.m_flow_offset = flow_offset;
+ m_instructions.add_command(&pkt_size_ch,sizeof(pkt_size_ch));
+ }
+
+ ins_id++;
+ }
+
+
+ if ( var_cnt ==0 ){
+ std::stringstream ss;
+ ss << "It is not valid to have a VM program without a variable or tuple generator ";
+ err(ss.str());
+ }
+}
+
+
+void StreamVm::build_bss() {
+ alloc_bss();
+ uint8_t * p=(uint8_t *)m_bss;
+
+ if ( m_is_random_var ){
+ *((uint32_t*)p)=rand();
+ p+=sizeof(uint32_t);
+ }
+
+ for (auto inst : m_inst_list) {
+ p+=inst->set_bss_init_value(p);
+ }
+}
+
+
+/**
+ * clone VM from this VM to 'other'
+ *
+ * @author imarom (22-Dec-15)
+ *
+ * @param other
+ */
+void
+StreamVm::clone(StreamVm &other) const {
+ /* clear previous if any exists */
+ for (auto instr : other.m_inst_list) {
+ delete instr;
+ }
+
+ other.m_is_random_var = false;
+ other.m_is_change_pkt_size = false;
+ other.m_is_split_needed = false;
+ other.m_is_compiled = false;
+
+ other.m_inst_list.clear();
+
+ for (auto instr : m_inst_list) {
+ StreamVmInstruction *new_instr = instr->clone();
+ other.add_instruction(new_instr);
+ }
+}
+
+/**
+ * actual work - compile the VM
+ *
+ */
+void StreamVm::compile(uint16_t pkt_len) {
+
+ if (is_vm_empty()) {
+ return;
+ }
+
+ m_pkt_size = pkt_len;
+
+ /* 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());
+ }
+
+ /* calculate the mbuf size that we should allocate */
+ m_prefix_size = calc_writable_mbuf_size(get_max_packet_update_offset(), m_pkt_size);
+
+ m_is_compiled = true;
+}
+
+
+StreamVm::~StreamVm() {
+ for (auto inst : m_inst_list) {
+ delete inst;
+ }
+ free_bss();
+}
+
+/**
+ * calculate expected packet size of stream's VM
+ *
+ */
+double
+StreamVm::calc_expected_pkt_size(uint16_t regular_pkt_size) const {
+
+ /* if no packet size change - simply return the regular packet size */
+ if (!m_is_change_pkt_size) {
+ return regular_pkt_size;
+ }
+ /* if we have an instruction that changes the packet size
+ so find the expected size
+ we must compile the VM temporarly to get this value
+ */
+
+ StreamVm dummy;
+
+ this->clone(dummy);
+ dummy.compile(regular_pkt_size);
+
+ assert(dummy.m_expected_pkt_size != 0);
+
+ return (dummy.m_expected_pkt_size);
+}
+
+/**
+* return a pointer to a flow var / client var
+* by name if exists, otherwise NULL
+*
+*/
+StreamVmInstructionVar *
+StreamVm::lookup_var_by_name(const std::string &var_name) {
+ for (StreamVmInstruction *inst : m_inst_list) {
+
+ /* try to cast up to a variable */
+ StreamVmInstructionVar *var = dynamic_cast<StreamVmInstructionVar *>(inst);
+ if (!var) {
+ continue;
+ }
+
+ if (var->get_var_name() == var_name) {
+ return var;
+ }
+
+ }
+
+ return NULL;
+}
+
+
+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;
+
+ StreamDPOpFlowVar8Step *lpv8s;
+ StreamDPOpFlowVar16Step *lpv16s;
+ StreamDPOpFlowVar32Step *lpv32s;
+ StreamDPOpFlowVar64Step *lpv64s;
+
+ StreamDPOpHwCsFix *lpHwFix;
+
+ StreamDPOpIpv4Fix *lpIpv4Fix;
+ StreamDPOpPktWr8 *lpw8;
+ StreamDPOpPktWr16 *lpw16;
+ StreamDPOpPktWr32 *lpw32;
+ StreamDPOpPktWr64 *lpw64;
+ StreamDPOpPktWrMask *lpwrmask;
+ StreamDPOpClientsLimit *lp_client;
+ StreamDPOpClientsUnLimit *lp_client_unlimited;
+ StreamDPOpPktSizeChange *lp_pkt_size_change;
+
+ StreamDPOpFlowRandLimit8 * lpv_rl8;
+ StreamDPOpFlowRandLimit16 * lpv_rl16;
+ StreamDPOpFlowRandLimit32 * lpv_rl32;
+ StreamDPOpFlowRandLimit64 * lpv_rl64;
+
+ while ( p < p_end) {
+ uint8_t op_code=*p;
+ switch (op_code) {
+
+ case ditRANDOM8 :
+ lpv8s =(StreamDPOpFlowVar8Step *)p;
+ lpv8s->dump(fd,"RAND8");
+ p+=sizeof(StreamDPOpFlowVar8Step);
+ break;
+ case ditRANDOM16 :
+ lpv16s =(StreamDPOpFlowVar16Step *)p;
+ lpv16s->dump(fd,"RAND16");
+ p+=sizeof(StreamDPOpFlowVar16Step);
+ break;
+ case ditRANDOM32 :
+ lpv32s =(StreamDPOpFlowVar32Step *)p;
+ lpv32s->dump(fd,"RAND32");
+ p+=sizeof(StreamDPOpFlowVar32Step);
+ break;
+ case ditRANDOM64 :
+ lpv64s =(StreamDPOpFlowVar64Step *)p;
+ lpv64s->dump(fd,"RAND64");
+ p+=sizeof(StreamDPOpFlowVar64Step);
+ break;
+
+ case ditFIX_HW_CS :
+ lpHwFix =(StreamDPOpHwCsFix *)p;
+ lpHwFix->dump(fd,"HwFixCs");
+ p+=sizeof(StreamDPOpHwCsFix);
+ 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;
+
+ case itPKT_SIZE_CHANGE :
+ lp_pkt_size_change =(StreamDPOpPktSizeChange *)p;
+ lp_pkt_size_change->dump(fd,"pkt_size_c");
+ p+=sizeof(StreamDPOpPktSizeChange);
+ break;
+
+ case ditINC8_STEP :
+ lpv8s =(StreamDPOpFlowVar8Step *)p;
+ lpv8s->dump(fd,"INC8_STEP");
+ p+=sizeof(StreamDPOpFlowVar8Step);
+ break;
+ case ditINC16_STEP :
+ lpv16s =(StreamDPOpFlowVar16Step *)p;
+ lpv16s->dump(fd,"INC16_STEP");
+ p+=sizeof(StreamDPOpFlowVar16Step);
+ break;
+ case ditINC32_STEP :
+ lpv32s =(StreamDPOpFlowVar32Step *)p;
+ lpv32s->dump(fd,"INC32_STEP");
+ p+=sizeof(StreamDPOpFlowVar32Step);
+ break;
+ case ditINC64_STEP :
+ lpv64s =(StreamDPOpFlowVar64Step *)p;
+ lpv64s->dump(fd,"INC64_STEP");
+ p+=sizeof(StreamDPOpFlowVar64Step);
+ break;
+
+ case ditDEC8_STEP :
+ lpv8s =(StreamDPOpFlowVar8Step *)p;
+ lpv8s->dump(fd,"DEC8_DEC");
+ p+=sizeof(StreamDPOpFlowVar8Step);
+ break;
+ case ditDEC16_STEP :
+ lpv16s =(StreamDPOpFlowVar16Step *)p;
+ lpv16s->dump(fd,"DEC16_STEP");
+ p+=sizeof(StreamDPOpFlowVar16Step);
+ break;
+ case ditDEC32_STEP :
+ lpv32s =(StreamDPOpFlowVar32Step *)p;
+ lpv32s->dump(fd,"DEC32_STEP");
+ p+=sizeof(StreamDPOpFlowVar32Step);
+ break;
+ case ditDEC64_STEP :
+ lpv64s =(StreamDPOpFlowVar64Step *)p;
+ lpv64s->dump(fd,"DEC64_STEP");
+ p+=sizeof(StreamDPOpFlowVar64Step);
+ break;
+
+ case itPKT_WR_MASK :
+ lpwrmask =(StreamDPOpPktWrMask *)p;
+ lpwrmask->dump(fd,"WR_MASK");
+ p+=sizeof(StreamDPOpPktWrMask);
+ break;
+
+ case ditRAND_LIMIT8 :
+ lpv_rl8 =(StreamDPOpFlowRandLimit8 *)p;
+ lpv_rl8->dump(fd,"RAND_LIMIT8");
+ p+=sizeof(StreamDPOpFlowRandLimit8);
+ break;
+
+ case ditRAND_LIMIT16 :
+ lpv_rl16 =(StreamDPOpFlowRandLimit16 *)p;
+ lpv_rl16->dump(fd,"RAND_LIMIT16");
+ p+=sizeof(StreamDPOpFlowRandLimit16);
+ break;
+
+ case ditRAND_LIMIT32 :
+ lpv_rl32 =(StreamDPOpFlowRandLimit32 *)p;
+ lpv_rl32->dump(fd,"RAND_LIMIT32");
+ p+=sizeof(StreamDPOpFlowRandLimit32);
+ break;
+
+ case ditRAND_LIMIT64 :
+ lpv_rl64 =(StreamDPOpFlowRandLimit64 *)p;
+ lpv_rl64->dump(fd,"RAND_LIMIT64");
+ p+=sizeof(StreamDPOpFlowRandLimit64);
+ break;
+
+ default:
+ assert(0);
+ }
+ };
+}
+
+
+void StreamDPOpFlowVar8Step::dump(FILE *fd,std::string opt){
+ fprintf(fd," %10s op:%lu, of:%lu, (%lu-%lu-%lu) \n", opt.c_str(),(ulong)m_op,(ulong)m_flow_offset,(ulong)m_min_val,(ulong)m_max_val,(ulong)m_step);
+}
+
+void StreamDPOpFlowVar16Step::dump(FILE *fd,std::string opt){
+ fprintf(fd," %10s op:%lu, of:%lu, (%lu-%lu-%lu) \n", opt.c_str(),(ulong)m_op,(ulong)m_flow_offset,(ulong)m_min_val,(ulong)m_max_val,(ulong)m_step);
+}
+
+void StreamDPOpFlowVar32Step::dump(FILE *fd,std::string opt){
+ fprintf(fd," %10s op:%lu, of:%lu, (%lu-%lu-%lu) \n", opt.c_str(),(ulong)m_op,(ulong)m_flow_offset,(ulong)m_min_val,(ulong)m_max_val,(ulong)m_step);
+}
+
+void StreamDPOpFlowVar64Step::dump(FILE *fd,std::string opt){
+ fprintf(fd," %10s op:%lu, of:%lu, (%lu-%lu-%lu) \n", opt.c_str(),(ulong)m_op,(ulong)m_flow_offset,(ulong)m_min_val,(ulong)m_max_val,(ulong)m_step);
+}
+
+
+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 StreamDPOpPktWrMask::dump(FILE *fd,std::string opt){
+ fprintf(fd," %10s op:%lu, flags:%lu, var_of:%lu , (%ld-%lu-%lu-%lu-%lu) \n", opt.c_str(),(ulong)m_op,(ulong)m_flags,(ulong)m_var_offset,(long)m_shift,(ulong)m_pkt_offset,(ulong)m_mask,(ulong)m_pkt_cast_size,(ulong)m_flowv_cast_size);
+}
+
+
+void StreamDPOpHwCsFix::dump(FILE *fd,std::string opt){
+ fprintf(fd," %10s op:%lu, lens: %lu,%lu \n", opt.c_str(),(ulong)m_op,(ulong)m_l2_len,(ulong)m_l3_len);
+
+}
+
+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);
+}
+
+void StreamDPOpPktSizeChange::dump(FILE *fd,std::string opt){
+ fprintf(fd," %10s op:%lu, flow_offset: %lu \n", opt.c_str(),(ulong)m_op,(ulong)m_flow_offset);
+}
+
+
+void StreamDPOpFlowRandLimit8::dump(FILE *fd,std::string opt){
+ fprintf(fd," %10s, flow_offset: %lu limit :%lu seed:%x (%x-%x) \n", opt.c_str(),(ulong)m_flow_offset,(ulong)m_limit,m_seed,m_min_val,m_max_val);
+}
+
+void StreamDPOpFlowRandLimit16::dump(FILE *fd,std::string opt){
+ fprintf(fd," %10s, flow_offset: %lu limit :%lu seed:%x (%x-%x) \n", opt.c_str(),(ulong)m_flow_offset,(ulong)m_limit,m_seed,m_min_val,m_max_val);
+}
+
+void StreamDPOpFlowRandLimit32::dump(FILE *fd,std::string opt){
+ fprintf(fd," %10s, flow_offset: %lu limit :%lu seed:%x (%x-%x) \n", opt.c_str(),(ulong)m_flow_offset,(ulong)m_limit,m_seed,m_min_val,m_max_val);
+}
+
+void StreamDPOpFlowRandLimit64::dump(FILE *fd,std::string opt){
+ fprintf(fd," %10s, flow_offset: %lu limit :%lu seed:%x (%lu-%lu) \n", opt.c_str(),(ulong)m_flow_offset,(ulong)m_limit,m_seed,m_min_val,m_max_val);
+}
+
+
+
+
+void StreamDPOpPktWrMask::wr(uint8_t * flow_var_base,
+ uint8_t * pkt_base) {
+ uint32_t val=0;
+ uint8_t * pv=(flow_var_base+m_var_offset);
+ /* read flow var with the right size */
+ switch (m_flowv_cast_size) {
+ case 1:
+ val= (uint32_t)(*((uint8_t*)pv));
+ break;
+ case 2:
+ val=(uint32_t)(*((uint16_t*)pv));
+ break;
+ case 4:
+ val=(*((uint32_t*)pv));
+ break;
+ default:
+ assert(0);
+ }
+
+ val+=m_add_value;
+
+ /* shift the flow var val */
+ if (m_shift>0) {
+ val=val<<m_shift;
+ }else{
+ if (m_shift<0) {
+ val=val>>(-m_shift);
+ }
+ }
+
+ uint8_t * p_pkt = pkt_base+m_pkt_offset;
+ uint32_t pkt_val=0;
+
+ /* RMW */
+ if ( likely( is_big() ) ) {
+
+ switch (m_pkt_cast_size) {
+ case 1:
+ pkt_val= (uint32_t)(*((uint8_t*)p_pkt));
+ pkt_val = ((pkt_val & ~m_mask) | (val & m_mask)) & 0xff;
+ *p_pkt=pkt_val;
+ break;
+ case 2:
+ pkt_val= (uint32_t)PKT_NTOHS((*((uint16_t*)p_pkt)));
+ pkt_val = ((pkt_val & ~m_mask) | (val & m_mask)) & 0xffff;
+ *((uint16_t*)p_pkt)=PKT_NTOHS(pkt_val);
+ break;
+ case 4:
+ pkt_val= (uint32_t)PKT_NTOHL((*((uint32_t*)p_pkt)));
+ pkt_val = ((pkt_val & ~m_mask) | (val & m_mask)) ;
+ *((uint32_t*)p_pkt)=PKT_NTOHL(pkt_val);
+ break;
+ default:
+ assert(0);
+ }
+ }else{
+ switch (m_flowv_cast_size) {
+ case 1:
+ pkt_val= (uint32_t)(*((uint8_t*)p_pkt));
+ pkt_val = ((pkt_val & ~m_mask) | (val & m_mask)) & 0xff;
+ *p_pkt=pkt_val;
+ break;
+ case 2:
+ pkt_val= (uint32_t)(*((uint16_t*)p_pkt));
+ pkt_val = ((pkt_val & ~m_mask) | (val & m_mask)) & 0xffff;
+ *((uint16_t*)p_pkt)=pkt_val;
+ break;
+ case 4:
+ pkt_val= (uint32_t)(*((uint32_t*)p_pkt));
+ pkt_val = ((pkt_val & ~m_mask) | (val & m_mask)) ;
+ *((uint32_t*)p_pkt)=pkt_val;
+ break;
+ default:
+ assert(0);
+ }
+ }
+}
+
+
+
+
+void StreamDPVmInstructionsRunner::slow_commands(uint8_t op_code,
+ uint8_t * flow_var, /* flow var */
+ uint8_t * pkt,
+ uint8_t * & p){
+ ua_t ua;
+
+ switch (op_code) {
+ case StreamDPVmInstructions::itPKT_WR_MASK:
+ ua.lpwr_mask =(StreamDPOpPktWrMask *)p;
+ ua.lpwr_mask->wr(flow_var,pkt);
+ p+=sizeof(StreamDPOpPktWrMask);
+ break;
+
+ case StreamDPVmInstructions::ditRAND_LIMIT8:
+ ua.lpv_rl8 =(StreamDPOpFlowRandLimit8 *)p;
+ ua.lpv_rl8->run(flow_var);
+ p+=sizeof(StreamDPOpFlowRandLimit8);
+ break;
+ case StreamDPVmInstructions::ditRAND_LIMIT16:
+ ua.lpv_rl16 =(StreamDPOpFlowRandLimit16 *)p;
+ ua.lpv_rl16->run(flow_var);
+ p+=sizeof(StreamDPOpFlowRandLimit16);
+ break;
+ case StreamDPVmInstructions::ditRAND_LIMIT32:
+ ua.lpv_rl32 =(StreamDPOpFlowRandLimit32 *)p;
+ ua.lpv_rl32->run(flow_var);
+ p+=sizeof(StreamDPOpFlowRandLimit32);
+ break;
+ case StreamDPVmInstructions::ditRAND_LIMIT64:
+ ua.lpv_rl64 =(StreamDPOpFlowRandLimit64 *)p;
+ ua.lpv_rl64->run(flow_var);
+ p+=sizeof(StreamDPOpFlowRandLimit64);
+ break;
+
+ default:
+ assert(0);
+ }
+}
+
+
+