summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorimarom <imarom@cisco.com>2015-09-07 16:27:12 +0300
committerimarom <imarom@cisco.com>2015-09-07 16:27:12 +0300
commite33befcf222fd2108d589dede11069d4256bb21a (patch)
tree839e07e84cd658df6e0465558e80e27cec3288dc
parent3adfe9c3c8a6e2ce1cdc5bd1a673e428c18fa64b (diff)
added VM support to the streams and RPC parser
-rwxr-xr-xlinux/ws_main.py3
-rw-r--r--src/gtest/rpc_test.cpp1
-rw-r--r--src/rpc-server/commands/trex_rpc_cmd_stream.cpp124
-rw-r--r--src/rpc-server/commands/trex_rpc_cmds.h5
-rw-r--r--src/rpc-server/trex_rpc_cmd.cpp20
-rw-r--r--src/rpc-server/trex_rpc_cmd_api.h36
-rw-r--r--src/stateless/trex_stream_api.h4
-rw-r--r--src/stateless/trex_stream_vm.cpp54
-rw-r--r--src/stateless/trex_stream_vm.h171
9 files changed, 416 insertions, 2 deletions
diff --git a/linux/ws_main.py b/linux/ws_main.py
index 5bf9a743..e9f21d11 100755
--- a/linux/ws_main.py
+++ b/linux/ws_main.py
@@ -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/',
diff --git a/src/gtest/rpc_test.cpp b/src/gtest/rpc_test.cpp
index 5d3c4738..8a7e9176 100644
--- a/src/gtest/rpc_test.cpp
+++ b/src/gtest/rpc_test.cpp
@@ -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);
diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp
index 16889413..90b55ea8 100644
--- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp
+++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp
@@ -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) {
diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h
index 428d48c1..f88631bc 100644
--- a/src/rpc-server/commands/trex_rpc_cmds.h
+++ b/src/rpc-server/commands/trex_rpc_cmds.h
@@ -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);
);
diff --git a/src/rpc-server/trex_rpc_cmd.cpp b/src/rpc-server/trex_rpc_cmd.cpp
index 6988cba7..3fc77f71 100644
--- a/src/rpc-server/trex_rpc_cmd.cpp
+++ b/src/rpc-server/trex_rpc_cmd.cpp
@@ -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;
diff --git a/src/rpc-server/trex_rpc_cmd_api.h b/src/rpc-server/trex_rpc_cmd_api.h
index da895809..def52fca 100644
--- a/src/rpc-server/trex_rpc_cmd_api.h
+++ b/src/rpc-server/trex_rpc_cmd_api.h
@@ -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);
@@ -131,6 +134,39 @@ protected:
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
*
*/
diff --git a/src/stateless/trex_stream_api.h b/src/stateless/trex_stream_api.h
index c9248999..26999751 100644
--- a/src/stateless/trex_stream_api.h
+++ b/src/stateless/trex_stream_api.h
@@ -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
index 00000000..2e760ae9
--- /dev/null
+++ b/src/stateless/trex_stream_vm.cpp
@@ -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
index 00000000..56edbcaf
--- /dev/null
+++ b/src/stateless/trex_stream_vm.h
@@ -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__ */