diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 01d72b0..854594f 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(common src/addr_tuple4.cpp src/session_table.cpp src/raw_packet.cpp src/ctrl_packet.cpp src/bfd.cpp src/utils.cpp src/g_vxlan.cpp) +target_link_libraries(common PUBLIC cjson) target_include_directories(common PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) diff --git a/common/include/ctrl_packet.h b/common/include/ctrl_packet.h index 03f60da..09654fc 100644 --- a/common/include/ctrl_packet.h +++ b/common/include/ctrl_packet.h @@ -21,6 +21,7 @@ struct ctrl_pkt_parser char tsync[4]; uint64_t session_id; enum session_state state; + char method[32]; int policy_ids[32]; int policy_id_num; }; @@ -29,7 +30,8 @@ void ctrl_packet_parser_init(struct ctrl_pkt_parser *handler); // return 0 : success // return -1 : error -int ctrl_packet_parser_parse(struct ctrl_pkt_parser *handler, const void *data, size_t length); +int ctrl_packet_parser_parse(struct ctrl_pkt_parser *handler, const char *data, size_t length); +void ctrl_packet_parser_dump(struct ctrl_pkt_parser *handler); #ifdef __cpluscplus } diff --git a/common/include/utils.h b/common/include/utils.h index b107110..1256ee9 100644 --- a/common/include/utils.h +++ b/common/include/utils.h @@ -6,11 +6,12 @@ extern "C" { #endif -#define MIN(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) > (b) ? (b) : (a)) #define LOG_TAG_POLICY "POLICY" #define LOG_TAG_UTILS "UTILS" #define LOG_TAG_RAWPKT "RAW_PACKET" +#define LOG_TAG_CTRLPKT "CTRL_PACKET" #define LOG_TAG_STABLE "SESSION_TABLE" #define LOG_TAG_PKTIO "PACKET_IO" #define LOG_TAG_METRICS "METRICS" diff --git a/common/src/ctrl_packet.cpp b/common/src/ctrl_packet.cpp index 5dc2874..7164cdf 100644 --- a/common/src/ctrl_packet.cpp +++ b/common/src/ctrl_packet.cpp @@ -1,7 +1,27 @@ #include +#include +#include "log.h" +#include "utils.h" #include "ctrl_packet.h" +static const char *session_state_to_string(enum session_state state) +{ + switch (state) + { + case SESSION_STATE_OPENING: + return "opening"; + case SESSION_STATE_CLONING: + return "closing"; + case SESSION_STATE_ACTIVE: + return "active"; + case SESSION_STATE_RESETALL: + return "resetall"; + default: + return "unknown"; + } +} + void ctrl_packet_parser_init(struct ctrl_pkt_parser *handler) { memset(handler, 0, sizeof(struct ctrl_pkt_parser)); @@ -9,7 +29,141 @@ void ctrl_packet_parser_init(struct ctrl_pkt_parser *handler) // return 0 : success // return -1 : error -int ctrl_packet_parser_parse(struct ctrl_pkt_parser *handler, const void *data, size_t length) +int ctrl_packet_parser_parse(struct ctrl_pkt_parser *handler, const char *data, size_t length) { + int iter = 0; + cJSON *item = NULL; + cJSON *elem = NULL; + cJSON *json = NULL; + + json = cJSON_Parse(data); + if (json == NULL) + { + LOG_ERROR("%s: unexpected control packet: (invalid json format) %s", LOG_TAG_CTRLPKT, data); + return -1; + } + + // tsync + item = cJSON_GetObjectItem(json, "tsync"); + if (!item || !cJSON_IsString(item)) + { + LOG_ERROR("%s: unexpected control packet: (invalid tsync format) %s", LOG_TAG_CTRLPKT, data); + goto error_out; + } + memcpy(handler->tsync, item->valuestring, MIN(sizeof(handler->tsync), strlen(item->valuestring))); + + // session_id + item = cJSON_GetObjectItem(json, "session_id"); + if (!item || !cJSON_IsNumber(item)) + { + LOG_ERROR("%s: unexpected control packet: (invalid session_id format) %s", LOG_TAG_CTRLPKT, data); + goto error_out; + } + handler->session_id = item->valueint; + + // state + item = cJSON_GetObjectItem(json, "state"); + if (!item || !cJSON_IsString(item)) + { + LOG_ERROR("%s: unexpected control packet: (invalid state format) %s", LOG_TAG_CTRLPKT, data); + goto error_out; + } + + if (strcasecmp(item->valuestring, "opening") == 0) + { + handler->state = SESSION_STATE_OPENING; + } + else if (strcasecmp(item->valuestring, "active") == 0) + { + handler->state = SESSION_STATE_ACTIVE; + } + else if (strcasecmp(item->valuestring, "closing") == 0) + { + handler->state = SESSION_STATE_CLONING; + } + else if (strcasecmp(item->valuestring, "resetall") == 0) + { + handler->state = SESSION_STATE_RESETALL; + } + else + { + LOG_ERROR("%s: unexpected control packet: (invalid state value) %s", LOG_TAG_CTRLPKT, data); + goto error_out; + } + + if (handler->state != SESSION_STATE_ACTIVE) + { + goto success_out; + } + + // method + item = cJSON_GetObjectItem(json, "method"); + if (!item || !cJSON_IsString(item)) + { + LOG_ERROR("%s: unexpected control packet: (invalid method format) %s", LOG_TAG_CTRLPKT, data); + goto error_out; + } + memcpy(handler->method, item->valuestring, MIN(sizeof(handler->method), strlen(item->valuestring))); + if (strcasecmp(item->valuestring, "policy_update") != 0) + { + LOG_ERROR("%s: unexpected control packet: (invalid method value) %s", LOG_TAG_CTRLPKT, data); + goto error_out; + } + + // params + item = cJSON_GetObjectItem(json, "params"); + if (!item || !cJSON_IsObject(item)) + { + LOG_ERROR("%s: unexpected control packet: (invalid params format) %s", LOG_TAG_CTRLPKT, data); + goto error_out; + } + + item = cJSON_GetObjectItem(item, "service_chaining"); + if (!item || !cJSON_IsArray(item) || !cJSON_GetArraySize(item)) + { + LOG_ERROR("%s: unexpected control packet: (invalid service_chaining format) %s", LOG_TAG_CTRLPKT, data); + goto error_out; + } + + handler->policy_id_num = MIN(cJSON_GetArraySize(item), (int)(sizeof(handler->policy_ids) / sizeof(handler->policy_ids[0]))); + cJSON_ArrayForEach(elem, item) + { + if (!cJSON_IsNumber(elem)) + { + LOG_ERROR("%s: unexpected control packet: (invalid service_chaining value) %s", LOG_TAG_POLICY, data); + continue; + } + handler->policy_ids[iter] = elem->valueint; + iter++; + + if (iter == handler->policy_id_num) + { + break; + } + } + +success_out: + cJSON_Delete(json); return 0; -} \ No newline at end of file + +error_out: + cJSON_Delete(json); + return -1; +} + +void ctrl_packet_parser_dump(struct ctrl_pkt_parser *handler) +{ + if (handler) + { + LOG_INFO("%s: tsync : %s", LOG_TAG_POLICY, handler->tsync); + LOG_INFO("%s: session_id : %lu", LOG_TAG_POLICY, handler->session_id); + LOG_INFO("%s: state : %s", LOG_TAG_POLICY, session_state_to_string(handler->state)); + LOG_INFO("%s: method : %s", LOG_TAG_POLICY, handler->method); + LOG_INFO("%s: policy_id_num : %d", LOG_TAG_POLICY, handler->policy_id_num); + + for (int i = 0; i < handler->policy_id_num; i++) + { + LOG_INFO("%s: policy_ids[%03d] : %i", LOG_TAG_POLICY, i, handler->policy_ids[i]); + } + } +} diff --git a/common/test/CMakeLists.txt b/common/test/CMakeLists.txt index 9b2c5f7..7970b60 100644 --- a/common/test/CMakeLists.txt +++ b/common/test/CMakeLists.txt @@ -22,6 +22,14 @@ add_executable(gtest_raw_packet gtest_raw_packet.cpp) target_include_directories(gtest_raw_packet PUBLIC ${CMAKE_SOURCE_DIR}/common/include) target_link_libraries(gtest_raw_packet common gtest) +############################################################################### +# gtest_ctrl_packet +############################################################################### + +add_executable(gtest_ctrl_packet gtest_ctrl_packet.cpp) +target_include_directories(gtest_ctrl_packet PUBLIC ${CMAKE_SOURCE_DIR}/common/include) +target_link_libraries(gtest_ctrl_packet common gtest) + ############################################################################### # gtest_utils ############################################################################### @@ -38,4 +46,5 @@ include(GoogleTest) gtest_discover_tests(gtest_addr_tuple4) gtest_discover_tests(gtest_session_table) gtest_discover_tests(gtest_raw_packet) +gtest_discover_tests(gtest_ctrl_packet) gtest_discover_tests(gtest_utils) \ No newline at end of file diff --git a/common/test/gtest_ctrl_packet.cpp b/common/test/gtest_ctrl_packet.cpp new file mode 100644 index 0000000..3f8365d --- /dev/null +++ b/common/test/gtest_ctrl_packet.cpp @@ -0,0 +1,29 @@ +#include + +#include "ctrl_packet.h" + +TEST(CTRL_PACKET, PARSE) +{ + const char *data = "{\"tsync\":\"1.0\",\"session_id\":123456789,\"state\":\"active\",\"method\":\"policy_update\",\"params\":{\"service_chaining\":[1,2,3],\"shaping\":[4,5,6]}}"; + size_t length = strlen(data); + + struct ctrl_pkt_parser parser; + ctrl_packet_parser_init(&parser); + EXPECT_TRUE(ctrl_packet_parser_parse(&parser, data, length) == 0); + ctrl_packet_parser_dump(&parser); + + EXPECT_STREQ(parser.tsync, "1.0"); + EXPECT_TRUE(parser.session_id == 123456789); + EXPECT_TRUE(parser.state == SESSION_STATE_ACTIVE); + EXPECT_STREQ(parser.method, "policy_update"); + EXPECT_TRUE(parser.policy_id_num == 3); + EXPECT_TRUE(parser.policy_ids[0] == 1); + EXPECT_TRUE(parser.policy_ids[1] == 2); + EXPECT_TRUE(parser.policy_ids[2] == 3); +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file