added VM support to the streams and RPC parser
authorimarom <[email protected]>
Mon, 7 Sep 2015 13:27:12 +0000 (16:27 +0300)
committerimarom <[email protected]>
Mon, 7 Sep 2015 13:27:12 +0000 (16:27 +0300)
linux/ws_main.py
src/gtest/rpc_test.cpp
src/rpc-server/commands/trex_rpc_cmd_stream.cpp
src/rpc-server/commands/trex_rpc_cmds.h
src/rpc-server/trex_rpc_cmd.cpp
src/rpc-server/trex_rpc_cmd_api.h
src/stateless/trex_stream_api.h
src/stateless/trex_stream_vm.cpp [new file with mode: 0644]
src/stateless/trex_stream_vm.h [new file with mode: 0644]

index 5bf9a74..e9f21d1 100755 (executable)
@@ -141,7 +141,8 @@ net_src = SrcGroup(dir='src/common/Network/Packet',
 # stateless code
 stateless_src = SrcGroup(dir='src/stateless/',
                           src_list=['trex_stream.cpp',
-                                    'trex_stateless.cpp'
+                                    'trex_stream_vm.cpp',
+                                    'trex_stateless.cpp',
                                     ])
 # RPC code
 rpc_server_src = SrcGroup(dir='src/rpc-server/',
index 5d3c473..8a7e917 100644 (file)
@@ -250,6 +250,7 @@ TEST_F(RpcTest, add_stream) {
               "\"isg\":4.3, \"enabled\":true, \"self_start\":true,"
               "\"next_stream_id\":-1,"
               "\"packet\":{\"binary\":[4,1,255], \"meta\":\"dummy\"},"
+              "\"vm\":[],"
               "\"rx_stats\":{\"enabled\":false}}}}";
 
     resp_str = send_msg(add_str);
index 1688941..90b55ea 100644 (file)
@@ -27,6 +27,23 @@ 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
  * 
@@ -74,6 +91,10 @@ TrexRpcCmdAddStream::_run(const Json::Value &params, Json::Value &result) {
     /* meta data */
     stream->m_pkt.meta = parse_string(pkt, "meta", result);
 
+    /* parse VM */
+    const Json::Value &vm =  parse_array(section ,"vm", result);
+    parse_vm(vm, stream, result);
+
     /* parse RX info */
     const Json::Value &rx = parse_object(section, "rx_stats", result);
 
@@ -142,6 +163,109 @@ TrexRpcCmdAddStream::allocate_new_stream(const Json::Value &section, uint8_t por
 
 }
 
+void 
+TrexRpcCmdAddStream::parse_vm_instr_checksum(const Json::Value &inst, TrexStream *stream, Json::Value &result) {
+
+    uint16_t pkt_offset = parse_uint16(inst, "pkt_offset", result); 
+    stream->m_vm.add_instruction(new StreamVmInstructionFixChecksumIpv4(pkt_offset));
+}
+
+void 
+TrexRpcCmdAddStream::parse_vm_instr_flow_var(const Json::Value &inst, TrexStream *stream, Json::Value &result) {
+    std::string  flow_var_name = parse_string(inst, "name", result);
+
+    auto sizes = {1, 2, 4, 8};
+    uint8_t      flow_var_size = parse_choice(inst, "size", sizes, result);
+
+    auto ops = {"inc", "dec", "random"};
+    std::string  op_type_str = parse_choice(inst, "op", ops, result);
+
+    StreamVmInstructionFlowMan::flow_var_op_e op_type;
+
+    if (op_type_str == "inc") {
+        op_type = StreamVmInstructionFlowMan::FLOW_VAR_OP_INC;
+    } else if (op_type_str == "dec") {
+        op_type = StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC;
+    } else if (op_type_str == "random") {
+        op_type = StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM;
+    } else {
+        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;
+    uint64_t min_value;
+    uint64_t max_value;
+
+    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");
+    }
+
+    stream->m_vm.add_instruction(new StreamVmInstructionFlowMan(flow_var_name,
+                                                                flow_var_size,
+                                                                op_type,
+                                                                init_value,
+                                                                min_value,
+                                                                max_value
+                                                                ));
+}
+
+void 
+TrexRpcCmdAddStream::parse_vm_instr_write_flow_var(const Json::Value &inst, TrexStream *stream, Json::Value &result) {
+    std::string  flow_var_name = parse_string(inst, "flow_var_name", result);
+    uint16_t     pkt_offset    = parse_uint16(inst, "pkt_offset", result);
+    int          add_value     = parse_int(inst,    "add_value", result);
+    bool         is_big_endian = parse_bool(inst,   "is_big_endian", result);
+
+    stream->m_vm.add_instruction(new StreamVmInstructionWriteToPkt(flow_var_name,
+                                                                   pkt_offset,
+                                                                   add_value,
+                                                                   is_big_endian));
+}
+
+void 
+TrexRpcCmdAddStream::parse_vm(const Json::Value &vm, TrexStream *stream, Json::Value &result) {
+    /* array of VM instructions on vm */
+    for (int i = 0; i < vm.size(); i++) {
+        const Json::Value & inst = vm[i];
+
+        auto vm_types = {"fix_checksum_ipv4", "flow_var", "write_flow_var"};
+        std::string vm_type = parse_choice(inst, "type", vm_types, result);
+
+        // checksum instruction
+        if (vm_type == "fix_checksum_ipv4") {
+            parse_vm_instr_checksum(inst, stream, result);
+
+        } else if (vm_type == "flow_var") {
+            parse_vm_instr_flow_var(inst, stream, result);
+
+        } else if (vm_type == "write_flow_var") {
+            parse_vm_instr_write_flow_var(inst, stream, result);
+
+        } else {
+            /* internal error */
+            throw TrexRpcException("internal error");
+        }
+    }
+}
+
 void
 TrexRpcCmdAddStream::validate_stream(const TrexStream *stream, Json::Value &result) {
 
index 428d48c..f88631b 100644 (file)
@@ -70,7 +70,10 @@ TREX_RPC_CMD_DEFINE_EXTENED(TrexRpcCmdAddStream, "add_stream", 3,
 /* extended part */
 TrexStream * allocate_new_stream(const Json::Value &section, uint8_t port_id, uint32_t stream_id, Json::Value &result);
 void validate_stream(const TrexStream *stream, Json::Value &result);
-
+void parse_vm(const Json::Value &vm, TrexStream *stream, Json::Value &result);
+void parse_vm_instr_checksum(const Json::Value &inst, TrexStream *stream, Json::Value &result);
+void parse_vm_instr_flow_var(const Json::Value &inst, TrexStream *stream, Json::Value &result);
+void parse_vm_instr_write_flow_var(const Json::Value &inst, TrexStream *stream, Json::Value &result);
 );
 
 
index 6988cba..3fc77f7 100644 (file)
@@ -50,6 +50,8 @@ TrexRpcCommand::type_to_str(field_type_e type) {
     switch (type) {
     case FIELD_TYPE_BYTE:
         return "byte";
+    case FIELD_TYPE_UINT16:
+        return "uint16";
     case FIELD_TYPE_BOOL:
         return "bool";
     case FIELD_TYPE_INT:
@@ -107,6 +109,18 @@ TrexRpcCommand::parse_byte(const Json::Value &parent, int index, Json::Value &re
     return parent[index].asUInt();
 }
 
+uint16_t 
+TrexRpcCommand::parse_uint16(const Json::Value &parent, const std::string &name, Json::Value &result) {
+    check_field_type(parent, name, FIELD_TYPE_UINT16, result);
+    return parent[name].asUInt();
+}
+
+uint16_t  
+TrexRpcCommand::parse_uint16(const Json::Value &parent, int index, Json::Value &result) {
+    check_field_type(parent, index, FIELD_TYPE_UINT16, result);
+    return parent[index].asUInt();
+}
+
 int
 TrexRpcCommand::parse_int(const Json::Value &parent, const std::string &name, Json::Value &result) {
     check_field_type(parent, name, FIELD_TYPE_INT, result);
@@ -190,6 +204,12 @@ TrexRpcCommand::check_field_type_common(const Json::Value &field, const std::str
         }
         break;
 
+    case FIELD_TYPE_UINT16:
+        if ( (!field.isUInt()) || (field.asInt() > 0xFFFF)) {
+            rc = false;
+        }
+        break;
+
     case FIELD_TYPE_BOOL:
         if (!field.isBool()) {
             rc = false;
index da89580..def52fc 100644 (file)
@@ -91,6 +91,7 @@ protected:
      */
     enum field_type_e {
         FIELD_TYPE_BYTE,
+        FIELD_TYPE_UINT16,
         FIELD_TYPE_INT,
         FIELD_TYPE_DOUBLE,
         FIELD_TYPE_BOOL,
@@ -115,6 +116,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);
     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);
@@ -123,6 +125,7 @@ protected:
     const Json::Value & parse_array(const Json::Value &parent, const std::string &name, Json::Value &result);
 
     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);
     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);
@@ -130,6 +133,39 @@ protected:
     const Json::Value & parse_object(const Json::Value &parent, int index, Json::Value &result);
     const Json::Value & parse_array(const Json::Value &parent, int index, Json::Value &result);
 
+    /**
+     * parse a field from choices 
+     * 
+     */
+    template<typename T> T parse_choice(const Json::Value &params, const std::string &name, std::initializer_list<T> choices, Json::Value &result) {
+        const Json::Value &field = params[name];
+
+        if (field == Json::Value::null) {
+            std::stringstream ss;
+            ss << "field '" << name << "' is missing";
+            generate_parse_err(result, ss.str());
+        }
+
+        for (auto x : choices) {
+            if (field == x) {
+                return (x);
+            }
+        }
+
+        std::stringstream ss;
+
+        ss << "field '" << name << "' can only be one of [";
+        for (auto x : choices) {
+            ss << "'" << x << "' ,";
+        }
+
+        std::string s = ss.str();
+        s.pop_back();
+        s.pop_back();
+        s += "]";
+        generate_parse_err(result, s);
+    }
+
     /**
      * check field type
      * 
index c924899..2699975 100644 (file)
@@ -26,6 +26,8 @@ limitations under the License.
 #include <stdint.h>
 #include <string>
 
+#include <trex_stream_vm.h>
+
 class TrexRpcCmdAddStream;
 
 /**
@@ -68,6 +70,7 @@ private:
     } m_pkt;
 
     /* VM */
+    StreamVm m_vm;
 
     /* RX check */
     struct {
@@ -78,6 +81,7 @@ private:
 
     } m_rx_check;
 
+
 };
 
 /**
diff --git a/src/stateless/trex_stream_vm.cpp b/src/stateless/trex_stream_vm.cpp
new file mode 100644 (file)
index 0000000..2e760ae
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ Itay Marom
+ 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>
+
+/***************************
+ * StreamVmInstruction
+ * 
+ **************************/
+StreamVmInstruction::~StreamVmInstruction() {
+
+}
+
+/***************************
+ * StreamVm
+ * 
+ **************************/
+void StreamVm::add_instruction(StreamVmInstruction *inst) {
+    m_inst_list.push_back(inst);
+}
+
+const std::vector<StreamVmInstruction *> & 
+StreamVm::get_instruction_list() {
+    return m_inst_list;
+}
+
+bool StreamVm::compile() {
+    /* implement me */
+    return (false);
+}
+
+StreamVm::~StreamVm() {
+    for (auto inst : m_inst_list) {
+        delete inst;
+    }
+}
+
diff --git a/src/stateless/trex_stream_vm.h b/src/stateless/trex_stream_vm.h
new file mode 100644 (file)
index 0000000..56edbca
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ Itay Marom
+ 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.
+*/
+#ifndef __TREX_STREAM_VM_API_H__
+#define __TREX_STREAM_VM_API_H__
+
+#include <string>
+#include <stdint.h>
+#include <vector>
+
+/**
+ * interface for stream VM instruction
+ * 
+ */
+class StreamVmInstruction {
+public:
+
+    virtual ~StreamVmInstruction();
+
+private:
+    static const std::string m_name;
+};
+
+/**
+ * fix checksum for ipv4
+ * 
+ */
+class StreamVmInstructionFixChecksumIpv4 : public StreamVmInstruction {
+public:
+    StreamVmInstructionFixChecksumIpv4(uint16_t offset) : m_pkt_offset(offset) {
+
+    }
+
+private:
+    uint16_t m_pkt_offset;
+};
+
+/**
+ * flow manipulation instruction
+ * 
+ * @author imarom (07-Sep-15)
+ */
+class StreamVmInstructionFlowMan : public StreamVmInstruction {
+
+public:
+
+    /**
+     * different types of operations on the object
+     */
+    enum flow_var_op_e {
+        FLOW_VAR_OP_INC,
+        FLOW_VAR_OP_DEC,
+        FLOW_VAR_OP_RANDOM
+    };
+
+    StreamVmInstructionFlowMan(const std::string &var_name,
+                               uint8_t size,
+                               flow_var_op_e op,
+                               uint64_t init_value,
+                               uint64_t min_value,
+                               uint64_t max_value) : 
+                                                     m_var_name(var_name),
+                                                     m_size_bytes(size),
+                                                     m_op(op),
+                                                     m_init_value(init_value),
+                                                     m_min_value(min_value),
+                                                     m_max_value(max_value) {
+
+    }
+
+private:
+
+
+    /* flow var name */
+    std::string   m_var_name;
+
+    /* flow var size */
+    uint8_t       m_size_bytes;
+
+    /* type of op */
+    flow_var_op_e m_op;
+
+    /* range */
+    uint64_t m_init_value;
+    uint64_t m_min_value;
+    uint64_t m_max_value;
+
+
+};
+
+/**
+ * write flow var to packet
+ * 
+ */
+class StreamVmInstructionWriteToPkt : public StreamVmInstruction {
+public:
+
+    StreamVmInstructionWriteToPkt(const std::string &flow_var_name,
+                                  uint16_t           pkt_offset,
+                                  int32_t            add_value = 0,
+                                  bool               is_big_endian = true) :
+
+                                                        m_flow_var_name(flow_var_name),
+                                                        m_pkt_offset(pkt_offset),
+                                                        m_add_value(add_value),
+                                                        m_is_big_endian(is_big_endian) {}
+private:
+
+    /* flow var name to write */
+    std::string   m_flow_var_name;
+
+    /* where to write */
+    uint16_t      m_pkt_offset;
+
+    /* add/dec value from field when writing */
+    int32_t       m_add_value;
+
+    /* writing endian */
+    bool         m_is_big_endian;
+};
+
+/**
+ * describes a VM program
+ * 
+ */
+class StreamVm {
+public:
+
+    /**
+     * add new instruction to the VM
+     * 
+     */
+    void add_instruction(StreamVmInstruction *inst);
+
+    /**
+     * get const access to the instruction list
+     * 
+     */
+    const std::vector<StreamVmInstruction *> & get_instruction_list();
+
+    /**
+     * compile the VM 
+     * return true of success, o.w false 
+     * 
+     */
+    bool compile();
+
+    ~StreamVm();
+
+private:
+    std::vector<StreamVmInstruction *> m_inst_list;
+};
+
+#endif /* __TREX_STREAM_VM_API_H__ */