diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f17360..80cc624 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ add_custom_target("install-program" COMMAND ${CMAKE_COMMAND} ARGS -DCOMPONENT=Pr add_custom_target("install-profile" COMMAND ${CMAKE_COMMAND} ARGS -DCOMPONENT=Profile -P cmake_install.cmake) enable_testing() +add_subdirectory(conf) add_subdirectory(vendor) add_subdirectory(common) add_subdirectory(platform) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index feee134..09c6baf 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(common src/addr_tuple4.cpp src/session_table.cpp src/raw_packet.cpp src/bfd.cpp) +add_library(common src/addr_tuple4.cpp src/session_table.cpp src/raw_packet.cpp src/bfd.cpp src/utils.cpp) target_include_directories(common PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) diff --git a/common/include/raw_packet.h b/common/include/raw_packet.h index 01bb4ba..886a30a 100644 --- a/common/include/raw_packet.h +++ b/common/include/raw_packet.h @@ -6,6 +6,8 @@ extern "C" { #endif +#include + enum layer_type { // 数据链路层 @@ -43,6 +45,15 @@ enum layer_type LAYER_TYPE_UNKNOWN, }; +enum ldbc_method +{ + LDBC_METHOD_HASH_INT_IP = 1, + LDBC_METHOD_HASH_EXT_IP = 2, + LDBC_METHOD_HASH_INT_IP_AND_EXT_IP = 3, + LDBC_METHOD_HASH_INNERMOST_INT_IP = 4, + LDBC_METHOD_HASH_INNERMOST_EXT_IP = 5, +}; + enum parse_status { PARSE_STATUS_CONTINUE, @@ -73,6 +84,8 @@ int raw_packet_parser_get_most_outer_tuple4(struct raw_pkt_parser *handler, stru int raw_packet_parser_get_most_inner_address(struct raw_pkt_parser *handler, struct addr_tuple4 *addr); int raw_packet_parser_get_most_outer_address(struct raw_pkt_parser *handler, struct addr_tuple4 *addr); +uint64_t raw_packet_parser_get_hash_value(struct raw_pkt_parser *handler, enum ldbc_method method, int dir_is_internal); + #ifdef __cpluscplus } #endif diff --git a/common/include/utils.h b/common/include/utils.h new file mode 100644 index 0000000..904107f --- /dev/null +++ b/common/include/utils.h @@ -0,0 +1,31 @@ +#ifndef _UTILS_H +#define _UTILS_H + +#ifdef __cpluscplus +extern "C" +{ +#endif + +#define MIN(a, b) ((a) > (b) ? (a) : (b)) + +#define LOG_TAG_POLICY "POLICY" +#define LOG_TAG_UTILS "UTILS" + +struct fixed_num_array +{ + int elems[128]; + int num; + int size; +}; + +void fixed_num_array_init(struct fixed_num_array *array); +void fixed_num_array_add_elem(struct fixed_num_array *array, int elem); +void fixed_num_array_del_elem(struct fixed_num_array *array, int elem); +int fixed_num_array_count_elem(struct fixed_num_array *array); +int fixed_num_array_index_elem(struct fixed_num_array *array, int index); + +#ifdef __cpluscplus +} +#endif + +#endif diff --git a/common/src/raw_packet.cpp b/common/src/raw_packet.cpp index 63c4c6b..e71d21a 100644 --- a/common/src/raw_packet.cpp +++ b/common/src/raw_packet.cpp @@ -12,6 +12,7 @@ #include #include "log.h" +#include "uthash.h" #include "addr_tuple4.h" #include "raw_packet.h" @@ -89,6 +90,8 @@ struct raw_pkt_parser * Static API ******************************************************************************/ +static const char *ldbc_method_to_string(enum ldbc_method ldbc_method); + // parser utils static void set_addr_tuple4(const void *data, enum layer_type layer_type, struct addr_tuple4 *addr); static const char *layer_type2str(enum layer_type this_type); @@ -353,10 +356,157 @@ int raw_packet_parser_get_most_outer_address(struct raw_pkt_parser *handler, str return -1; } +uint64_t raw_packet_parser_get_hash_value(struct raw_pkt_parser *handler, enum ldbc_method method, int dir_is_internal) +{ + uint64_t temp = 0; + uint64_t hash_value = 1; + + int inner_addr_len = 0; + int outer_addr_len = 0; + const char *inner_src_addr = NULL; + const char *inner_dst_addr = NULL; + const char *outer_src_addr = NULL; + const char *outer_dst_addr = NULL; + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + + if (handler == NULL) + { + return hash_value; + } + + if (raw_packet_parser_get_most_inner_address(handler, &inner_addr) == -1) + { + return hash_value; + } + + if (raw_packet_parser_get_most_outer_address(handler, &outer_addr) == -1) + { + return hash_value; + } + + if (inner_addr.addr_type == ADDR_TUPLE4_TYPE_V4) + { + inner_src_addr = (const char *)&(inner_addr.addr_v4.src_addr); + inner_dst_addr = (const char *)&(inner_addr.addr_v4.dst_addr); + inner_addr_len = sizeof(inner_addr.addr_v4.dst_addr); + } + else + { + inner_src_addr = (const char *)&(inner_addr.addr_v6.src_addr); + inner_dst_addr = (const char *)&(inner_addr.addr_v6.dst_addr); + inner_addr_len = sizeof(inner_addr.addr_v6.dst_addr); + } + + if (outer_addr.addr_type == ADDR_TUPLE4_TYPE_V4) + { + outer_src_addr = (const char *)&(outer_addr.addr_v4.src_addr); + outer_dst_addr = (const char *)&(outer_addr.addr_v4.dst_addr); + outer_addr_len = sizeof(outer_addr.addr_v4.dst_addr); + } + else + { + outer_src_addr = (const char *)&(outer_addr.addr_v6.src_addr); + outer_dst_addr = (const char *)&(outer_addr.addr_v6.dst_addr); + outer_addr_len = sizeof(outer_addr.addr_v6.dst_addr); + } + + switch (method) + { + case LDBC_METHOD_HASH_INT_IP: + if (dir_is_internal) + { + // outer src ip + HASH_VALUE(outer_src_addr, outer_addr_len, hash_value); + } + else + { + // outer dst ip + HASH_VALUE(outer_dst_addr, outer_addr_len, hash_value); + } + break; + case LDBC_METHOD_HASH_EXT_IP: + if (dir_is_internal) + { + // outer dst ip + HASH_VALUE(outer_dst_addr, outer_addr_len, hash_value); + } + else + { + // outer src ip + HASH_VALUE(outer_src_addr, outer_addr_len, hash_value); + } + break; + case LDBC_METHOD_HASH_INT_IP_AND_EXT_IP: + // outer dst ip ^ outer src ip + HASH_VALUE(outer_src_addr, outer_addr_len, hash_value); + HASH_VALUE(outer_dst_addr, outer_addr_len, temp); + hash_value = hash_value ^ temp; + break; + case LDBC_METHOD_HASH_INNERMOST_INT_IP: + if (dir_is_internal) + { + // innner src ip + HASH_VALUE(inner_src_addr, inner_addr_len, hash_value); + } + else + { + // innner dst ip + HASH_VALUE(inner_dst_addr, inner_addr_len, hash_value); + } + break; + case LDBC_METHOD_HASH_INNERMOST_EXT_IP: + if (dir_is_internal) + { + // innner dst ip + HASH_VALUE(inner_dst_addr, inner_addr_len, hash_value); + } + else + { + // innner src ip + HASH_VALUE(inner_src_addr, inner_addr_len, hash_value); + } + break; + default: + return hash_value; + } + + char *inner_addr_str = addr_tuple4_to_str(&inner_addr); + char *outer_addr_str = addr_tuple4_to_str(&outer_addr); + LOG_ERROR("%s: pkt_trace_id: %lu, outer_addr: %s, inner_addr: %s, is_internal: %d, hash_method: %s, hash_value: %lu", + LOG_TAG, handler->pkt_trace_id, outer_addr_str, inner_addr_str, dir_is_internal, ldbc_method_to_string(method), hash_value); + free(inner_addr_str); + free(outer_addr_str); + + return hash_value; +} + /****************************************************************************** * Private API ******************************************************************************/ +static const char *ldbc_method_to_string(enum ldbc_method ldbc_method) +{ + switch (ldbc_method) + { + case LDBC_METHOD_HASH_INT_IP: + return "outter_internal_ip"; + case LDBC_METHOD_HASH_EXT_IP: + return "outter_external_ip"; + case LDBC_METHOD_HASH_INT_IP_AND_EXT_IP: + return "outter_internal_ip_and_external_ip"; + case LDBC_METHOD_HASH_INNERMOST_INT_IP: + return "inner_internal_ip"; + case LDBC_METHOD_HASH_INNERMOST_EXT_IP: + return "inner_external_ip"; + default: + return "unknown"; + } +} + static void set_addr_tuple4(const void *data, enum layer_type layer_type, struct addr_tuple4 *addr) { const struct tcphdr *tcp_hdr = NULL; diff --git a/common/src/utils.cpp b/common/src/utils.cpp new file mode 100644 index 0000000..10f2268 --- /dev/null +++ b/common/src/utils.cpp @@ -0,0 +1,63 @@ +#include +#include + +#include "utils.h" +#include "log.h" + +void fixed_num_array_init(struct fixed_num_array *array) +{ + memset(array, 0, sizeof(fixed_num_array)); + array->num = 0; + array->size = sizeof(array->elems) / sizeof(array->elems[0]); +} + +void fixed_num_array_add_elem(struct fixed_num_array *array, int elem) +{ + if (array->num < array->size) + { + array->elems[array->num] = elem; + array->num++; + } + else + { + LOG_ERROR("%s: fixed num array add elem too much !!!", LOG_TAG_UTILS); + } +} + +void fixed_num_array_del_elem(struct fixed_num_array *array, int elem) +{ + for (int i = 0; i < array->num; i++) + { + if (array->elems[i] == elem) + { + if (i + 1 != array->size) + { + memmove(&(array->elems[i]), &(array->elems[i + 1]), sizeof(array->elems[0]) * (array->num - i - 1)); + } + i--; + array->num--; + } + } +} + +int fixed_num_array_count_elem(struct fixed_num_array *array) +{ + if (array) + { + return array->num; + } + else + { + return 0; + } +} + +int fixed_num_array_index_elem(struct fixed_num_array *array, int index) +{ + if (index >= array->num) + { + assert(0); + } + + return array->elems[index]; +} \ No newline at end of file diff --git a/common/test/CMakeLists.txt b/common/test/CMakeLists.txt index b76ed3e..9b2c5f7 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_utils +############################################################################### + +add_executable(gtest_utils gtest_utils.cpp) +target_include_directories(gtest_utils PUBLIC ${CMAKE_SOURCE_DIR}/common/include) +target_link_libraries(gtest_utils common gtest) + ############################################################################### # gtest_discover_tests ############################################################################### @@ -29,4 +37,5 @@ target_link_libraries(gtest_raw_packet common gtest) include(GoogleTest) gtest_discover_tests(gtest_addr_tuple4) gtest_discover_tests(gtest_session_table) -gtest_discover_tests(gtest_raw_packet) \ No newline at end of file +gtest_discover_tests(gtest_raw_packet) +gtest_discover_tests(gtest_utils) \ No newline at end of file diff --git a/common/test/gtest_raw_packet.cpp b/common/test/gtest_raw_packet.cpp index 17ec4a4..6badb1f 100644 --- a/common/test/gtest_raw_packet.cpp +++ b/common/test/gtest_raw_packet.cpp @@ -1371,6 +1371,27 @@ TEST(RAW_PACKET, ETH_MPLS_MPLS_PWETHCW_ETH_ARP) raw_packet_parser_destory(handler); } +TEST(RAW_PACKET, GET_HASH_VALUE) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data4, sizeof(data4)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data4 == 106); + + // inner_addr_str: "2001:da8:200:900e:200:5efe:d24d:58a3 0 2600:140e:6::1702:1058 0" + // outer_addr_str: "210.77.88.163 0 59.66.4.50 0" + + EXPECT_TRUE(raw_packet_parser_get_hash_value(handler, LDBC_METHOD_HASH_INT_IP, 1) == raw_packet_parser_get_hash_value(handler, LDBC_METHOD_HASH_EXT_IP, 0)); + EXPECT_TRUE(raw_packet_parser_get_hash_value(handler, LDBC_METHOD_HASH_EXT_IP, 1) == raw_packet_parser_get_hash_value(handler, LDBC_METHOD_HASH_INT_IP, 0)); + + EXPECT_TRUE(raw_packet_parser_get_hash_value(handler, LDBC_METHOD_HASH_INT_IP_AND_EXT_IP, 1) == raw_packet_parser_get_hash_value(handler, LDBC_METHOD_HASH_INT_IP_AND_EXT_IP, 0)); + EXPECT_TRUE(raw_packet_parser_get_hash_value(handler, LDBC_METHOD_HASH_INNERMOST_INT_IP, 1) == raw_packet_parser_get_hash_value(handler, LDBC_METHOD_HASH_INNERMOST_EXT_IP, 0)); + + raw_packet_parser_destory(handler); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/common/test/gtest_utils.cpp b/common/test/gtest_utils.cpp new file mode 100644 index 0000000..6d75a71 --- /dev/null +++ b/common/test/gtest_utils.cpp @@ -0,0 +1,43 @@ +#include + +#include "utils.h" + +TEST(UTILS, FIXED_NUM_ARRAY) +{ + struct fixed_num_array array; + fixed_num_array_init(&array); + + fixed_num_array_add_elem(&array, 1); + fixed_num_array_add_elem(&array, 2); + fixed_num_array_add_elem(&array, 3); + fixed_num_array_add_elem(&array, 1); + fixed_num_array_add_elem(&array, 2); + + EXPECT_TRUE(fixed_num_array_count_elem(&array) == 5); + EXPECT_TRUE(fixed_num_array_index_elem(&array, 0) == 1); + EXPECT_TRUE(fixed_num_array_index_elem(&array, 1) == 2); + EXPECT_TRUE(fixed_num_array_index_elem(&array, 2) == 3); + EXPECT_TRUE(fixed_num_array_index_elem(&array, 3) == 1); + EXPECT_TRUE(fixed_num_array_index_elem(&array, 4) == 2); + + fixed_num_array_del_elem(&array, 3); // 1,2,1,2 + EXPECT_TRUE(fixed_num_array_count_elem(&array) == 4); + EXPECT_TRUE(fixed_num_array_index_elem(&array, 0) == 1); + EXPECT_TRUE(fixed_num_array_index_elem(&array, 1) == 2); + EXPECT_TRUE(fixed_num_array_index_elem(&array, 2) == 1); + EXPECT_TRUE(fixed_num_array_index_elem(&array, 3) == 2); + + fixed_num_array_del_elem(&array, 2); // 1,1 + EXPECT_TRUE(fixed_num_array_count_elem(&array) == 2); + EXPECT_TRUE(fixed_num_array_index_elem(&array, 0) == 1); + EXPECT_TRUE(fixed_num_array_index_elem(&array, 1) == 1); + + fixed_num_array_del_elem(&array, 1); + EXPECT_TRUE(fixed_num_array_count_elem(&array) == 0); +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/conf/CMakeLists.txt b/conf/CMakeLists.txt new file mode 100644 index 0000000..5beca73 --- /dev/null +++ b/conf/CMakeLists.txt @@ -0,0 +1 @@ +install(FILES sce.conf DESTINATION conf COMPONENT Profile) \ No newline at end of file diff --git a/conf/sce.conf b/conf/sce.conf new file mode 100644 index 0000000..3044ce9 --- /dev/null +++ b/conf/sce.conf @@ -0,0 +1,21 @@ +[system] +nr_worker_threads=8 + +[maat] +# 0:json 1:redis 2:iris +input_mode=1 +stat_switch=1 +perf_switch=1 +scan_detail=0 +deferred_load=0 +effect_interval_ms=1000 +stat_file=log/sce.fs2 +table_info=resource/table_info.conf +accept_path=/opt/tsg/etc/tsg_device_tag.json +inc_cfg_dir=resource/inc/ +ful_cfg_dir=resource/ful/ +json_cfg_file=resource/sce.json +foreign_cont_dir=resource/foreign_files +redis_db_idx=0 +redis_server=127.0.0.1 +redis_port_range=6379 \ No newline at end of file diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt index affcd5e..ba67e1d 100644 --- a/platform/CMakeLists.txt +++ b/platform/CMakeLists.txt @@ -1,11 +1,14 @@ -add_executable(sce src/main.cpp src/policy.cpp) +add_library(platform src/policy.cpp src/health_check.cpp) +target_link_libraries(platform PUBLIC common) +target_link_libraries(platform PUBLIC pthread) +target_link_libraries(platform PUBLIC MESA_handle_logger) +target_link_libraries(platform PUBLIC MESA_prof_load) +target_link_libraries(platform PUBLIC maatframe) +target_link_libraries(platform PUBLIC cjson) +target_include_directories(platform PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/) -target_include_directories(sce PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/) -target_link_libraries(sce PUBLIC common) -target_link_libraries(sce PUBLIC pthread) -target_link_libraries(sce PUBLIC MESA_handle_logger) -target_link_libraries(sce PUBLIC MESA_prof_load) -target_link_libraries(sce PUBLIC maatframe) -target_link_libraries(sce PUBLIC cjson) +add_executable(sce src/main.cpp) +target_link_libraries(sce PUBLIC platform) +install(TARGETS sce RUNTIME DESTINATION bin COMPONENT Program) -install(TARGETS sce RUNTIME DESTINATION bin COMPONENT Program) \ No newline at end of file +add_subdirectory(test) \ No newline at end of file diff --git a/platform/include/health_check.h b/platform/include/health_check.h new file mode 100644 index 0000000..777908b --- /dev/null +++ b/platform/include/health_check.h @@ -0,0 +1,37 @@ +#ifndef _HEALTH_CHECK_H +#define _HEALTH_CHECK_H + +#ifdef __cpluscplus +extern "C" +{ +#endif + +#include "policy.h" + +void health_check_session_init(); + +// return 0 : success +// return -1 : key exist +// struct health_check *policy : need deep copy +int health_check_session_add(int session_id, const struct health_check *policy); + +// return 0 : success +// return -1 : key not exist +int health_check_session_del(int session_id); + +// return 1 : active +// return 0 : inactive +// return -1 : key not exist +int health_check_session_get_status(int session_id); + +// return 0 : success +// return -1 : key not exist +int health_check_session_set_status(int session_id, int is_active); + +void health_check_session_foreach(); + +#ifdef __cpluscplus +} +#endif + +#endif diff --git a/platform/include/policy.h b/platform/include/policy.h new file mode 100644 index 0000000..874dae5 --- /dev/null +++ b/platform/include/policy.h @@ -0,0 +1,125 @@ +#ifndef _POLICY_H +#define _POLICY_H + +#ifdef __cpluscplus +extern "C" +{ +#endif + +#include "raw_packet.h" + +enum traffic_type +{ + TRAFFIC_TYPE_NONE = 0, + TRAFFIC_TYPE_RAW = 1, + TRAFFIC_TYPE_DECRYPTED = 2, +}; + +enum forward_type +{ + FORWARD_TYPE_NONE = 0, + FORWARD_TYPE_STEERING = 1, + FORWARD_TYPE_MIRRORING = 2, +}; + +enum session_action +{ + SESSION_ACTION_BYPASS = 0, + SESSION_ACTION_FORWARD = 1, + SESSION_ACTION_BLOCK = 2, +}; + +enum session_action_reason +{ + ACTION_BYPASS_DUE_DEFAULT = 0x00, + + ACTION_BYPASS_DUE_NO_AVAILABLE_SF = 0x11, + ACTION_BYPASS_DUE_HEALTH_SF_LIMIT = 0x12, + ACTION_BYPASS_DUE_UNAVAILABLE_ACTION = 0x13, + ACTION_BYPASS_DUE_FAILURE_ACTION = 0x14, + ACTION_BYPASS_DUE_INVALID_POLICY = 0x15, + + ACTION_BLOCK_DUE_UNAVAILABLE_ACTION = 0x21, + ACTION_BLOCK_DUE_FAILURE_ACTION = 0x22, + + ACTION_FORWAED_DUE_SELECTED_AVAILABLE_SF = 0x31, +}; + +enum package_method +{ + PACKAGE_METHOD_NONE = 0, + PACKAGE_METHOD_LAYER2_SWITCH = 1, + PACKAGE_METHOD_LAYER3_SWITCH = 2, + PACKAGE_METHOD_VXLAN_G = 3, +}; + +enum health_check_method +{ + HEALTH_CHECK_METHOD_NONE = 0, + HEALTH_CHECK_METHOD_IN_BAND_BFD = 1, + HEALTH_CHECK_METHOD_BFD = 2, + HEALTH_CHECK_METHOD_HTTP = 3, +}; + +struct health_check +{ + enum health_check_method method; + + char url[128]; + char address[64]; + int port; + int retires; + int interval_ms; +}; + +struct connectivity +{ + enum package_method method; + int int_vlan_tag; + int ext_vlan_tag; + char dest_ip[64]; +}; + +struct selected_sf +{ + int sff_profile_id; + enum forward_type sff_forward_type; + + int sf_profile_id; + enum session_action sf_action; + enum session_action_reason sf_action_reason; + struct connectivity sf_connectivity; +}; + +struct selected_chaining +{ + int policy_id; + enum traffic_type traffic_type; + + struct selected_sf *chaining; + int chaining_size; + int chaining_index; +}; + +// return NULL : error +// return !NULL : success +struct policy_enforcer *policy_enforcer_create(const char *instance, const char *profile, int thread_num, void *logger); +void policy_enforcer_destory(struct policy_enforcer *enforcer); + +// return 0 : success +// return -1 : error +int policy_enforcer_register(struct policy_enforcer *enforcer); + +struct selected_chaining *selected_chaining_create(int chaining_size); +void selected_chaining_destory(struct selected_chaining *chaining); +void selected_chaining_dump(struct selected_chaining *chaining); +void selected_chaining_bref(struct selected_chaining *chaining); + +// return value need be free by selected_chaining_destory() +struct selected_chaining *policy_enforce_select_chaining(struct policy_enforcer *enforcer, struct raw_pkt_parser *parser, int policy_id, int dir_is_internal); + +#ifdef __cpluscplus +} +#endif + +#endif diff --git a/platform/src/health_check.cpp b/platform/src/health_check.cpp new file mode 100644 index 0000000..caca405 --- /dev/null +++ b/platform/src/health_check.cpp @@ -0,0 +1,65 @@ +#include + +#include "health_check.h" + +struct session_table +{ + // rwlock ???; + // handler; +}; + +static struct session_table g_handle; + +struct session_iterm +{ + int session_id; // key + + struct health_check policy; // value1: deep copy + int is_active; // value2 +}; + +void health_check_session_init() +{ + memset(&g_handle, 0, sizeof(g_handle)); + + // TODO +} + +// return 0 : success +// return -1 : key exist +// struct health_check *policy : need deep copy +int health_check_session_add(int session_id, const struct health_check *policy) +{ + // TODO + return 0; +} + +// return 0 : success +// return -1 : key not exist +int health_check_session_del(int session_id) +{ + // TODO + return 0; +} + +// return 1 : active +// return 0 : inactive +// return -1 : key not exist +int health_check_session_get_status(int session_id) +{ + // TODO + return 1; +} + +// return 0 : success +// return -1 : key not exist +int health_check_session_set_status(int session_id, int is_active) +{ + // TODO + return 0; +} + +void health_check_session_foreach() +{ + // TODO +} \ No newline at end of file diff --git a/platform/src/policy.cpp b/platform/src/policy.cpp new file mode 100644 index 0000000..7c3955a --- /dev/null +++ b/platform/src/policy.cpp @@ -0,0 +1,1479 @@ +#include +#include + +#include +#include +#include + +#include "health_check.h" +#include "raw_packet.h" +#include "policy.h" +#include "utils.h" +#include "log.h" + +/****************************************************************************** + * Struct policy_enforcer + ******************************************************************************/ + +enum input_mode +{ + MAAT_INPUT_JSON = 0, + MAAT_INPUT_REDIS = 1, + MAAT_INPUT_FILE = 2, +}; + +struct policy_config +{ + enum input_mode input_mode; + + int stat_switch; + int perf_switch; + int scan_detail; + int deferred_load; + int effect_interval_ms; + + char device_group[32]; + char stat_file[2048]; + char table_info[2048]; + char accept_tags[2048]; + char accept_path[2048]; + char inc_cfg_dir[2048]; + char ful_cfg_dir[2048]; + char json_cfg_file[2048]; + char foreign_cont_dir[2048]; + + int redis_db_idx; + char redis_server[2048]; + char redis_port_range[2048]; +}; + +struct policy_enforcer +{ + struct policy_config config; + Maat_feather_t maat; + + int compile_table_id; // SERVICE_CHAINING_COMPILE table id + int sff_table_id; // SERVICE_FUNCTION_FORWARDER_PROFILE table id + int sf_table_id; // SERVICE_FUNCTION_PROFILE table id +}; + +/****************************************************************************** + * Struct chaining_param + ******************************************************************************/ + +struct chaining_param +{ + int policy_id; + int ref_cnt; + + enum traffic_type traffic_type; + int *sff_profile_ids; + int sff_profile_ids_num; +}; + +/****************************************************************************** + * Struct sff_param + ******************************************************************************/ + +enum failure_action +{ + FAILURE_ACTION_BYPASS = 1, + FAILURE_ACTION_BLOCK = 2, + FAILURE_ACTION_RE_DISPATCH = 3, +}; + +enum unavailable_action +{ + UNAVAILABLE_ACTION_BYPASSS = 1, + UNAVAILABLE_ACTION_BLOCK = 2, +}; + +enum ldbc_localization +{ + LDBC_LOCALIZATION_NEARBY = 1, + LDBC_LOCALIZATION_GLOBAL = 2, +}; + +struct exception +{ + enum failure_action fail_action; + enum unavailable_action unavail_action; + int health_service_func_lt; +}; + +struct load_balance +{ + enum ldbc_method method; + enum ldbc_localization localiza; +}; + +struct sff_param +{ + int sff_profile_id; + int sff_ref_cnt; + + enum forward_type sff_forward_type; + struct load_balance sff_ldbc; + struct exception sff_exception; + + int *sf_profile_ids; + int sf_profile_ids_num; +}; + +/****************************************************************************** + * Struct sf_param + ******************************************************************************/ + +enum admin_status +{ + ADMMIN_STATUS_ACTIVE = 1, + ADMMIN_STATUS_INACTIVE = 2, +}; + +struct sf_param +{ + int sf_profile_id; + int sf_ref_cnt; + + char sf_device_group[32]; + enum admin_status sf_admin_status; + struct connectivity sf_connectivity; + struct health_check sf_health_check; +}; + +/****************************************************************************** + * Private API + ******************************************************************************/ + +static const char *traffic_type_to_string(enum traffic_type traffic_type) +{ + switch (traffic_type) + { + case TRAFFIC_TYPE_NONE: + return "none"; + case TRAFFIC_TYPE_RAW: + return "raw"; + case TRAFFIC_TYPE_DECRYPTED: + return "decrypted"; + default: + return "unknown"; + } +} + +static const char *forward_type_to_string(enum forward_type forward_type) +{ + switch (forward_type) + { + case FORWARD_TYPE_NONE: + return "none"; + case FORWARD_TYPE_STEERING: + return "steering"; + case FORWARD_TYPE_MIRRORING: + return "mirroring"; + default: + return "unknown"; + } +} + +static const char *session_action_to_string(enum session_action session_action) +{ + switch (session_action) + { + case SESSION_ACTION_BYPASS: + return "bypass"; + case SESSION_ACTION_FORWARD: + return "forward"; + case SESSION_ACTION_BLOCK: + return "block"; + default: + return "unknown"; + } +} + +static const char *session_action_reason_to_string(enum session_action_reason session_action_reason) +{ + switch (session_action_reason) + { + case ACTION_BYPASS_DUE_DEFAULT: + return "bypass_due_default"; + case ACTION_BYPASS_DUE_NO_AVAILABLE_SF: + return "bypass_due_no_available_sf"; + case ACTION_BYPASS_DUE_HEALTH_SF_LIMIT: + return "bypass_due_health_sf_limit"; + case ACTION_BYPASS_DUE_UNAVAILABLE_ACTION: + return "bypass_due_unavailable_action"; + case ACTION_BYPASS_DUE_FAILURE_ACTION: + return "bypass_due_failure_action"; + case ACTION_BYPASS_DUE_INVALID_POLICY: + return "bypass_due_invalid_policy"; + case ACTION_BLOCK_DUE_UNAVAILABLE_ACTION: + return "block_due_unavailable_action"; + case ACTION_BLOCK_DUE_FAILURE_ACTION: + return "block_due_failure_action"; + case ACTION_FORWAED_DUE_SELECTED_AVAILABLE_SF: + return "forward_due_selected_available_sf"; + default: + return "unknown"; + } +} + +static const char *package_method_to_string(enum package_method package_method) +{ + switch (package_method) + { + case PACKAGE_METHOD_NONE: + return "none"; + case PACKAGE_METHOD_LAYER2_SWITCH: + return "layer2_switch"; + case PACKAGE_METHOD_LAYER3_SWITCH: + return "layer3_switch"; + case PACKAGE_METHOD_VXLAN_G: + return "vxlan_g"; + default: + return "unknown"; + } +} + +// {"tags":[{"tag":"device_group","value":"group-xxg-9140"},{"tag":"data_center","value":"center-xxg-9140"}]} +static void parser_device_group(const char *accept_tags, char *buffer) +{ + cJSON *json; + cJSON *tags; + cJSON *elem; + cJSON *item_key; + cJSON *item_val; + + json = cJSON_Parse(accept_tags); + if (json == NULL) + { + LOG_ERROR("%s: unexpected maat config: (invalid accept_tags format) %s", LOG_TAG_POLICY, accept_tags); + goto error_out; + } + + tags = cJSON_GetObjectItem(json, "tags"); + if (!tags || !cJSON_IsArray(tags) || !cJSON_GetArraySize(tags)) + { + LOG_ERROR("%s: unexpected maat config: (invalid accept_tags->tags format) %s", LOG_TAG_POLICY, accept_tags); + goto error_out; + } + + cJSON_ArrayForEach(elem, tags) + { + item_key = cJSON_GetObjectItem(elem, "tag"); + if (!item_key || !cJSON_IsString(item_key)) + { + LOG_ERROR("%s: unexpected maat config: (invalid accept_tags->tags->tag format) %s", LOG_TAG_POLICY, accept_tags); + continue; + } + + item_val = cJSON_GetObjectItem(elem, "value"); + if (!item_val || !cJSON_IsString(item_val)) + { + LOG_ERROR("%s: unexpected maat config: (invalid accept_tags->tags->value format) %s", LOG_TAG_POLICY, accept_tags); + continue; + } + + if (strcasecmp(item_key->valuestring, "device_group") == 0) + { + memcpy(buffer, item_val->valuestring, strlen(item_val->valuestring)); + break; + } + } + +error_out: + if (json) + { + cJSON_Delete(json); + json = NULL; + } +} + +static void policy_enforcer_config(const char *profile, struct policy_config *config) +{ + MESA_load_profile_int_def(profile, "MAAT", "input_mode", (int *)&(config->input_mode), MAAT_INPUT_REDIS); + MESA_load_profile_int_def(profile, "MAAT", "stat_switch", &(config->stat_switch), 1); + MESA_load_profile_int_def(profile, "MAAT", "perf_switch", &(config->perf_switch), 1); + MESA_load_profile_int_def(profile, "MAAT", "scan_detail", &(config->scan_detail), 0); + MESA_load_profile_int_def(profile, "MAAT", "deferred_load", &(config->deferred_load), 0); + MESA_load_profile_int_def(profile, "MAAT", "effect_interval_ms", &(config->effect_interval_ms), 1000); + + MESA_load_profile_string_def(profile, "MAAT", "stat_file", config->stat_file, sizeof(config->stat_file), "log/sce.fs2"); + MESA_load_profile_string_def(profile, "MAAT", "table_info", config->table_info, sizeof(config->table_info), "resource/table_info.conf"); + MESA_load_profile_string_def(profile, "MAAT", "accept_path", config->accept_path, sizeof(config->accept_path), "/opt/tsg/etc/tsg_device_tag.json"); + MESA_load_profile_string_def(profile, "MAAT", "inc_cfg_dir", config->inc_cfg_dir, sizeof(config->inc_cfg_dir), "resource/inc/"); + MESA_load_profile_string_def(profile, "MAAT", "ful_cfg_dir", config->ful_cfg_dir, sizeof(config->ful_cfg_dir), "resource/ful/"); + MESA_load_profile_string_def(profile, "MAAT", "json_cfg_file", config->json_cfg_file, sizeof(config->json_cfg_file), "resource/sce.json"); + MESA_load_profile_string_def(profile, "MAAT", "foreign_cont_dir", config->foreign_cont_dir, sizeof(config->foreign_cont_dir), "resource/sce_files"); + + MESA_load_profile_int_def(profile, "MAAT", "redis_db_idx", &(config->redis_db_idx), 0); + MESA_load_profile_string_def(profile, "MAAT", "redis_server", config->redis_server, sizeof(config->redis_server), "127.0.0.1"); + MESA_load_profile_string_def(profile, "MAAT", "redis_port_range", config->redis_port_range, sizeof(config->redis_server), "6379"); + + if (strlen(config->accept_path)) + { + MESA_load_profile_string_def(config->accept_path, "MAAT", "accept_tags", config->accept_tags, sizeof(config->accept_tags), "{\"tags\":[{\"tag\":\"device_id\",\"value\":\"device_1\"}]}"); + parser_device_group(config->accept_tags, config->device_group); + } + + LOG_DEBUG("%s: MAAT->input_mode : %s", LOG_TAG_POLICY, (config->input_mode == MAAT_INPUT_REDIS ? "redis" : (config->input_mode == MAAT_INPUT_JSON ? "json" : (config->input_mode == MAAT_INPUT_FILE ? "file" : "unknown")))); + LOG_DEBUG("%s: MAAT->stat_switch : %d", LOG_TAG_POLICY, config->stat_switch); + LOG_DEBUG("%s: MAAT->perf_switch : %d", LOG_TAG_POLICY, config->perf_switch); + LOG_DEBUG("%s: MAAT->scan_detail : %d", LOG_TAG_POLICY, config->scan_detail); + LOG_DEBUG("%s: MAAT->deferred_load : %d", LOG_TAG_POLICY, config->deferred_load); + LOG_DEBUG("%s: MAAT->effect_interval_ms : %d", LOG_TAG_POLICY, config->effect_interval_ms); + + LOG_DEBUG("%s: MAAT->stat_file : %s", LOG_TAG_POLICY, config->stat_file); + LOG_DEBUG("%s: MAAT->table_info : %s", LOG_TAG_POLICY, config->table_info); + LOG_DEBUG("%s: MAAT->accept_path : %s", LOG_TAG_POLICY, config->accept_path); + LOG_DEBUG("%s: MAAT->accept_tags : %s", LOG_TAG_POLICY, config->accept_tags); + LOG_DEBUG("%s: MAAT->device_group : %s", LOG_TAG_POLICY, config->device_group); + LOG_DEBUG("%s: MAAT->inc_cfg_dir : %s", LOG_TAG_POLICY, config->inc_cfg_dir); + LOG_DEBUG("%s: MAAT->ful_cfg_dir : %s", LOG_TAG_POLICY, config->ful_cfg_dir); + LOG_DEBUG("%s: MAAT->json_cfg_file : %s", LOG_TAG_POLICY, config->json_cfg_file); + LOG_DEBUG("%s: MAAT->foreign_cont_dir : %s", LOG_TAG_POLICY, config->foreign_cont_dir); + + LOG_DEBUG("%s: MAAT->redis_db_idx : %d", LOG_TAG_POLICY, config->redis_db_idx); + LOG_DEBUG("%s: MAAT->redis_server : %s", LOG_TAG_POLICY, config->redis_server); + LOG_DEBUG("%s: MAAT->redis_port_range : %s", LOG_TAG_POLICY, config->redis_port_range); +} + +static void chaining_param_new_cb(int table_id, const char *key, const char *table_line, MAAT_PLUGIN_EX_DATA *ad, long argl, void *argp) +{ + int iter = 0; + cJSON *json = NULL; + cJSON *item = NULL; + cJSON *element = NULL; + size_t user_region_offset = 0; + size_t user_region_len = 0; + struct chaining_param *param = NULL; + + if (Maat_helper_read_column(table_line, 7, &user_region_offset, &user_region_len) < 0) + { + LOG_ERROR("%s: unexpected chaining policy: (invalid user region) %s", LOG_TAG_POLICY, table_line); + return; + } + + char *json_str = (char *)calloc(user_region_len + 1, sizeof(char)); + memcpy(json_str, table_line + user_region_offset, user_region_len); + json = cJSON_Parse(json_str); + if (json == NULL) + { + LOG_ERROR("%s: unexpected chaining policy: (invalid json format) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + + param = (struct chaining_param *)calloc(1, sizeof(struct chaining_param)); + param->policy_id = atoi(key); + param->ref_cnt = 1; + + // targeted_traffic + item = cJSON_GetObjectItem(json, "targeted_traffic"); + if (!item || !cJSON_IsString(item)) + { + LOG_ERROR("%s: unexpected chaining policy: (invalid targeted_traffic param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + if (strcasecmp(item->valuestring, "raw") == 0) + { + LOG_DEBUG("%s: parse chaining policy: %d, targeted_traffic: raw", LOG_TAG_POLICY, param->policy_id); + param->traffic_type = TRAFFIC_TYPE_RAW; + } + else if (strcasecmp(item->valuestring, "decrypted") == 0) + { + LOG_DEBUG("%s: parse chaining policy: %d, targeted_traffic: decrypted", LOG_TAG_POLICY, param->policy_id); + param->traffic_type = TRAFFIC_TYPE_DECRYPTED; + } + else + { + LOG_ERROR("%s: unexpected chaining policy: (invalid targeted_traffic param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + + // sff_profiles + item = cJSON_GetObjectItem(json, "sff_profiles"); + if (!item || !cJSON_IsArray(item) || !cJSON_GetArraySize(item)) + { + LOG_ERROR("%s: unexpected chaining policy: (invalid sff_profiles param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + param->sff_profile_ids_num = cJSON_GetArraySize(item); + param->sff_profile_ids = (int *)calloc(param->sff_profile_ids_num, sizeof(int)); + cJSON_ArrayForEach(element, item) + { + if (!cJSON_IsNumber(element)) + { + LOG_ERROR("%s: unexpected chaining policy: (invalid sff_profiles param) %s", LOG_TAG_POLICY, table_line); + continue; + } + + LOG_DEBUG("%s: parse chaining policy: %d, sff_profiles[%d/%d]: %d", LOG_TAG_POLICY, param->policy_id, iter, param->sff_profile_ids_num, element->valueint); + param->sff_profile_ids[iter] = element->valueint; + iter++; + } + + *ad = param; + LOG_INFO("%s: Add chaining policy: %d", LOG_TAG_POLICY, param->policy_id); + + cJSON_Delete(json); + free(json_str); + return; + +error_out: + if (json) + { + cJSON_Delete(json); + json = NULL; + } + + if (json_str) + { + free(json_str); + json_str = NULL; + } + + if (param) + { + if (param->sff_profile_ids) + { + free(param->sff_profile_ids); + param->sff_profile_ids = NULL; + } + free(param); + param = NULL; + } +} + +static void chaining_param_free_cb(int table_id, MAAT_PLUGIN_EX_DATA *ad, long argl, void *argp) +{ + struct chaining_param *param = (struct chaining_param *)*ad; + if (param == NULL) + { + return; + } + + if ((__sync_sub_and_fetch(¶m->ref_cnt, 1) == 0)) + { + LOG_INFO("%s: Del chaining policy: %d", LOG_TAG_POLICY, param->policy_id); + if (param->sff_profile_ids) + { + free(param->sff_profile_ids); + param->sff_profile_ids = NULL; + } + free(param); + param = NULL; + + *ad = NULL; + } +} + +static void chaining_param_dup_cb(int table_id, MAAT_PLUGIN_EX_DATA *to, MAAT_PLUGIN_EX_DATA *from, long argl, void *argp) +{ + struct chaining_param *param = (struct chaining_param *)*from; + if (param) + { + __sync_add_and_fetch(&(param->ref_cnt), 1); + *to = param; + } + else + { + *to = NULL; + } +} + +static void chaining_param_free(struct chaining_param *param) +{ + chaining_param_free_cb(0, (void **)¶m, 0, NULL); +} + +static void sff_param_new_cb(int table_id, const char *key, const char *table_line, MAAT_PLUGIN_EX_DATA *ad, long argl, void *argp) +{ + int iter = 0; + struct sff_param *param = NULL; + cJSON *root1 = NULL; + cJSON *root2 = NULL; + cJSON *item = NULL; + + int profile_id = 0; + int type = 0; + char load_balance_method[32] = {0}; + char load_balance_localization[8] = {0}; + char failure_action[16] = {0}; + char unavailability_action[64] = {0}; + char service_func_profiles[128] = {0}; + int is_valid = 0; + + if (sscanf(table_line, "%d\t%d\t%s\t%s\t%s\t%s\t%s\t%d", + &profile_id, &type, + load_balance_method, load_balance_localization, failure_action, unavailability_action, service_func_profiles, + &is_valid) != 8) + { + LOG_ERROR("%s: unexpected sff profile: %s", LOG_TAG_POLICY, table_line); + return; + } + + param = (struct sff_param *)calloc(1, sizeof(struct sff_param)); + param->sff_profile_id = atoi(key); + param->sff_ref_cnt = 1; + + // type + switch (type) + { + case 1: + param->sff_forward_type = FORWARD_TYPE_STEERING; + LOG_DEBUG("%s: parse sff profile: %d, type: steering", LOG_TAG_POLICY, param->sff_profile_id); + break; + case 2: + param->sff_forward_type = FORWARD_TYPE_MIRRORING; + LOG_DEBUG("%s: parse sff profile: %d, type: mirroring", LOG_TAG_POLICY, param->sff_profile_id); + break; + default: + LOG_ERROR("%s: unexpected sff profile: (invalid type param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + + // load_balance_method + if (0 == strcasecmp(load_balance_method, "hash-int-ip")) + { + param->sff_ldbc.method = LDBC_METHOD_HASH_INT_IP; + } + else if (0 == strcasecmp(load_balance_method, "hash-ext-ip")) + { + param->sff_ldbc.method = LDBC_METHOD_HASH_EXT_IP; + } + else if (0 == strcasecmp(load_balance_method, "hash-int-ip-and-ext-ip")) + { + param->sff_ldbc.method = LDBC_METHOD_HASH_INT_IP_AND_EXT_IP; + } + else if (0 == strcasecmp(load_balance_method, "hash-innermost-int-ip")) + { + param->sff_ldbc.method = LDBC_METHOD_HASH_INNERMOST_INT_IP; + } + else + { + // not support LDBC_METHOD_HASH_INNERMOST_EXT_IP + LOG_ERROR("%s: unexpected sff profile: (invalid load_balance_method param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + LOG_DEBUG("%s: parse sff profile: %d, load_balance_method: %s", LOG_TAG_POLICY, param->sff_profile_id, load_balance_method); + + // load_balance_localization + if (0 == strcasecmp(load_balance_localization, "nearby")) + { + param->sff_ldbc.localiza = LDBC_LOCALIZATION_NEARBY; + } + else if (0 == strcasecmp(load_balance_localization, "global")) + { + param->sff_ldbc.localiza = LDBC_LOCALIZATION_GLOBAL; + } + else + { + LOG_ERROR("%s: unexpected sff profile: (invalid load_balance_localization param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + LOG_DEBUG("%s: parse sff profile: %d, load_balance_localization: %s", LOG_TAG_POLICY, param->sff_profile_id, load_balance_localization); + + // failure_action + if (0 == strcasecmp(failure_action, "bypass")) + { + param->sff_exception.fail_action = FAILURE_ACTION_BYPASS; + } + else if (0 == strcasecmp(failure_action, "block")) + { + param->sff_exception.fail_action = FAILURE_ACTION_BLOCK; + } + else if (0 == strcasecmp(failure_action, "re-dispatch")) + { + param->sff_exception.fail_action = FAILURE_ACTION_RE_DISPATCH; + } + else + { + LOG_ERROR("%s: unexpected sff profile: (invalid failure_action param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + LOG_DEBUG("%s: parse sff profile: %d, failure_action: %s", LOG_TAG_POLICY, param->sff_profile_id, failure_action); + + // unavailability_action + if (param->sff_exception.fail_action == FAILURE_ACTION_RE_DISPATCH) + { + root1 = cJSON_Parse(unavailability_action); + if (root1 == NULL) + { + LOG_ERROR("%s: unexpected sff profile: (invalid unavailability_action param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + item = cJSON_GetObjectItem(root1, "action"); + if (!item || !cJSON_IsString(item)) + { + LOG_ERROR("%s: unexpected chaining policy: (invalid unavailability_action->action param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + if (0 == strcasecmp(item->valuestring, "bypass")) + { + param->sff_exception.unavail_action = UNAVAILABLE_ACTION_BYPASSS; + } + else if (0 == strcasecmp(item->valuestring, "block")) + { + param->sff_exception.unavail_action = UNAVAILABLE_ACTION_BLOCK; + } + else + { + LOG_ERROR("%s: unexpected chaining policy: (invalid unavailability_action->action param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + LOG_DEBUG("%s: parse sff profile: %d, unavailability_action->action: %s", LOG_TAG_POLICY, param->sff_profile_id, item->valuestring); + + item = cJSON_GetObjectItem(root1, "health_service_func_lt"); + if (item && cJSON_IsNumber(item)) + { + param->sff_exception.health_service_func_lt = item->valueint; + LOG_DEBUG("%s: parse sff profile: %d, unavailability_action->health_service_func_lt: %d", LOG_TAG_POLICY, param->sff_profile_id, item->valueint); + } + } + + // service_func_profiles + root2 = cJSON_Parse(service_func_profiles); + if (root2 == NULL || !cJSON_IsArray(root2) || !cJSON_GetArraySize(root2)) + { + LOG_ERROR("%s: unexpected sff profile: (invalid service_func_profiles param) %s", LOG_TAG_POLICY, table_line); + return; + } + param->sf_profile_ids_num = cJSON_GetArraySize(root2); + param->sf_profile_ids = (int *)calloc(param->sf_profile_ids_num, sizeof(int)); + cJSON_ArrayForEach(item, root2) + { + if (!cJSON_IsNumber(item)) + { + LOG_ERROR("%s: unexpected sff profile: (invalid service_func_profiles param) %s", LOG_TAG_POLICY, table_line); + continue; + } + LOG_DEBUG("%s: parse sff profile: %d, service_func_profiles[%d/%d] = %d", LOG_TAG_POLICY, param->sff_profile_id, iter, param->sf_profile_ids_num, item->valueint); + param->sf_profile_ids[iter] = item->valueint; + iter++; + } + + *ad = param; + LOG_INFO("%s: Add sff profile: %d", LOG_TAG_POLICY, param->sff_profile_id); + + cJSON_Delete(root1); + cJSON_Delete(root2); + return; + +error_out: + if (root1) + { + cJSON_Delete(root1); + root1 = NULL; + } + + if (root2) + { + cJSON_Delete(root2); + root2 = NULL; + } + + if (param) + { + if (param->sf_profile_ids) + { + free(param->sf_profile_ids); + param->sf_profile_ids = NULL; + } + free(param); + param = NULL; + } +} + +static void sff_param_free_cb(int table_id, MAAT_PLUGIN_EX_DATA *ad, long argl, void *argp) +{ + struct sff_param *param = (struct sff_param *)*ad; + if (param == NULL) + { + return; + } + + if ((__sync_sub_and_fetch(¶m->sff_ref_cnt, 1) == 0)) + { + LOG_INFO("%s: Del sff profile: %d", LOG_TAG_POLICY, param->sff_profile_id); + if (param->sf_profile_ids) + { + free(param->sf_profile_ids); + param->sf_profile_ids = NULL; + } + free(param); + param = NULL; + + *ad = NULL; + } +} + +static void sff_param_dup_cb(int table_id, MAAT_PLUGIN_EX_DATA *to, MAAT_PLUGIN_EX_DATA *from, long argl, void *argp) +{ + struct sff_param *param = (struct sff_param *)*from; + if (param) + { + __sync_add_and_fetch(&(param->sff_ref_cnt), 1); + *to = param; + } + else + { + *to = NULL; + } +} + +static void sff_param_free(struct sff_param *param) +{ + sff_param_free_cb(0, (void **)¶m, 0, NULL); +} + +static void sf_param_new_cb(int table_id, const char *key, const char *table_line, MAAT_PLUGIN_EX_DATA *ad, long argl, void *argp) +{ + struct sf_param *param = NULL; + cJSON *root1 = NULL; + cJSON *root2 = NULL; + cJSON *item = NULL; + + int profile_id = 0; + char device_group[32] = {0}; + int admin_status = 0; + char connectivity[128] = {0}; + char health_check[128] = {0}; + int is_valid = 0; + + if (sscanf(table_line, "%d\t%s\t%d\t%s\t%s\t%d", + &profile_id, device_group, &admin_status, connectivity, health_check, &is_valid) != 6) + { + LOG_ERROR("%s: unexpected sf profile: %s", LOG_TAG_POLICY, table_line); + return; + } + + param = (struct sf_param *)calloc(1, sizeof(struct sf_param)); + param->sf_profile_id = atoi(key); + param->sf_ref_cnt = 1; + memcpy(param->sf_device_group, device_group, strlen(device_group)); + + // admin_status + switch (admin_status) + { + case 1: + param->sf_admin_status = ADMMIN_STATUS_ACTIVE; + LOG_DEBUG("%s: parse sf profile: %d, admin_status: active", LOG_TAG_POLICY, param->sf_profile_id); + break; + case 0: + param->sf_admin_status = ADMMIN_STATUS_INACTIVE; + LOG_DEBUG("%s: parse sf profile: %d, admin_status: inactive", LOG_TAG_POLICY, param->sf_profile_id); + break; + default: + LOG_ERROR("%s: unexpected sf profile: (invalid admin_status param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + + // connectivity + root1 = cJSON_Parse(connectivity); + if (root1 == NULL) + { + LOG_ERROR("%s: unexpected sf profile: (invalid connectivity param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + + item = cJSON_GetObjectItem(root1, "method"); + if (!item || !cJSON_IsString(item)) + { + LOG_ERROR("%s: unexpected sf profile: (invalid connectivity->method param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + if (0 == strcasecmp(item->valuestring, "layer2_switch")) + { + param->sf_connectivity.method = PACKAGE_METHOD_LAYER2_SWITCH; + } + else if (0 == strcasecmp(item->valuestring, "layer3_switch")) + { + param->sf_connectivity.method = PACKAGE_METHOD_LAYER3_SWITCH; + } + else if (0 == strcasecmp(item->valuestring, "vxlan_g")) + { + param->sf_connectivity.method = PACKAGE_METHOD_VXLAN_G; + } + else + { + LOG_ERROR("%s: unexpected sf profile: (invalid connectivity->method param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + LOG_DEBUG("%s: parse sf profile: %d, connectivity->method: %s", LOG_TAG_POLICY, param->sf_profile_id, item->valuestring); + + if (param->sf_connectivity.method == PACKAGE_METHOD_LAYER2_SWITCH || param->sf_connectivity.method == PACKAGE_METHOD_LAYER3_SWITCH) + { + item = cJSON_GetObjectItem(root1, "int_vlan_tag"); + if (!item || !cJSON_IsNumber(item)) + { + LOG_ERROR("%s: unexpected sf profile: (invalid connectivity->int_vlan_tag param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + param->sf_connectivity.int_vlan_tag = item->valueint; + LOG_DEBUG("%s: parse sf profile: %d, connectivity->int_vlan_tag: %d", LOG_TAG_POLICY, param->sf_profile_id, item->valueint); + + item = cJSON_GetObjectItem(root1, "ext_vlan_tag"); + if (!item || !cJSON_IsNumber(item)) + { + LOG_ERROR("%s: unexpected sf profile: (invalid connectivity->ext_vlan_tag param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + param->sf_connectivity.ext_vlan_tag = item->valueint; + LOG_DEBUG("%s: parse sf profile: %d, connectivity->ext_vlan_tag: %d", LOG_TAG_POLICY, param->sf_profile_id, item->valueint); + } + else if (param->sf_connectivity.method == PACKAGE_METHOD_VXLAN_G) + { + item = cJSON_GetObjectItem(root1, "dest_ip"); + if (!item || !cJSON_IsString(item)) + { + LOG_ERROR("%s: unexpected sf profile: (invalid connectivity->dest_ip param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + memcpy(param->sf_connectivity.dest_ip, item->valuestring, strlen(item->valuestring)); + LOG_DEBUG("%s: parse sf profile: %d, connectivity->dest_ip: %s", LOG_TAG_POLICY, param->sf_profile_id, item->valuestring); + } + + // health_check + root2 = cJSON_Parse(health_check); + if (root2 == NULL) + { + LOG_ERROR("%s: unexpected sf profile: (invalid health_check param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + item = cJSON_GetObjectItem(root2, "method"); + if (!item || !cJSON_IsString(item)) + { + LOG_ERROR("%s: unexpected sf profile: (invalid health_check->method param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + if (0 == strcasecmp(item->valuestring, "none")) + { + param->sf_health_check.method = HEALTH_CHECK_METHOD_NONE; + } + else if (0 == strcasecmp(item->valuestring, "in_band_bfd")) + { + param->sf_health_check.method = HEALTH_CHECK_METHOD_IN_BAND_BFD; + } + else if (0 == strcasecmp(item->valuestring, "bfd")) + { + param->sf_health_check.method = HEALTH_CHECK_METHOD_BFD; + } + else if (0 == strcasecmp(item->valuestring, "http")) + { + param->sf_health_check.method = HEALTH_CHECK_METHOD_HTTP; + } + else + { + LOG_ERROR("%s: unexpected sf profile: (invalid health_check->method param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + LOG_DEBUG("%s: parse sf profile: %d, health_check->method: %s", LOG_TAG_POLICY, param->sf_profile_id, item->valuestring); + + if (param->sf_health_check.method == HEALTH_CHECK_METHOD_BFD || param->sf_health_check.method == HEALTH_CHECK_METHOD_IN_BAND_BFD) + { + item = cJSON_GetObjectItem(root2, "address"); + if (!item || !cJSON_IsString(item)) + { + LOG_ERROR("%s: unexpected sf profile: (invalid health_check->address param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + memcpy(param->sf_health_check.address, item->valuestring, strlen(item->valuestring)); + LOG_DEBUG("%s: parse sf profile: %d, health_check->address: %s", LOG_TAG_POLICY, param->sf_profile_id, item->valuestring); + + item = cJSON_GetObjectItem(root2, "port"); + if (!item || !cJSON_IsString(item)) + { + LOG_ERROR("%s: unexpected sf profile: (invalid health_check->port param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + param->sf_health_check.port = atoi(item->valuestring); + LOG_DEBUG("%s: parse sf profile: %d, health_check->port: %s", LOG_TAG_POLICY, param->sf_profile_id, item->valuestring); + } + + if (param->sf_health_check.method == HEALTH_CHECK_METHOD_HTTP) + { + item = cJSON_GetObjectItem(root2, "url"); + if (!item || !cJSON_IsString(item)) + { + LOG_ERROR("%s: unexpected sf profile: (invalid health_check->url param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + memcpy(param->sf_health_check.url, item->valuestring, strlen(item->valuestring)); + LOG_DEBUG("%s: parse sf profile: %d, health_check->url: %s", LOG_TAG_POLICY, param->sf_profile_id, item->valuestring); + } + + if (param->sf_health_check.method == HEALTH_CHECK_METHOD_HTTP || param->sf_health_check.method == HEALTH_CHECK_METHOD_BFD || param->sf_health_check.method == HEALTH_CHECK_METHOD_IN_BAND_BFD) + { + item = cJSON_GetObjectItem(root2, "interval_ms"); + if (!item || !cJSON_IsNumber(item)) + { + LOG_ERROR("%s: unexpected sf profile: (invalid health_check->interval_ms param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + param->sf_health_check.interval_ms = item->valueint; + LOG_DEBUG("%s: parse sf profile: %d, health_check->interval_ms: %d", LOG_TAG_POLICY, param->sf_profile_id, item->valueint); + + item = cJSON_GetObjectItem(root2, "retires"); + if (!item || !cJSON_IsNumber(item)) + { + LOG_ERROR("%s: unexpected sf profile: (invalid health_check->retires param) %s", LOG_TAG_POLICY, table_line); + goto error_out; + } + param->sf_health_check.retires = item->valueint; + LOG_DEBUG("%s: parse sf profile: %d, health_check->retires: %d", LOG_TAG_POLICY, param->sf_profile_id, item->valueint); + } + health_check_session_add(param->sf_profile_id, ¶m->sf_health_check); + + *ad = param; + LOG_INFO("%s: Add sf profile: %d", LOG_TAG_POLICY, param->sf_profile_id); + + cJSON_Delete(root1); + cJSON_Delete(root2); + return; + +error_out: + if (root1) + { + cJSON_Delete(root1); + root1 = NULL; + } + + if (root2) + { + cJSON_Delete(root2); + root2 = NULL; + } + + if (param) + { + free(param); + param = NULL; + } +} + +static void sf_param_free_cb(int table_id, MAAT_PLUGIN_EX_DATA *ad, long argl, void *argp) +{ + struct sf_param *param = (struct sf_param *)*ad; + if (param == NULL) + { + return; + } + + if ((__sync_sub_and_fetch(¶m->sf_ref_cnt, 1) == 0)) + { + health_check_session_del(param->sf_profile_id); + LOG_INFO("%s: Del sf profile: %d", LOG_TAG_POLICY, param->sf_profile_id); + free(param); + param = NULL; + + *ad = NULL; + } +} + +static void sf_param_dup_cb(int table_id, MAAT_PLUGIN_EX_DATA *to, MAAT_PLUGIN_EX_DATA *from, long argl, void *argp) +{ + struct sf_param *param = (struct sf_param *)*from; + if (param) + { + __sync_add_and_fetch(&(param->sf_ref_cnt), 1); + *to = param; + } + else + { + *to = NULL; + } +} + +static void sf_param_free(struct sf_param *param) +{ + sf_param_free_cb(0, (void **)¶m, 0, NULL); +} + +// After return must check array elem nums +static void select_sf_by_nearby_and_active(struct policy_enforcer *enforcer, struct sff_param *sff_param, struct fixed_num_array *array) +{ + char buffer[16]; + struct sf_param *sf = NULL; + + for (int i = 0; i < sff_param->sf_profile_ids_num; i++) + { + memset(&buffer, 0, sizeof(buffer)); + snprintf(buffer, sizeof(buffer), "%u", sff_param->sf_profile_ids[i]); + sf = (struct sf_param *)Maat_plugin_get_EX_data(enforcer->maat, enforcer->sf_table_id, buffer); + if (sf == NULL) + { + LOG_ERROR("%s: failed to get sf parameter of profile %d", LOG_TAG_POLICY, sff_param->sf_profile_ids[i]); + continue; + } + + if (sff_param->sff_ldbc.localiza == LDBC_LOCALIZATION_NEARBY) + { + if (strcasecmp(enforcer->config.device_group, sf->sf_device_group) == 0) + { + if (sf->sf_admin_status == ADMMIN_STATUS_ACTIVE) + { + fixed_num_array_add_elem(array, sff_param->sf_profile_ids[i]); + } + } + } + else + { + if (sf->sf_admin_status == ADMMIN_STATUS_ACTIVE) + { + fixed_num_array_add_elem(array, sff_param->sf_profile_ids[i]); + } + } + sf_param_free(sf); + } +} + +// return : SESSION_ACTION_BYPASS, not care selected_sf_profile_id +// return : SESSION_ACTION_BLOCK, not care selected_sf_profile_id +// return : SESSION_ACTION_FORWARD, care selected_sf_profile_id +static enum session_action select_sf_by_ldbc(uint64_t hash, struct sff_param *sff_param, struct fixed_num_array *array, int *selected_sf_profile_id, enum session_action_reason *sf_action_reason) +{ + *selected_sf_profile_id = -1; + int sf_is_active = 0; + int sf_profile_id = 0; + int sf_profile_index = 0; + int sf_profile_num = 0; + + sf_profile_num = fixed_num_array_count_elem(array); + + while (sf_profile_num) + { + sf_profile_index = (int)(hash % sf_profile_num); + sf_profile_id = fixed_num_array_index_elem(array, sf_profile_index); + sf_is_active = health_check_session_get_status(sf_profile_id); + + if (sf_is_active) + { + *selected_sf_profile_id = sf_profile_id; + *sf_action_reason = ACTION_FORWAED_DUE_SELECTED_AVAILABLE_SF; + return SESSION_ACTION_FORWARD; + } + else + { + if (sff_param->sff_exception.fail_action == FAILURE_ACTION_RE_DISPATCH) + { + fixed_num_array_del_elem(array, sf_profile_id); + sf_profile_num = fixed_num_array_count_elem(array); + + if (sff_param->sff_exception.health_service_func_lt > 0 && sf_profile_num < sff_param->sff_exception.health_service_func_lt) + { + *sf_action_reason = ACTION_BYPASS_DUE_HEALTH_SF_LIMIT; + return SESSION_ACTION_BYPASS; + } + else + { + if (sf_profile_num == 0) + { + if (sff_param->sff_exception.unavail_action == UNAVAILABLE_ACTION_BYPASSS) + { + *sf_action_reason = ACTION_BYPASS_DUE_UNAVAILABLE_ACTION; + return SESSION_ACTION_BYPASS; + } + else + { + *sf_action_reason = ACTION_BLOCK_DUE_UNAVAILABLE_ACTION; + return SESSION_ACTION_BLOCK; + } + } + else + { + continue; + } + } + } + else if (sff_param->sff_exception.fail_action == FAILURE_ACTION_BYPASS) + { + *selected_sf_profile_id = sf_profile_id; + *sf_action_reason = ACTION_BYPASS_DUE_FAILURE_ACTION; + return SESSION_ACTION_BYPASS; + } + else if (sff_param->sff_exception.fail_action == FAILURE_ACTION_BLOCK) + { + *selected_sf_profile_id = sf_profile_id; + *sf_action_reason = ACTION_BLOCK_DUE_FAILURE_ACTION; + return SESSION_ACTION_BLOCK; + } + } + }; + + *sf_action_reason = ACTION_BYPASS_DUE_NO_AVAILABLE_SF; + return SESSION_ACTION_BYPASS; +} + +/****************************************************************************** + * Public API + ******************************************************************************/ + +// return NULL : error +// return !NULL : success +struct policy_enforcer *policy_enforcer_create(const char *instance, const char *profile, int thread_num, void *logger) +{ + int ret = 0; + int redis_port_begin = 0; + int redis_port_end = 0; + int redis_port_select = 0; + + struct policy_enforcer *enforcer = (struct policy_enforcer *)calloc(1, sizeof(struct policy_enforcer)); + assert(enforcer); + policy_enforcer_config(profile, &(enforcer->config)); + + enforcer->maat = Maat_feather(thread_num, enforcer->config.table_info, logger); + if (enforcer->maat == NULL) + { + LOG_ERROR("%s: unable create maat feather", LOG_TAG_POLICY); + goto error_out; + } + + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_INSTANCE_NAME, instance, strlen(instance)); + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_FOREIGN_CONT_DIR, enforcer->config.foreign_cont_dir, strlen(enforcer->config.foreign_cont_dir)); + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_SCAN_DETAIL, &(enforcer->config.scan_detail), sizeof(enforcer->config.scan_detail)); + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_EFFECT_INVERVAL_MS, &(enforcer->config.effect_interval_ms), sizeof(enforcer->config.effect_interval_ms)); + + switch (enforcer->config.input_mode) + { + case MAAT_INPUT_JSON: + if (!strlen(enforcer->config.json_cfg_file)) + { + LOG_ERROR("%s: invalid json_cfg_file", LOG_TAG_POLICY); + goto error_out; + } + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_JSON_FILE_PATH, enforcer->config.json_cfg_file, strlen(enforcer->config.json_cfg_file)); + break; + case MAAT_INPUT_REDIS: + if (!strlen(enforcer->config.redis_server)) + { + LOG_ERROR("%s: invalid redis_server", LOG_TAG_POLICY); + goto error_out; + } + ret = sscanf(enforcer->config.redis_port_range, "%d-%d", &redis_port_begin, &redis_port_end); + if (ret == 1) + { + redis_port_select = redis_port_begin; + } + else if (ret == 2) + { + srand(time(NULL)); + redis_port_select = redis_port_begin + rand() % (redis_port_end - redis_port_begin); + } + else + { + LOG_ERROR("%s: invalid redis_port_range", LOG_TAG_POLICY); + goto error_out; + } + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_REDIS_PORT, &redis_port_select, sizeof(redis_port_select)); + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_REDIS_IP, enforcer->config.redis_server, strlen(enforcer->config.redis_server)); + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_REDIS_INDEX, &(enforcer->config.redis_db_idx), sizeof(enforcer->config.redis_db_idx)); + break; + case MAAT_INPUT_FILE: + if (!strlen(enforcer->config.ful_cfg_dir)) + { + LOG_ERROR("%s: invalid ful_cfg_dir", LOG_TAG_POLICY); + goto error_out; + } + if (!strlen(enforcer->config.inc_cfg_dir)) + { + LOG_ERROR("%s: invalid inc_cfg_dir", LOG_TAG_POLICY); + goto error_out; + } + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_FULL_CFG_DIR, enforcer->config.ful_cfg_dir, strlen(enforcer->config.ful_cfg_dir)); + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_INC_CFG_DIR, enforcer->config.inc_cfg_dir, strlen(enforcer->config.inc_cfg_dir)); + break; + default: + LOG_ERROR("%s: invalid input_mode %d", LOG_TAG_POLICY, enforcer->config.input_mode); + goto error_out; + } + + if (enforcer->config.stat_switch) + { + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_STAT_FILE_PATH, enforcer->config.stat_file, strlen(enforcer->config.stat_file)); + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_STAT_ON, NULL, 0); + if (enforcer->config.perf_switch) + { + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_PERF_ON, NULL, 0); + } + } + + if (enforcer->config.deferred_load) + { + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_DEFERRED_LOAD, NULL, 0); + } + + if (strlen(enforcer->config.accept_tags)) + { + Maat_set_feather_opt(enforcer->maat, MAAT_OPT_ACCEPT_TAGS, &(enforcer->config.accept_tags), sizeof(enforcer->config.accept_tags)); + } + + if (Maat_initiate_feather(enforcer->maat) < 0) + { + LOG_ERROR("%s: maat init feather failed", LOG_TAG_POLICY); + goto error_out; + } + + return enforcer; + +error_out: + policy_enforcer_destory(enforcer); + return NULL; +} + +void policy_enforcer_destory(struct policy_enforcer *enforcer) +{ + if (enforcer) + { + if (enforcer->maat) + { + Maat_burn_feather(enforcer->maat); + enforcer->maat = NULL; + } + + free(enforcer); + enforcer = NULL; + } +} + +// return 0 : success +// return -1 : error +int policy_enforcer_register(struct policy_enforcer *enforcer) +{ + enforcer->compile_table_id = Maat_table_register(enforcer->maat, "SERVICE_CHAINING_COMPILE"); + if (enforcer->compile_table_id < 0) + { + LOG_ERROR("%s: register SERVICE_CHAINING_COMPILE table failed", LOG_TAG_POLICY); + return -1; + } + + enforcer->sff_table_id = Maat_table_register(enforcer->maat, "SERVICE_FUNCTION_FORWARDER_PROFILE"); + if (enforcer->sff_table_id < 0) + { + LOG_ERROR("%s: register SERVICE_FUNCTION_FORWARDER_PROFILE table ailed", LOG_TAG_POLICY); + return -1; + } + + enforcer->sf_table_id = Maat_table_register(enforcer->maat, "SERVICE_FUNCTION_PROFILE"); + if (enforcer->sf_table_id < 0) + { + LOG_ERROR("%s: register SERVICE_FUNCTION_PROFILE table failed", LOG_TAG_POLICY); + return -1; + } + + if (Maat_plugin_EX_register(enforcer->maat, enforcer->compile_table_id, + chaining_param_new_cb, + chaining_param_free_cb, + chaining_param_dup_cb, + NULL, 0, enforcer) != 0) + { + LOG_ERROR("%s: register SERVICE_CHAINING_COMPILE plugin extension callbacks failed", LOG_TAG_POLICY); + return -1; + } + + if (Maat_plugin_EX_register(enforcer->maat, enforcer->sff_table_id, + sff_param_new_cb, + sff_param_free_cb, + sff_param_dup_cb, + NULL, 0, enforcer) != 0) + { + LOG_ERROR("%s: register SERVICE_FUNCTION_FORWARDER_PROFILE plugin extension callbacks failed", LOG_TAG_POLICY); + return -1; + } + + if (Maat_plugin_EX_register(enforcer->maat, enforcer->sf_table_id, + sf_param_new_cb, + sf_param_free_cb, + sf_param_dup_cb, + NULL, 0, enforcer) != 0) + { + LOG_ERROR("%s: register SERVICE_FUNCTION_PROFILE plugin extension callbacks failed", LOG_TAG_POLICY); + return -1; + } + + return 0; +} + +// return NULL : error +// return !NULL : success +struct selected_chaining *selected_chaining_create(int chaining_size) +{ + struct selected_chaining *chaining = (struct selected_chaining *)calloc(1, sizeof(struct selected_chaining)); + assert(chaining); + chaining->policy_id = -1; + chaining->traffic_type = TRAFFIC_TYPE_NONE; + chaining->chaining_index = 0; + chaining->chaining_size = chaining_size; + chaining->chaining = (struct selected_sf *)calloc(chaining->chaining_size, sizeof(struct selected_sf)); + assert(chaining->chaining); + + for (int i = 0; i < chaining->chaining_size; i++) + { + struct selected_sf *elem = &(chaining->chaining[i]); + elem->sff_profile_id = -1; + elem->sff_forward_type = FORWARD_TYPE_NONE; + elem->sf_profile_id = -1; + elem->sf_action = SESSION_ACTION_BYPASS; + elem->sf_action_reason = ACTION_BYPASS_DUE_DEFAULT; + } + + return chaining; +} + +void selected_chaining_destory(struct selected_chaining *chaining) +{ + if (chaining) + { + if (chaining->chaining) + { + free(chaining->chaining); + chaining->chaining = NULL; + } + free(chaining); + chaining = NULL; + } +} + +void selected_chaining_dump(struct selected_chaining *chaining) +{ + if (chaining == NULL) + { + LOG_DEBUG("%s: selected_chaining: NULL", LOG_TAG_POLICY); + return; + } + + LOG_DEBUG("%s: selected_chaining->policy_id : %d", LOG_TAG_POLICY, chaining->policy_id); + LOG_DEBUG("%s: selected_chaining->traffic_type : %s", LOG_TAG_POLICY, traffic_type_to_string(chaining->traffic_type)); + LOG_DEBUG("%s: selected_chaining->chaining_size : %d", LOG_TAG_POLICY, chaining->chaining_size); + + for (int i = 0; i < chaining->chaining_size; i++) + { + struct selected_sf *node = &(chaining->chaining[i]); + // sff + LOG_DEBUG("%s: selected_chaining->node[%d]->sff_profile_id : %d", LOG_TAG_POLICY, i, node->sff_profile_id); + LOG_DEBUG("%s: selected_chaining->node[%d]->sff_forward_type : %s", LOG_TAG_POLICY, i, forward_type_to_string(node->sff_forward_type)); + // sf + LOG_DEBUG("%s: selected_chaining->node[%d]->sf_profile_id : %d", LOG_TAG_POLICY, i, node->sf_profile_id); + LOG_DEBUG("%s: selected_chaining->node[%d]->sf_action : %s", LOG_TAG_POLICY, i, session_action_to_string(node->sf_action)); + LOG_DEBUG("%s: selected_chaining->node[%d]->sf_action_reason : %s", LOG_TAG_POLICY, i, session_action_reason_to_string(node->sf_action_reason)); + LOG_DEBUG("%s: selected_chaining->node[%d]->sf_connectivity->package_method : %s", LOG_TAG_POLICY, i, package_method_to_string(node->sf_connectivity.method)); + LOG_DEBUG("%s: selected_chaining->node[%d]->sf_connectivity->int_vlan_tag : %d", LOG_TAG_POLICY, i, node->sf_connectivity.int_vlan_tag); + LOG_DEBUG("%s: selected_chaining->node[%d]->sf_connectivity->ext_vlan_tag : %d", LOG_TAG_POLICY, i, node->sf_connectivity.ext_vlan_tag); + LOG_DEBUG("%s: selected_chaining->node[%d]->sf_connectivity->dest_ip : %s", LOG_TAG_POLICY, i, node->sf_connectivity.dest_ip); + } +} + +void selected_chaining_bref(struct selected_chaining *chaining) +{ + if (chaining == NULL) + { + return; + } + + char buff[4096] = {0}; + int buff_used = 0; + int buff_size = sizeof(buff); + buff_used += snprintf(buff + buff_used, buff_size - buff_used, "policy_id:%d, chaining_size:%d, ", chaining->policy_id, chaining->chaining_size); + for (int i = 0; i < chaining->chaining_size; i++) + { + struct selected_sf *node = &(chaining->chaining[i]); + if (buff_size - buff_used > 0) + { + buff_used += snprintf(buff + buff_used, buff_size - buff_used, "node[%d]={%s:%d:%d} ", i, session_action_reason_to_string(node->sf_action_reason), node->sff_profile_id, node->sf_profile_id); + } + } + LOG_DEBUG("%s: selected_chaining_bref: %s", LOG_TAG_POLICY, buff); +} + +// return NULL: NEED BYPASS ALL SFF +// return !NULL: +struct selected_chaining *policy_enforce_select_chaining(struct policy_enforcer *enforcer, struct raw_pkt_parser *parser, int policy_id, int dir_is_internal) +{ + uint64_t hash_value = 0; + char buffer[16] = {0}; + struct sf_param *sf_param = NULL; + struct sff_param *sff_param = NULL; + struct fixed_num_array array = {0}; + struct selected_chaining *chaining = NULL; + struct chaining_param *chaining_param = NULL; + + snprintf(buffer, sizeof(buffer), "%d", policy_id); + chaining_param = (struct chaining_param *)Maat_plugin_get_EX_data(enforcer->maat, enforcer->compile_table_id, buffer); + if (chaining_param == NULL) + { + LOG_ERROR("%s: failed to get chaining parameter of policy %d, bypass !!!", LOG_TAG_POLICY, policy_id); + // BYPASS ALL SFF + return NULL; + } + LOG_DEBUG("%s: enforce chaining policy %d", LOG_TAG_POLICY, policy_id); + + chaining = selected_chaining_create(chaining_param->sff_profile_ids_num); + assert(chaining); + chaining->policy_id = policy_id; + chaining->traffic_type = chaining_param->traffic_type; + + for (int i = 0; i < chaining_param->sff_profile_ids_num; i++) + { + struct selected_sf *item = &(chaining->chaining[chaining->chaining_index]); + item->sff_profile_id = chaining_param->sff_profile_ids[i]; + + memset(buffer, 0, sizeof(buffer)); + snprintf(buffer, sizeof(buffer), "%u", item->sff_profile_id); + sff_param = (struct sff_param *)Maat_plugin_get_EX_data(enforcer->maat, enforcer->sff_table_id, buffer); + if (sff_param == NULL) + { + LOG_ERROR("%s: failed to get sff parameter of profile %d, bypass current sff !!!", LOG_TAG_POLICY, item->sff_profile_id); + item->sf_action = SESSION_ACTION_BYPASS; + item->sf_action_reason = ACTION_BYPASS_DUE_INVALID_POLICY; + chaining->chaining_index++; + continue; + } + item->sff_forward_type = sff_param->sff_forward_type; + + memset(&array, 0, sizeof(array)); + fixed_num_array_init(&array); + select_sf_by_nearby_and_active(enforcer, sff_param, &array); + if (fixed_num_array_count_elem(&array) == 0) + { + LOG_DEBUG("%s: chaining policy %d -> sff_profile %d, no sf available after filtering by 'nearby & active', bypass current sff !!!", LOG_TAG_POLICY, policy_id, item->sff_profile_id); + item->sf_action = SESSION_ACTION_BYPASS; + item->sf_action_reason = ACTION_BYPASS_DUE_NO_AVAILABLE_SF; + chaining->chaining_index++; + sff_param_free(sff_param); + continue; + } + + hash_value = raw_packet_parser_get_hash_value(parser, sff_param->sff_ldbc.method, dir_is_internal); + item->sf_action = select_sf_by_ldbc(hash_value, sff_param, &array, &(item->sf_profile_id), &(item->sf_action_reason)); + if (item->sf_action != SESSION_ACTION_FORWARD) + { + chaining->chaining_index++; + sff_param_free(sff_param); + continue; + } + + memset(&buffer, 0, sizeof(buffer)); + snprintf(buffer, sizeof(buffer), "%u", item->sf_profile_id); + sf_param = (struct sf_param *)Maat_plugin_get_EX_data(enforcer->maat, enforcer->sf_table_id, buffer); + if (sf_param == NULL) + { + LOG_ERROR("%s: failed to get sf parameter of selected profile %d, bypass current sff !!!", LOG_TAG_POLICY, item->sf_profile_id); + item->sf_action = SESSION_ACTION_BYPASS; + item->sf_action_reason = ACTION_BYPASS_DUE_INVALID_POLICY; + chaining->chaining_index++; + sff_param_free(sff_param); + continue; + } + + item->sf_connectivity.method = sf_param->sf_connectivity.method; + item->sf_connectivity.int_vlan_tag = sf_param->sf_connectivity.int_vlan_tag; + item->sf_connectivity.ext_vlan_tag = sf_param->sf_connectivity.ext_vlan_tag; + memcpy(item->sf_connectivity.dest_ip, sf_param->sf_connectivity.dest_ip, strlen(sf_param->sf_connectivity.dest_ip)); + chaining->chaining_index++; + + sf_param_free(sf_param); + sff_param_free(sff_param); + } + + chaining_param_free(chaining_param); + + return chaining; +} diff --git a/platform/test/CMakeLists.txt b/platform/test/CMakeLists.txt new file mode 100644 index 0000000..7cdd705 --- /dev/null +++ b/platform/test/CMakeLists.txt @@ -0,0 +1,17 @@ +############################################################################### +# gtest_policy +############################################################################### + +add_executable(gtest_policy gtest_policy.cpp) +target_include_directories(gtest_policy PUBLIC ${CMAKE_SOURCE_DIR}/common/include) +target_include_directories(gtest_policy PUBLIC ${CMAKE_SOURCE_DIR}/platform/include) +target_link_libraries(gtest_policy common platform gtest) + +############################################################################### +# gtest_discover_tests +############################################################################### + +include(GoogleTest) +gtest_discover_tests(gtest_policy) + +file(COPY ./test_resource/ DESTINATION ./test_resource/) \ No newline at end of file diff --git a/platform/test/gtest_policy.cpp b/platform/test/gtest_policy.cpp new file mode 100644 index 0000000..f71988c --- /dev/null +++ b/platform/test/gtest_policy.cpp @@ -0,0 +1,54 @@ +#include + +#include "policy.h" +#include "raw_packet.h" + +unsigned char data1[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xa4, 0xc6, 0x4f, 0x3b, 0xb3, 0x9a, 0x81, 0x00, 0x66, 0x58, 0x81, 0x00, 0x61, 0xf9, 0x08, 0x00, 0x45, 0xb8, 0x00, 0x94, + 0xe8, 0x58, 0x00, 0x00, 0xff, 0x04, 0x11, 0x48, 0x45, 0x43, 0x23, 0x92, 0x29, 0xca, 0x2e, 0x6e, 0x45, 0xb8, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0xfe, 0x11, + 0xde, 0x84, 0x0a, 0x0a, 0x64, 0x19, 0x0a, 0x0a, 0x65, 0x02, 0xf3, 0x9f, 0x42, 0x68, 0x00, 0x6c, 0x4b, 0x9a, 0x00, 0x02, 0x00, 0x00, 0x04, 0x73, 0x6c, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd}; + +TEST(POLICY, SELECTED_CHAINING_LIFE_CYCLE) +{ + struct selected_chaining *chaining = NULL; + + chaining = selected_chaining_create(128); + EXPECT_TRUE(chaining != nullptr); + + selected_chaining_destory(chaining); +} + +TEST(POLICY, POLICY_ENFORCER_LIFE_CYCLE) +{ + struct raw_pkt_parser *parser = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(parser != nullptr); + const void *payload = raw_packet_parser_parse(parser, (const void *)data1, sizeof(data1)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data1 == 70); + + const char *profile = "./test_resource/sce.conf"; + struct policy_enforcer *enforcer = policy_enforcer_create("SCE", profile, 8, NULL); + EXPECT_TRUE(enforcer != nullptr); + EXPECT_TRUE(policy_enforcer_register(enforcer) == 0); + + int policy_id = 2; + int dir_is_internal = 1; + struct selected_chaining *chaining = policy_enforce_select_chaining(enforcer, parser, policy_id, dir_is_internal); + EXPECT_TRUE(chaining != nullptr); + selected_chaining_dump(chaining); + selected_chaining_bref(chaining); + + selected_chaining_destory(chaining); + policy_enforcer_destory(enforcer); + raw_packet_parser_destory(parser); +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/platform/test/test_resource/sce.conf b/platform/test/test_resource/sce.conf new file mode 100644 index 0000000..607a4c1 --- /dev/null +++ b/platform/test/test_resource/sce.conf @@ -0,0 +1,21 @@ +[system] +nr_worker_threads=8 + +[maat] +# 0:json 1:redis 2:iris +input_mode=0 +stat_switch=1 +perf_switch=1 +scan_detail=0 +deferred_load=0 +effect_interval_ms=1000 +stat_file=./sce.fs2 +table_info=test_resource/table_info.conf +accept_path=/opt/tsg/etc/tsg_device_tag.json +inc_cfg_dir=test_resource/inc/ +ful_cfg_dir=test_resource/ful/ +json_cfg_file=test_resource/sce.json +foreign_cont_dir=test_resource/foreign_files +redis_db_idx=0 +redis_server=127.0.0.1 +redis_port_range=6379 \ No newline at end of file diff --git a/platform/test/test_resource/sce.json b/platform/test/test_resource/sce.json new file mode 100644 index 0000000..09731d5 --- /dev/null +++ b/platform/test/test_resource/sce.json @@ -0,0 +1,41 @@ +{ + "plugin_table": [ + { + "table_name": "SERVICE_FUNCTION_PROFILE", + "table_content": [ + "1\tdevice_group_a\t1\t{\"method\":\"vxlan_g\",\"dest_ip\":\"1.1.1.1\"}\t{\"method\":\"none\"}\t1", + "2\tdevice_group_a\t1\t{\"method\":\"vxlan_g\",\"dest_ip\":\"1.1.1.1\"}\t{\"method\":\"bfd\",\"address\":\"1.2.3.4\",\"port\":\"10000\",\"interval_ms\":100,\"retires\":5}\t1", + "3\tdevice_group_a\t1\t{\"method\":\"vxlan_g\",\"dest_ip\":\"1.1.1.1\"}\t{\"method\":\"in_band_bfd\",\"address\":\"1.2.3.4\",\"port\":\"10000\",\"interval_ms\":100,\"retires\":5}\t1", + "4\tdevice_group_a\t1\t{\"method\":\"vxlan_g\",\"dest_ip\":\"1.1.1.1\"}\t{\"method\":\"http\",\"url\":\"http://192.168.100.1:8080/health_check.index\",\"interval_ms\":100,\"retires\":5}\t1", + "5\tdevice_group_a\t1\t{\"method\":\"layer2_switch\",\"int_vlan_tag\":10,\"ext_vlan_tag\":5}\t{\"method\":\"none\"}\t1", + "6\tdevice_group_a\t1\t{\"method\":\"layer3_switch\",\"int_vlan_tag\":10,\"ext_vlan_tag\":5}\t{\"method\":\"none\"}\t1", + "7\tdevice_group_a\t0\t{\"method\":\"layer3_switch\",\"int_vlan_tag\":10,\"ext_vlan_tag\":5}\t{\"method\":\"none\"}\t1", + "8\tdevice_group_b\t0\t{\"method\":\"layer3_switch\",\"int_vlan_tag\":10,\"ext_vlan_tag\":5}\t{\"method\":\"none\"}\t1" + ] + }, + { + "table_name": "SERVICE_FUNCTION_FORWARDER_PROFILE", + "table_content": [ + "1\t1\thash-int-ip\tnearby\tbypass\tnull\t[1]\t1", + "2\t1\thash-int-ip\tnearby\tbypass\tnull\t[1,2,3,4,5,6,7,8]\t1", + "3\t1\thash-int-ip\tnearby\tblock\tnull\t[1]\t1", + "4\t1\thash-int-ip\tnearby\tre-dispatch\t{\"action\":\"bypass\",\"health_service_func_lt\":2}\t[1,2,3]\t1", + "5\t1\thash-int-ip\tnearby\tre-dispatch\t{\"action\":\"block\"}\t[1,2,3]\t1", + "6\t1\thash-int-ip\tglobal\tblock\tnull\t[1]\t1", + "7\t1\thash-ext-ip\tglobal\tblock\tnull\t[1]\t1", + "8\t1\thash-int-ip-and-ext-ip\tglobal\tblock\tnull\t[1]\t1", + "9\t1\thash-innermost-int-ip\tglobal\tblock\tnull\t[1]\t1", + "10\t2\thash-innermost-int-ip\tglobal\tblock\tnull\t[1]\t1" + ] + }, + { + "table_name": "SERVICE_CHAINING_COMPILE", + "table_content": [ + "1\t0\t2\t1\t1\t{}\t{\"targeted_traffic\":\"raw\",\"sff_profiles\":[1]}\t1\t2", + "2\t0\t2\t1\t1\t{}\t{\"targeted_traffic\":\"raw\",\"sff_profiles\":[1,2,3,4,5,6,7,8,9,10]}\t1\t2", + "11\t0\t2\t1\t1\t{}\t{\"targeted_traffic\":\"decrypted\",\"sff_profiles\":[1]}\t1\t2", + "12\t0\t2\t1\t1\t{}\t{\"targeted_traffic\":\"decrypted\",\"sff_profiles\":[1,2,3,4,5,6,7,8,9,10]}\t1\t2" + ] + } + ] +} \ No newline at end of file diff --git a/platform/test/test_resource/table_info.conf b/platform/test/test_resource/table_info.conf new file mode 100644 index 0000000..ed7798c --- /dev/null +++ b/platform/test/test_resource/table_info.conf @@ -0,0 +1,3 @@ +0 SERVICE_CHAINING_COMPILE plugin {"key":1,"valid":8} +1 SERVICE_FUNCTION_FORWARDER_PROFILE plugin {"key":1,"valid":8} +2 SERVICE_FUNCTION_PROFILE plugin {"key":1,"valid":6} \ No newline at end of file diff --git a/resource/sce.json b/resource/sce.json new file mode 100644 index 0000000..09731d5 --- /dev/null +++ b/resource/sce.json @@ -0,0 +1,41 @@ +{ + "plugin_table": [ + { + "table_name": "SERVICE_FUNCTION_PROFILE", + "table_content": [ + "1\tdevice_group_a\t1\t{\"method\":\"vxlan_g\",\"dest_ip\":\"1.1.1.1\"}\t{\"method\":\"none\"}\t1", + "2\tdevice_group_a\t1\t{\"method\":\"vxlan_g\",\"dest_ip\":\"1.1.1.1\"}\t{\"method\":\"bfd\",\"address\":\"1.2.3.4\",\"port\":\"10000\",\"interval_ms\":100,\"retires\":5}\t1", + "3\tdevice_group_a\t1\t{\"method\":\"vxlan_g\",\"dest_ip\":\"1.1.1.1\"}\t{\"method\":\"in_band_bfd\",\"address\":\"1.2.3.4\",\"port\":\"10000\",\"interval_ms\":100,\"retires\":5}\t1", + "4\tdevice_group_a\t1\t{\"method\":\"vxlan_g\",\"dest_ip\":\"1.1.1.1\"}\t{\"method\":\"http\",\"url\":\"http://192.168.100.1:8080/health_check.index\",\"interval_ms\":100,\"retires\":5}\t1", + "5\tdevice_group_a\t1\t{\"method\":\"layer2_switch\",\"int_vlan_tag\":10,\"ext_vlan_tag\":5}\t{\"method\":\"none\"}\t1", + "6\tdevice_group_a\t1\t{\"method\":\"layer3_switch\",\"int_vlan_tag\":10,\"ext_vlan_tag\":5}\t{\"method\":\"none\"}\t1", + "7\tdevice_group_a\t0\t{\"method\":\"layer3_switch\",\"int_vlan_tag\":10,\"ext_vlan_tag\":5}\t{\"method\":\"none\"}\t1", + "8\tdevice_group_b\t0\t{\"method\":\"layer3_switch\",\"int_vlan_tag\":10,\"ext_vlan_tag\":5}\t{\"method\":\"none\"}\t1" + ] + }, + { + "table_name": "SERVICE_FUNCTION_FORWARDER_PROFILE", + "table_content": [ + "1\t1\thash-int-ip\tnearby\tbypass\tnull\t[1]\t1", + "2\t1\thash-int-ip\tnearby\tbypass\tnull\t[1,2,3,4,5,6,7,8]\t1", + "3\t1\thash-int-ip\tnearby\tblock\tnull\t[1]\t1", + "4\t1\thash-int-ip\tnearby\tre-dispatch\t{\"action\":\"bypass\",\"health_service_func_lt\":2}\t[1,2,3]\t1", + "5\t1\thash-int-ip\tnearby\tre-dispatch\t{\"action\":\"block\"}\t[1,2,3]\t1", + "6\t1\thash-int-ip\tglobal\tblock\tnull\t[1]\t1", + "7\t1\thash-ext-ip\tglobal\tblock\tnull\t[1]\t1", + "8\t1\thash-int-ip-and-ext-ip\tglobal\tblock\tnull\t[1]\t1", + "9\t1\thash-innermost-int-ip\tglobal\tblock\tnull\t[1]\t1", + "10\t2\thash-innermost-int-ip\tglobal\tblock\tnull\t[1]\t1" + ] + }, + { + "table_name": "SERVICE_CHAINING_COMPILE", + "table_content": [ + "1\t0\t2\t1\t1\t{}\t{\"targeted_traffic\":\"raw\",\"sff_profiles\":[1]}\t1\t2", + "2\t0\t2\t1\t1\t{}\t{\"targeted_traffic\":\"raw\",\"sff_profiles\":[1,2,3,4,5,6,7,8,9,10]}\t1\t2", + "11\t0\t2\t1\t1\t{}\t{\"targeted_traffic\":\"decrypted\",\"sff_profiles\":[1]}\t1\t2", + "12\t0\t2\t1\t1\t{}\t{\"targeted_traffic\":\"decrypted\",\"sff_profiles\":[1,2,3,4,5,6,7,8,9,10]}\t1\t2" + ] + } + ] +} \ No newline at end of file diff --git a/resource/table_info.conf b/resource/table_info.conf new file mode 100644 index 0000000..ed7798c --- /dev/null +++ b/resource/table_info.conf @@ -0,0 +1,3 @@ +0 SERVICE_CHAINING_COMPILE plugin {"key":1,"valid":8} +1 SERVICE_FUNCTION_FORWARDER_PROFILE plugin {"key":1,"valid":8} +2 SERVICE_FUNCTION_PROFILE plugin {"key":1,"valid":6} \ No newline at end of file