#include #include #include #include #include #include #include "global_metrics.h" #include "health_check.h" #include "packet.h" #include "policy.h" #include "utils.h" #include "log.h" #include "sce.h" #include "utarray.h" #define TABLE_NAME_SC "SERVICE_CHAINING_RULE" #define TABLE_NAME_SFF "SERVICE_FUNCTION_FORWARDER_PROFILE" #define TABLE_NAME_SF "SERVICE_FUNCTION_PROFILE" /****************************************************************************** * Struct policy_enforcer ******************************************************************************/ #define EFFECTIVE_RANGE_MAX_SIZE 128 enum input_mode { MAAT_INPUT_JSON = 0, MAAT_INPUT_REDIS = 1, }; struct policy_config { enum input_mode input_mode; int log_level; int stat_switch; int perf_switch; int scan_detail; int deferred_load; char data_center[EFFECTIVE_RANGE_MAX_SIZE]; char device_group[EFFECTIVE_RANGE_MAX_SIZE]; char stat_file[2048]; char table_info[2048]; char accept_tags[2048]; char accept_path[2048]; char json_cfg_file[2048]; char foreign_cont_dir[2048]; int redis_db_idx; char redis_server[2048]; char redis_port_range[2048]; int max_chaining_size; }; struct policy_enforcer { struct policy_config config; struct maat *maat; }; /****************************************************************************** * Struct chaining_param ******************************************************************************/ struct chaining_param { uuid_t rule_uuid; int ref_cnt; int vsys_id; enum traffic_type traffic_type; struct uuid_array sff_uuid_array; }; /****************************************************************************** * 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 { uuid_t sff_uuid; int sff_ref_cnt; enum forward_type sff_forward_type; struct load_balance sff_ldbc; struct exception sff_exception; struct uuid_array sf_uuid_array; }; /****************************************************************************** * Struct sf_param ******************************************************************************/ enum admin_status { ADMMIN_STATUS_ACTIVE = 1, ADMMIN_STATUS_INACTIVE = 2, }; enum effective_type { EFFECTIVE_TYPE_DEVICE_GROUP = 0x1, EFFECTIVE_TYPE_DATA_CENTER = 0x2, }; struct effective_range { enum effective_type type; char value[EFFECTIVE_RANGE_MAX_SIZE]; }; struct sf_param { int sf_vsys_id; uuid_t sf_uuid; int sf_ref_cnt; enum admin_status sf_admin_status; struct connectivity sf_connectivity; struct health_check sf_health_check; struct effective_range sf_effective_range; uint64_t health_check_session_id; }; /****************************************************************************** * Private API -- Utils ******************************************************************************/ static const char *effective_type_to_string(enum effective_type type) { switch (type) { case EFFECTIVE_TYPE_DEVICE_GROUP: return "device_group"; case EFFECTIVE_TYPE_DATA_CENTER: return "data_center"; default: return "unknown"; } } static const char *admin_status_to_string(enum admin_status admin_status) { switch (admin_status) { case ADMMIN_STATUS_ACTIVE: return "active"; case ADMMIN_STATUS_INACTIVE: return "inactive"; default: return "unknown"; } } // {"tags":[{"tag":"device_group","value":"group-xxg-9140"},{"tag":"data_center","value":"center-xxg-9140"}]} static void parser_effective_range(const char *accept_tags, char *data_center, char *device_group) { 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) { memset(device_group, 0, EFFECTIVE_RANGE_MAX_SIZE); memcpy(device_group, item_val->valuestring, MIN(strlen(item_val->valuestring), EFFECTIVE_RANGE_MAX_SIZE)); } if (strcasecmp(item_key->valuestring, "data_center") == 0) { memset(data_center, 0, EFFECTIVE_RANGE_MAX_SIZE); memcpy(data_center, item_val->valuestring, MIN(strlen(item_val->valuestring), EFFECTIVE_RANGE_MAX_SIZE)); } } 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); // LOG_LEVEL_TRACE = 0; LOG_LEVEL_DEBUG = 1; LOG_LEVEL_INFO = 2; // LOG_LEVEL_WARN = 3; LOG_LEVEL_ERROR = 4; LOG_LEVEL_FATAL = 5; MESA_load_profile_int_def(profile, "MAAT", "log_level", &(config->log_level), 5); 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_string_def(profile, "MAAT", "stat_file", config->stat_file, sizeof(config->stat_file), "log/maat.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", "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"); MESA_load_profile_int_def(profile, "MAAT", "max_chaining_size", &(config->max_chaining_size), 32); 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_effective_range(config->accept_tags, config->data_center, config->device_group); } LOG_DEBUG("%s: MAAT->input_mode : %s", LOG_TAG_POLICY, (config->input_mode == MAAT_INPUT_REDIS ? "redis" : "json")); LOG_DEBUG("%s: MAAT->log_level : %d", LOG_TAG_POLICY, config->log_level); 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->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->data_center : %s", LOG_TAG_POLICY, config->data_center); 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); LOG_DEBUG("%s: MAAT->max_chaining_size : %d", LOG_TAG_POLICY, config->max_chaining_size); } /****************************************************************************** * Private API -- MAAT Callback ******************************************************************************/ static void chaining_param_new_cb(const char *table_name, const char *key, const char *table_line, void **ad, long argl, void *argp) { int iter = 0; cJSON *json = NULL; cJSON *object = NULL; cJSON *array = NULL; cJSON *item = NULL; uuid_t sff_uuid; struct chaining_param *param = NULL; char *json_str = strdup(table_line); json = cJSON_Parse(json_str); if (json == NULL) { LOG_ERROR("%s: unexpected chaining rule: (invalid json format) %s", LOG_TAG_POLICY, table_line); goto error_out; } param = (struct chaining_param *)calloc(1, sizeof(struct chaining_param)); uuid_parse(key, param->rule_uuid); uuid_array_init(¶m->sff_uuid_array); param->ref_cnt = 1; // action_parameter object = cJSON_GetObjectItem(json, "action_parameter"); if (!object || !cJSON_IsObject(object)) { LOG_ERROR("%s: unexpected chaining rule: (invalid action_parameter param) %s", LOG_TAG_POLICY, table_line); goto error_out; } // vsys_id item = cJSON_GetObjectItem(object, "vsys_id"); if (!item || !cJSON_IsNumber(item)) { LOG_ERROR("%s: unexpected chaining rule: (invalid vsys_id param) %s", LOG_TAG_POLICY, table_line); goto error_out; } param->vsys_id = item->valueint; LOG_DEBUG("%s: parse chaining rule: %s, vsys_id: %d", LOG_TAG_POLICY, key, param->vsys_id); // targeted_traffic item = cJSON_GetObjectItem(object, "targeted_traffic"); if (!item || !cJSON_IsString(item)) { LOG_ERROR("%s: unexpected chaining rule: (invalid targeted_traffic param) %s", LOG_TAG_POLICY, table_line); goto error_out; } if (strcasecmp(item->valuestring, "raw") == 0) { param->traffic_type = TRAFFIC_TYPE_RAW; } else if (strcasecmp(item->valuestring, "decrypted") == 0) { param->traffic_type = TRAFFIC_TYPE_DECRYPTED; } else { LOG_ERROR("%s: unexpected chaining rule: (invalid targeted_traffic param) %s", LOG_TAG_POLICY, table_line); goto error_out; } LOG_DEBUG("%s: parse chaining rule: %s, targeted_traffic: %s", LOG_TAG_POLICY, key, traffic_type_tostring(param->traffic_type)); // sff_profiles array = cJSON_GetObjectItem(object, "sff_profiles"); if (!array || !cJSON_IsArray(array) || !cJSON_GetArraySize(array)) { LOG_ERROR("%s: unexpected chaining rule: (invalid sff_profiles param) %s", LOG_TAG_POLICY, table_line); goto error_out; } cJSON_ArrayForEach(item, array) { if (!cJSON_IsString(item)) { LOG_ERROR("%s: unexpected chaining rule: (invalid sff_profiles param) %s", LOG_TAG_POLICY, table_line); continue; } if (uuid_array_is_full(¶m->sff_uuid_array)) { LOG_ERROR("%s: unexpected chaining rule: (sff_profiles is full) %s", LOG_TAG_POLICY, table_line); break; } LOG_DEBUG("%s: parse chaining rule: %s, sff_profiles[%d]: %s", LOG_TAG_POLICY, key, iter, item->valuestring); uuid_parse(item->valuestring, sff_uuid); uuid_array_append(¶m->sff_uuid_array, sff_uuid); iter++; } *ad = param; LOG_INFO("%s: Add chaining rule: %s", LOG_TAG_POLICY, key); 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) { free(param); param = NULL; } } static void chaining_param_free_cb(const char *table_name, void **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)) { char rule_uuid_str[UUID_STRING_SIZE] = {0}; uuid_unparse(param->rule_uuid, rule_uuid_str); LOG_INFO("%s: Del chaining rule: %s", LOG_TAG_POLICY, rule_uuid_str); free(param); param = NULL; *ad = NULL; } } static void chaining_param_dup_cb(const char *table_name, void **to, void **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(NULL, (void **)¶m, 0, NULL); } static void sff_param_new_cb(const char *table_name, const char *key, const char *table_line, void **ad, long argl, void *argp) { int iter = 0; cJSON *json = NULL; cJSON *object = NULL; cJSON *array = NULL; cJSON *item = NULL; uuid_t sf_uuid; struct sff_param *param = NULL; char *json_str = strdup(table_line); json = cJSON_Parse(json_str); if (json == NULL) { LOG_ERROR("%s: unexpected sff profile: (invalid json format) %s", LOG_TAG_POLICY, table_line); goto error_out; } param = (struct sff_param *)calloc(1, sizeof(struct sff_param)); uuid_parse(key, param->sff_uuid); uuid_array_init(¶m->sf_uuid_array); param->sff_ref_cnt = 1; // type item = cJSON_GetObjectItem(json, "type"); if (!item || !cJSON_IsNumber(item)) { LOG_ERROR("%s: unexpected sff profile: (invalid type param) %s", LOG_TAG_POLICY, table_line); goto error_out; } if (item->valueint == 1) { param->sff_forward_type = FORWARD_TYPE_STEERING; } else if (item->valueint == 2) { param->sff_forward_type = FORWARD_TYPE_MIRRORING; } else { LOG_ERROR("%s: unexpected sff profile: (invalid type param) %s", LOG_TAG_POLICY, table_line); goto error_out; } LOG_DEBUG("%s: parse sff profile: %s, type: %s", LOG_TAG_POLICY, key, forward_type_tostring(param->sff_forward_type)); // load_balance_method item = cJSON_GetObjectItem(json, "load_balance_method"); if (!item || !cJSON_IsString(item)) { LOG_ERROR("%s: unexpected sff profile: (invalid load_balance_method param) %s", LOG_TAG_POLICY, table_line); goto error_out; } if (0 == strcasecmp(item->valuestring, "hash-int-ip")) { param->sff_ldbc.method = LDBC_METHOD_HASH_INT_IP; } else if (0 == strcasecmp(item->valuestring, "hash-ext-ip")) { param->sff_ldbc.method = LDBC_METHOD_HASH_EXT_IP; } else if (0 == strcasecmp(item->valuestring, "hash-int-ip-and-ext-ip")) { param->sff_ldbc.method = LDBC_METHOD_HASH_INT_IP_AND_EXT_IP; } else if (0 == strcasecmp(item->valuestring, "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: %s, load_balance_method: %s", LOG_TAG_POLICY, key, item->valuestring); // load_balance_localization item = cJSON_GetObjectItem(json, "load_balance_localization"); if (!item || !cJSON_IsString(item)) { LOG_ERROR("%s: unexpected sff profile: (invalid load_balance_localization param) %s", LOG_TAG_POLICY, table_line); goto error_out; } if (0 == strcasecmp(item->valuestring, "nearby")) { param->sff_ldbc.localiza = LDBC_LOCALIZATION_NEARBY; } else if (0 == strcasecmp(item->valuestring, "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: %s, load_balance_localization: %s", LOG_TAG_POLICY, key, item->valuestring); // failure_action item = cJSON_GetObjectItem(json, "failure_action"); if (!item || !cJSON_IsString(item)) { LOG_ERROR("%s: unexpected sff profile: (invalid failure_action param) %s", LOG_TAG_POLICY, table_line); goto error_out; } if (0 == strcasecmp(item->valuestring, "bypass")) { param->sff_exception.fail_action = FAILURE_ACTION_BYPASS; } else if (0 == strcasecmp(item->valuestring, "block")) { param->sff_exception.fail_action = FAILURE_ACTION_BLOCK; } else if (0 == strcasecmp(item->valuestring, "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: %s, failure_action: %s", LOG_TAG_POLICY, key, item->valuestring); // unavailability_action if (param->sff_exception.fail_action == FAILURE_ACTION_RE_DISPATCH) { object = cJSON_GetObjectItem(json, "unavailability_action"); if (!object || !cJSON_IsObject(object)) { LOG_ERROR("%s: unexpected sff profile: (invalid unavailability_action param) %s", LOG_TAG_POLICY, table_line); goto error_out; } item = cJSON_GetObjectItem(object, "action"); if (!item || !cJSON_IsString(item)) { LOG_ERROR("%s: unexpected chaining rule: (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 rule: (invalid unavailability_action->action param) %s", LOG_TAG_POLICY, table_line); goto error_out; } LOG_DEBUG("%s: parse sff profile: %s, unavailability_action->action: %s", LOG_TAG_POLICY, key, item->valuestring); item = cJSON_GetObjectItem(object, "health_service_func_lt"); if (item && cJSON_IsNumber(item)) { param->sff_exception.health_service_func_lt = item->valueint; LOG_DEBUG("%s: parse sff profile: %s, unavailability_action->health_service_func_lt: %d", LOG_TAG_POLICY, key, item->valueint); } } // service_func_profiles array = cJSON_GetObjectItem(json, "service_func_profiles"); if (array == NULL || !cJSON_IsArray(array) || !cJSON_GetArraySize(array)) { LOG_ERROR("%s: unexpected sff profile: (invalid service_func_profiles param) %s", LOG_TAG_POLICY, table_line); goto error_out; } cJSON_ArrayForEach(item, array) { if (!cJSON_IsString(item)) { LOG_ERROR("%s: unexpected sff profile: (invalid service_func_profiles param) %s", LOG_TAG_POLICY, table_line); continue; } if (uuid_array_is_full(¶m->sf_uuid_array)) { LOG_ERROR("%s: unexpected sff profile: (service_func_profiles is full) %s", LOG_TAG_POLICY, table_line); break; } LOG_DEBUG("%s: parse sff profile: %s, service_func_profiles[%d] = %s", LOG_TAG_POLICY, key, iter, item->valuestring); uuid_parse(item->valuestring, sf_uuid); uuid_array_append(¶m->sf_uuid_array, sf_uuid); iter++; } *ad = param; LOG_INFO("%s: Add sff profile: %s", LOG_TAG_POLICY, key); 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) { free(param); param = NULL; } } static void sff_param_free_cb(const char *table_name, void **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)) { char sff_uuid_str[UUID_STRING_SIZE] = {0}; uuid_unparse(param->sff_uuid, sff_uuid_str); LOG_INFO("%s: Del sff profile: %s", LOG_TAG_POLICY, sff_uuid_str); free(param); param = NULL; *ad = NULL; } } static void sff_param_dup_cb(const char *table_name, void **to, void **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(NULL, (void **)¶m, 0, NULL); } static void sf_param_new_cb(const char *table_name, const char *key, const char *table_line, void **ad, long argl, void *argp) { cJSON *json = NULL; cJSON *object = NULL; cJSON *item = NULL; struct sf_param *param = NULL; char *json_str = strdup(table_line); json = cJSON_Parse(json_str); if (json == NULL) { LOG_ERROR("%s: unexpected sf profile: (invalid json format) %s", LOG_TAG_POLICY, table_line); goto error_out; } param = (struct sf_param *)calloc(1, sizeof(struct sf_param)); uuid_parse(key, param->sf_uuid); param->sf_ref_cnt = 1; // vsys_id item = cJSON_GetObjectItem(json, "vsys_id"); if (!item || !cJSON_IsNumber(item)) { LOG_ERROR("%s: unexpected sf profile: (invalid vsys_id param) %s", LOG_TAG_POLICY, table_line); goto error_out; } param->sf_vsys_id = item->valueint; // device_group object = cJSON_GetObjectItem(json, "device_group"); if (!object || !cJSON_IsObject(object)) { LOG_ERROR("%s: unexpected sf profile: (invalid device_group param) %s", LOG_TAG_POLICY, table_line); goto error_out; } item = cJSON_GetObjectItem(object, "tag"); if (!item || !cJSON_IsString(item)) { LOG_ERROR("%s: unexpected sf profile: (invalid device_group->tag param) %s", LOG_TAG_POLICY, table_line); goto error_out; } if (0 == strcasecmp(item->valuestring, "device_group")) { param->sf_effective_range.type = EFFECTIVE_TYPE_DEVICE_GROUP; } else if (0 == strcasecmp(item->valuestring, "data_center")) { param->sf_effective_range.type = EFFECTIVE_TYPE_DATA_CENTER; } else { LOG_ERROR("%s: unexpected sf profile: (invalid device_group->tag param) %s", LOG_TAG_POLICY, table_line); goto error_out; } item = cJSON_GetObjectItem(object, "value"); if (!item || !cJSON_IsString(item)) { LOG_ERROR("%s: unexpected sf profile: (invalid device_group->value param) %s", LOG_TAG_POLICY, table_line); goto error_out; } memcpy(param->sf_effective_range.value, item->valuestring, MIN(strlen(item->valuestring), EFFECTIVE_RANGE_MAX_SIZE)); LOG_DEBUG("%s: parse sf profile: %s, device_group->tag: %s, device_group->value: %s", LOG_TAG_POLICY, key, effective_type_to_string(param->sf_effective_range.type), param->sf_effective_range.value); // admin_status item = cJSON_GetObjectItem(json, "admin_status"); if (!item || !cJSON_IsNumber(item)) { LOG_ERROR("%s: unexpected sf profile: (invalid admin_status param) %s", LOG_TAG_POLICY, table_line); goto error_out; } if (item->valueint == 1) { param->sf_admin_status = ADMMIN_STATUS_ACTIVE; } else if (item->valueint == 0) { param->sf_admin_status = ADMMIN_STATUS_INACTIVE; } else { LOG_ERROR("%s: unexpected sf profile: (invalid admin_status param) %s", LOG_TAG_POLICY, table_line); goto error_out; } LOG_DEBUG("%s: parse sf profile: %s, admin_status: %s", LOG_TAG_POLICY, key, admin_status_to_string(param->sf_admin_status)); // connectivity object = cJSON_GetObjectItem(json, "connectivity"); if (!object || !cJSON_IsObject(object)) { LOG_ERROR("%s: unexpected sf profile: (invalid connectivity param) %s", LOG_TAG_POLICY, table_line); goto error_out; } item = cJSON_GetObjectItem(object, "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 = ENCAPSULATE_METHOD_LAYER2_SWITCH; } else if (0 == strcasecmp(item->valuestring, "layer3_switch")) { param->sf_connectivity.method = ENCAPSULATE_METHOD_LAYER3_SWITCH; } else if (0 == strcasecmp(item->valuestring, "vxlan_g")) { param->sf_connectivity.method = ENCAPSULATE_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: %s, connectivity->method: %s", LOG_TAG_POLICY, key, encapsulate_method_tostring(param->sf_connectivity.method)); if (param->sf_connectivity.method == ENCAPSULATE_METHOD_LAYER2_SWITCH || param->sf_connectivity.method == ENCAPSULATE_METHOD_LAYER3_SWITCH) { item = cJSON_GetObjectItem(object, "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: %s, connectivity->int_vlan_tag: %d", LOG_TAG_POLICY, key, item->valueint); item = cJSON_GetObjectItem(object, "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: %s, connectivity->ext_vlan_tag: %d", LOG_TAG_POLICY, key, item->valueint); } else if (param->sf_connectivity.method == ENCAPSULATE_METHOD_VXLAN_G) { item = cJSON_GetObjectItem(object, "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: %s, connectivity->dest_ip: %s", LOG_TAG_POLICY, key, item->valuestring); } // health_check object = cJSON_GetObjectItem(json, "health_check"); if (!object || !cJSON_IsObject(object)) { LOG_ERROR("%s: unexpected sf profile: (invalid health_check param) %s", LOG_TAG_POLICY, table_line); goto error_out; } item = cJSON_GetObjectItem(object, "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: %s, health_check->method: %s", LOG_TAG_POLICY, key, item->valuestring); if ((param->sf_health_check.method == HEALTH_CHECK_METHOD_BFD && param->sf_connectivity.method == ENCAPSULATE_METHOD_VXLAN_G) || (param->sf_health_check.method == HEALTH_CHECK_METHOD_NONE && param->sf_connectivity.method == ENCAPSULATE_METHOD_VXLAN_G)) { memcpy(param->sf_health_check.address, param->sf_connectivity.dest_ip, strlen(param->sf_connectivity.dest_ip)); } if (param->sf_health_check.method == HEALTH_CHECK_METHOD_HTTP) { item = cJSON_GetObjectItem(object, "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: %s, health_check->url: %s", LOG_TAG_POLICY, key, 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(object, "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: %s, health_check->interval_ms: %d", LOG_TAG_POLICY, key, item->valueint); item = cJSON_GetObjectItem(object, "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: %s, health_check->retires: %d", LOG_TAG_POLICY, key, item->valueint); } if (param->sf_connectivity.method != ENCAPSULATE_METHOD_LAYER2_SWITCH) { param->health_check_session_id = health_check_session_add(¶m->sf_uuid, param->sf_vsys_id, ¶m->sf_health_check); } *ad = param; LOG_INFO("%s: Add sf profile: %s", LOG_TAG_POLICY, key); 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) { free(param); param = NULL; } } static void sf_param_free_cb(const char *table_name, void **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)) { if (param->sf_connectivity.method != ENCAPSULATE_METHOD_LAYER2_SWITCH) { health_check_session_del(param->health_check_session_id, ¶m->sf_uuid, param->sf_vsys_id); } char sf_uuid_str[UUID_STRING_SIZE] = {0}; uuid_unparse(param->sf_uuid, sf_uuid_str); LOG_INFO("%s: Del sf profile: %s", LOG_TAG_POLICY, sf_uuid_str); free(param); param = NULL; *ad = NULL; } } static void sf_param_dup_cb(const char *table_name, void **to, void **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(NULL, (void **)¶m, 0, NULL); } /****************************************************************************** * Private API -- Selected SF ******************************************************************************/ static void selected_sf_init(struct selected_sf *selected_sf) { if (selected_sf) { memset(selected_sf, 0, sizeof(struct selected_sf)); selected_sf->rule_vsys_id = 0; uuid_clear(selected_sf->rule_uuid); selected_sf->traffic_type = TRAFFIC_TYPE_NONE; uuid_clear(selected_sf->sff_uuid); selected_sf->sff_forward_type = FORWARD_TYPE_NONE; uuid_clear(selected_sf->sf_uuid); selected_sf->sf_action = SESSION_ACTION_BYPASS; selected_sf->sf_action_desc = ACTION_BYPASS_DUE_DEFAULT; } } static void selected_sf_set_info(struct selected_sf *selected_sf, struct sf_param *sf_param) { selected_sf->sf_vsys_id = sf_param->sf_vsys_id; uuid_copy(selected_sf->sf_uuid, sf_param->sf_uuid); selected_sf->sf_connectivity = sf_param->sf_connectivity; if (selected_sf->sf_connectivity.method == ENCAPSULATE_METHOD_VXLAN_G) { selected_sf->sf_dst_ip = inet_addr(selected_sf->sf_connectivity.dest_ip); } } static void selected_sf_set_action(struct selected_sf *selected_sf, enum action_desc action_desc) { selected_sf->sf_action_desc = action_desc; switch (action_desc) { case ACTION_BYPASS_DUE_DEFAULT: case ACTION_BYPASS_DUE_INVALID_POLICY: case ACTION_BYPASS_DUE_FAILURE_ACTION: case ACTION_BYPASS_DUE_UNAVAILABLE_ACTION: case ACTION_BYPASS_DUE_HEALTH_SF_LIMIT: selected_sf->sf_action = SESSION_ACTION_BYPASS; break; case ACTION_BLOCK_DUE_FAILURE_ACTION: case ACTION_BLOCK_DUE_UNAVAILABLE_ACTION: selected_sf->sf_action = SESSION_ACTION_BLOCK; break; case ACTION_FORWAED_DUE_SELECTED_SF: selected_sf->sf_action = SESSION_ACTION_FORWARD; break; } } // return 1 : current sf can be selected // return 0 : current sf can't be selected static int select_sf_by_admin_status(struct sf_param *sf) { if (sf->sf_admin_status == ADMMIN_STATUS_ACTIVE) { return 1; } else { return 0; } } // return 1 : current sf can be selected // return 0 : current sf can't be selected static int select_sf_by_device_group(struct policy_enforcer *enforcer, struct sf_param *sf) { if (strcasecmp(enforcer->config.device_group, sf->sf_effective_range.value) == 0) { return 1; } else { return 0; } } // return 1 : current sf can be selected // return 0 : current sf can't be selected static int select_sf_by_data_center(struct policy_enforcer *enforcer, struct sf_param *sf) { if (strcasecmp(enforcer->config.data_center, sf->sf_effective_range.value) == 0) { return 1; } else { return 0; } } // return 1 : current sf can be selected // return 0 : current sf can't be selected static int select_sf_by_localization(struct policy_enforcer *enforcer, struct sff_param *sff_param, struct sf_param *sf) { if (sff_param->sff_ldbc.localiza == LDBC_LOCALIZATION_NEARBY) { if (sf->sf_effective_range.type == EFFECTIVE_TYPE_DEVICE_GROUP) { return select_sf_by_device_group(enforcer, sf); } else { return select_sf_by_data_center(enforcer, sf); } } else { return 1; } } // return 1 : current sf can be selected // return 0 : current sf can't be selected static int handle_fail_action(struct exception *sff_exception, struct selected_sf *selected_sf, int sf_num) { if (sff_exception->fail_action == FAILURE_ACTION_RE_DISPATCH) { if (sff_exception->health_service_func_lt > 0 && sf_num < sff_exception->health_service_func_lt) { selected_sf_set_action(selected_sf, ACTION_BYPASS_DUE_HEALTH_SF_LIMIT); return 1; } else { if (sf_num == 0) { if (sff_exception->unavail_action == UNAVAILABLE_ACTION_BYPASSS) { selected_sf_set_action(selected_sf, ACTION_BYPASS_DUE_UNAVAILABLE_ACTION); return 1; } else { selected_sf_set_action(selected_sf, ACTION_BLOCK_DUE_UNAVAILABLE_ACTION); return 1; } } else { return 0; } } } else if (sff_exception->fail_action == FAILURE_ACTION_BYPASS) { selected_sf_set_action(selected_sf, ACTION_BYPASS_DUE_FAILURE_ACTION); return 1; } else if (sff_exception->fail_action == FAILURE_ACTION_BLOCK) { selected_sf_set_action(selected_sf, ACTION_BLOCK_DUE_FAILURE_ACTION); return 1; } else { return 0; } } static void select_sf_by_ldbc(struct sff_param *sff_param, struct selected_sf *selected_sf, struct session_ctx *s_ctx, UT_array *sf_array, uint64_t hash) { char sf_uuid_str[UUID_STRING_SIZE] = {0}; struct thread_metrics *thread_metrics = &s_ctx->ref_thread_ctx->thread_metrics; while (utarray_len(sf_array)) { unsigned int sf_index = (unsigned int)(hash % utarray_len(sf_array)); struct sf_param *sf_param = (struct sf_param *)utarray_eltptr(sf_array, sf_index); uuid_unparse(sf_param->sf_uuid, sf_uuid_str); if (sf_param->sf_connectivity.method == ENCAPSULATE_METHOD_LAYER2_SWITCH) { LOG_INFO("%s: session %lu %s select sf by ldbc, sf_uuid %s to be selected", LOG_TAG_POLICY, s_ctx->session_id, s_ctx->session_addr, sf_uuid_str); selected_sf_set_action(selected_sf, ACTION_FORWAED_DUE_SELECTED_SF); selected_sf_set_info(selected_sf, sf_param); return; } memset(selected_sf->sf_dst_mac, 0, sizeof(selected_sf->sf_dst_mac)); if (health_check_session_get_mac(sf_param->health_check_session_id, selected_sf->sf_dst_mac) == 0) { ATOMIC_INC(&(thread_metrics->sf_active)); LOG_INFO("%s: session %lu %s select sf by ldbc, sf_uuid %s to be selected", LOG_TAG_POLICY, s_ctx->session_id, s_ctx->session_addr, sf_uuid_str); selected_sf_set_action(selected_sf, ACTION_FORWAED_DUE_SELECTED_SF); selected_sf_set_info(selected_sf, sf_param); return; } else { ATOMIC_INC(&(thread_metrics->sf_inactive)); if (handle_fail_action(&sff_param->sff_exception, selected_sf, utarray_len(sf_array) - 1) == 0) { LOG_INFO("%s: session %lu %s select sf by re-dispatch, sf_uuid %s to be excluded", LOG_TAG_POLICY, s_ctx->session_id, s_ctx->session_addr, sf_uuid_str); utarray_erase(sf_array, sf_index, 1); continue; } else { LOG_INFO("%s: session %lu %s select sf by fail-action, sf_uuid %s to be selected", LOG_TAG_POLICY, s_ctx->session_id, s_ctx->session_addr, sf_uuid_str); selected_sf_set_info(selected_sf, sf_param); return; } } } handle_fail_action(&sff_param->sff_exception, selected_sf, 0); } static void select_sf_from_sff(struct policy_enforcer *enforcer, struct sff_param *sff_param, struct selected_sf *selected_sf, struct session_ctx *s_ctx, uint64_t packet_hash) { char sf_uuid_str[UUID_STRING_SIZE] = {0}; uuid_t *sf_uuid_ptr; UT_array *sf_array; UT_icd sf_icd = {sizeof(struct sf_param), NULL, NULL, NULL}; utarray_new(sf_array, &sf_icd); int sf_uuid_num = uuid_array_get_count(&sff_param->sf_uuid_array); for (int i = 0; i < sf_uuid_num; i++) { sf_uuid_ptr = uuid_array_get_at(&sff_param->sf_uuid_array, i); uuid_unparse(*sf_uuid_ptr, sf_uuid_str); struct sf_param *sf = (struct sf_param *)maat_plugin_table_get_ex_data(enforcer->maat, TABLE_NAME_SF, (const char *)sf_uuid_str, strlen(sf_uuid_str)); if (sf == NULL) { LOG_ERROR("%s: failed to get sf parameter of profile %s", LOG_TAG_POLICY, sf_uuid_str); continue; } if (select_sf_by_admin_status(sf) == 0) { LOG_INFO("%s: session %lu %s select sf by admin-status, sf_uuid %s to be excluded", LOG_TAG_POLICY, s_ctx->session_id, s_ctx->session_addr, sf_uuid_str); sf_param_free(sf); continue; } if (select_sf_by_localization(enforcer, sff_param, sf) == 0) { LOG_INFO("%s: session %lu %s select sf by localization, sf_uuid %s to be excluded", LOG_TAG_POLICY, s_ctx->session_id, s_ctx->session_addr, sf_uuid_str); sf_param_free(sf); continue; } utarray_push_back(sf_array, sf); sf_param_free(sf); } select_sf_by_ldbc(sff_param, selected_sf, s_ctx, sf_array, packet_hash); utarray_free(sf_array); } /****************************************************************************** * Public API -- Utils ******************************************************************************/ const char *traffic_type_tostring(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"; } } const char *forward_type_tostring(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"; } } const char *action_desc_tostring(enum action_desc action_desc) { switch (action_desc) { // success action case ACTION_FORWAED_DUE_SELECTED_SF: return "forward"; // failure action case ACTION_BYPASS_DUE_FAILURE_ACTION: return "bypass"; case ACTION_BLOCK_DUE_FAILURE_ACTION: return "block"; case ACTION_BLOCK_DUE_UNAVAILABLE_ACTION: return "re-dispatch block"; case ACTION_BYPASS_DUE_UNAVAILABLE_ACTION: return "re-dispatch bypass"; case ACTION_BYPASS_DUE_HEALTH_SF_LIMIT: return "re-dispatch bypass(health SF limit)"; // default action case ACTION_BYPASS_DUE_DEFAULT: return "bypass(default)"; case ACTION_BYPASS_DUE_INVALID_POLICY: return "bypass(invalid policy)"; // unreachable default: return "action unknown"; } } const char *encapsulate_method_tostring(enum encapsulate_method encap_method) { switch (encap_method) { case ENCAPSULATE_METHOD_NONE: return "none"; case ENCAPSULATE_METHOD_LAYER2_SWITCH: return "layer2_switch"; case ENCAPSULATE_METHOD_LAYER3_SWITCH: return "layer3_switch"; case ENCAPSULATE_METHOD_VXLAN_G: return "vxlan_g"; default: return "unknown"; } } /****************************************************************************** * Public API -- Selected Chaining ******************************************************************************/ // return NULL : error // return !NULL : success struct selected_chaining *selected_chaining_create(int chaining_size, uint64_t session_id, char *session_addr) { struct selected_chaining *chaining = (struct selected_chaining *)calloc(1, sizeof(struct selected_chaining) + chaining_size * sizeof(struct selected_sf)); assert(chaining); chaining->chaining_used = 0; chaining->chaining_size = chaining_size; chaining->chaining = (struct selected_sf *)(chaining + 1); assert(chaining->chaining); chaining->session_id = session_id; chaining->session_addr = session_addr; return chaining; } void selected_chaining_destory(struct selected_chaining *chaining) { if (chaining) { free(chaining); chaining = NULL; } } void selected_chaining_dump(struct selected_chaining *chaining) { char rule_uuid_str[UUID_STRING_SIZE] = {0}; char sff_uuid_str[UUID_STRING_SIZE] = {0}; char sf_uuid_str[UUID_STRING_SIZE] = {0}; if (chaining == NULL) { LOG_DEBUG("%s: selected_chaining: NULL", LOG_TAG_POLICY); return; } LOG_DEBUG("%s: session %lu %s selected_chaining->chaining_size : %d", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, chaining->chaining_size); LOG_DEBUG("%s: session %lu %s selected_chaining->chaining_used : %d", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, chaining->chaining_used); for (int i = 0; i < chaining->chaining_used; i++) { struct selected_sf *node = &(chaining->chaining[i]); uuid_unparse(node->rule_uuid, rule_uuid_str); uuid_unparse(node->sff_uuid, sff_uuid_str); uuid_unparse(node->sf_uuid, sf_uuid_str); LOG_DEBUG("%s: session %lu %s selected_chaining->node[%d]->rule_uuid : %s", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, i, rule_uuid_str); LOG_DEBUG("%s: session %lu %s selected_chaining->node[%d]->traffic_type : %s", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, i, traffic_type_tostring(node->traffic_type)); // sff LOG_DEBUG("%s: session %lu %s selected_chaining->node[%d]->sff_uuid : %s", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, i, sff_uuid_str); LOG_DEBUG("%s: session %lu %s selected_chaining->node[%d]->sff_forward_type : %s", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, i, forward_type_tostring(node->sff_forward_type)); // sf LOG_DEBUG("%s: session %lu %s selected_chaining->node[%d]->sf_uuid : %s", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, i, sf_uuid_str); LOG_DEBUG("%s: session %lu %s selected_chaining->node[%d]->sf_action_desc : %s", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, i, action_desc_tostring(node->sf_action_desc)); LOG_DEBUG("%s: session %lu %s selected_chaining->node[%d]->sf_connectivity->encapsulate_method : %s", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, i, encapsulate_method_tostring(node->sf_connectivity.method)); LOG_DEBUG("%s: session %lu %s selected_chaining->node[%d]->sf_connectivity->int_vlan_tag : %d", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, i, node->sf_connectivity.int_vlan_tag); LOG_DEBUG("%s: session %lu %s selected_chaining->node[%d]->sf_connectivity->ext_vlan_tag : %d", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, i, node->sf_connectivity.ext_vlan_tag); LOG_DEBUG("%s: session %lu %s selected_chaining->node[%d]->sf_connectivity->dest_ip : %s", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, i, node->sf_connectivity.dest_ip); } } void selected_chaining_bref(struct selected_chaining *chaining) { if (chaining == NULL) { return; } char rule_uuid_str[UUID_STRING_SIZE] = {0}; char sff_uuid_str[UUID_STRING_SIZE] = {0}; char sf_uuid_str[UUID_STRING_SIZE] = {0}; char buff[4096] = {0}; int buff_used = 0; int buff_size = sizeof(buff); buff_used += snprintf(buff + buff_used, buff_size - buff_used, "chaining_size:%d, chaining_used:%d, {", chaining->chaining_size, chaining->chaining_used); for (int i = 0; i < chaining->chaining_used; i++) { struct selected_sf *node = &(chaining->chaining[i]); if (buff_size - buff_used > 0) { if (i != 0) { buff_used += snprintf(buff + buff_used, buff_size - buff_used, ","); } uuid_unparse(node->rule_uuid, rule_uuid_str); uuid_unparse(node->sff_uuid, sff_uuid_str); uuid_unparse(node->sf_uuid, sf_uuid_str); buff_used += snprintf(buff + buff_used, buff_size - buff_used, "\"node[%d]\":{\"policy\":\"%s->%s->%s\",\"action\":\"%s->%s->%s\"}", i, rule_uuid_str, sff_uuid_str, sf_uuid_str, traffic_type_tostring(node->traffic_type), forward_type_tostring(node->sff_forward_type), action_desc_tostring(node->sf_action_desc)); } } LOG_INFO("%s: session %lu %s selected_chaining_bref: %s}", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, buff); } void selected_chaining_uniq(struct selected_chaining *chaining) { if (chaining == NULL) { return; } // Selected Service Chaining Before Unique : [1,2,3,1,2] // Selected Service Chaining After Unique : [1,2,3] int i = 0; int j = 0; int k = 0; int is_exist = 0; for (i = 0; i < chaining->chaining_used; i++) { is_exist = 0; for (j = 0; j < i; j++) { if (uuid_compare(chaining->chaining[i].sf_uuid, chaining->chaining[j].sf_uuid) == 0 && chaining->chaining[i].sf_action == chaining->chaining[j].sf_action) { is_exist = 1; break; } } if (is_exist == 0) { if (i != k) { memcpy(&(chaining->chaining[k]), &(chaining->chaining[i]), sizeof(struct selected_sf)); } k++; } } chaining->chaining_used = k; } /****************************************************************************** * Public API -- Policy Enforcer ******************************************************************************/ // return NULL : error // return !NULL : success struct policy_enforcer *policy_enforcer_create(const char *instance, const char *profile, int thread_num) { 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)); struct maat_options *opts = maat_options_new(); if (opts == NULL) { LOG_ERROR("%s: unable create maat opts", LOG_TAG_POLICY); goto error_out; } maat_options_set_logger(opts, "log/maat.log", (enum log_level)enforcer->config.log_level); maat_options_set_instance_name(opts, instance); maat_options_set_caller_thread_number(opts, thread_num); maat_options_set_foreign_cont_dir(opts, enforcer->config.foreign_cont_dir); // TODO set enforcer->config.scan_detail // Maat4 is not supported temporarily 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_options_set_json_file(opts, 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_options_set_redis(opts, enforcer->config.redis_server, redis_port_select, enforcer->config.redis_db_idx); 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_options_set_stat_on(opts); maat_options_set_stat_file(opts, enforcer->config.stat_file); if (enforcer->config.perf_switch) { maat_options_set_perf_on(opts); } } if (enforcer->config.deferred_load) { maat_options_set_deferred_load_on(opts); } if (strlen(enforcer->config.accept_tags)) { maat_options_set_accept_tags(opts, enforcer->config.accept_tags); } enforcer->maat = maat_new(opts, enforcer->config.table_info); if (enforcer->maat == NULL) { LOG_ERROR("%s: unable create maat", LOG_TAG_POLICY); goto error_out; } maat_options_free(opts); opts = NULL; return enforcer; error_out: if (opts) { maat_options_free(opts); opts = NULL; } policy_enforcer_destory(enforcer); return NULL; } void policy_enforcer_destory(struct policy_enforcer *enforcer) { if (enforcer) { if (enforcer->maat) { maat_free(enforcer->maat); enforcer->maat = NULL; } free(enforcer); enforcer = NULL; } } // return 0 : success // return -1 : error int policy_enforcer_register(struct policy_enforcer *enforcer) { LOG_INFO("%s: register policy callback ...", LOG_TAG_POLICY); if (maat_plugin_table_ex_schema_register(enforcer->maat, TABLE_NAME_SC, chaining_param_new_cb, chaining_param_free_cb, chaining_param_dup_cb, 0, enforcer) != 0) { LOG_ERROR("%s: register %s plugin extension callbacks failed", LOG_TAG_POLICY, TABLE_NAME_SC); return -1; } if (maat_plugin_table_ex_schema_register(enforcer->maat, TABLE_NAME_SFF, sff_param_new_cb, sff_param_free_cb, sff_param_dup_cb, 0, enforcer) != 0) { LOG_ERROR("%s: register %s plugin extension callbacks failed", LOG_TAG_POLICY, TABLE_NAME_SFF); return -1; } if (maat_plugin_table_ex_schema_register(enforcer->maat, TABLE_NAME_SF, sf_param_new_cb, sf_param_free_cb, sf_param_dup_cb, 0, enforcer) != 0) { LOG_ERROR("%s: register %s plugin extension callbacks failed", LOG_TAG_POLICY, TABLE_NAME_SF); return -1; } LOG_INFO("%s: register policy callback success", LOG_TAG_POLICY); return 0; } int policy_enforce_chaining_size(struct policy_enforcer *enforcer) { return enforcer->config.max_chaining_size; } void policy_enforce_select_chainings(struct policy_enforcer *enforcer, struct session_ctx *s_ctx, struct packet *data_pkt, uuid_t *rule_uuid_ptr, int direction) { char rule_uuid_str[UUID_STRING_SIZE] = {0}; char sff_id_str[UUID_STRING_SIZE] = {0}; char sf_uuid_str[UUID_STRING_SIZE] = {0}; uuid_t *sff_uuid_ptr; struct selected_chaining *chaining = NULL; uuid_unparse(*rule_uuid_ptr, rule_uuid_str); struct chaining_param *chaining_param = (struct chaining_param *)maat_plugin_table_get_ex_data(enforcer->maat, TABLE_NAME_SC, (const char *)rule_uuid_str, strlen(rule_uuid_str)); if (chaining_param == NULL) { LOG_ERROR("%s: session %lu %s failed to get chaining parameter of policy %s", LOG_TAG_POLICY, s_ctx->session_id, s_ctx->session_addr, rule_uuid_str); return; } if (chaining_param->traffic_type == TRAFFIC_TYPE_RAW) { chaining = s_ctx->chaining_raw; } else { chaining = s_ctx->chaining_decrypted; } LOG_INFO("%s: session %lu %s enforce %s chaining: rule_uuid %s", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, traffic_type_tostring(chaining_param->traffic_type), rule_uuid_str); int sff_uuid_num = uuid_array_get_count(&chaining_param->sff_uuid_array); for (int i = 0; i < sff_uuid_num && chaining->chaining_used < chaining->chaining_size; i++) { struct selected_sf *selected_sf = &(chaining->chaining[chaining->chaining_used]); selected_sf_init(selected_sf); sff_uuid_ptr = uuid_array_get_at(&chaining_param->sff_uuid_array, i); uuid_unparse(*sff_uuid_ptr, sff_id_str); struct sff_param *sff_param = (struct sff_param *)maat_plugin_table_get_ex_data(enforcer->maat, TABLE_NAME_SFF, (const char *)sff_id_str, strlen(sff_id_str)); if (sff_param == NULL) { LOG_ERROR("%s: session %lu %s failed to get sff parameter of profile %s, bypass current sff !!!", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, sff_id_str); continue; } // sc info uuid_copy(selected_sf->rule_uuid, *rule_uuid_ptr); selected_sf->rule_vsys_id = chaining_param->vsys_id; selected_sf->traffic_type = chaining_param->traffic_type; // sff info uuid_copy(selected_sf->sff_uuid, *sff_uuid_ptr); selected_sf->sff_forward_type = sff_param->sff_forward_type; // sf_index selected_sf->sf_index = chaining->chaining_used; uint64_t packet_hash = packet_get_hash(data_pkt, sff_param->sff_ldbc.method, direction); select_sf_from_sff(enforcer, sff_param, selected_sf, s_ctx, packet_hash); uuid_unparse(selected_sf->sf_uuid, sf_uuid_str); LOG_INFO("%s: session %lu %s enforce chaining [%d/%d]: policy: %s->%s->%s, action: %s->%s->%s", LOG_TAG_POLICY, chaining->session_id, chaining->session_addr, selected_sf->sf_index, chaining->chaining_size, rule_uuid_str, sff_id_str, sf_uuid_str, traffic_type_tostring(chaining_param->traffic_type), forward_type_tostring(selected_sf->sff_forward_type), action_desc_tostring(selected_sf->sf_action_desc)); chaining->chaining_used++; sff_param_free(sff_param); } uuid_array_append(&s_ctx->rule_uuid_array, *rule_uuid_ptr); selected_chaining_uniq(chaining); chaining_param_free(chaining_param); }