748 lines
25 KiB
C++
748 lines
25 KiB
C++
#include <assert.h>
|
||
#include <cjson/cJSON.h>
|
||
#include <tfe_stream.h>
|
||
#include <tfe_plugin.h>
|
||
#include <tfe_proxy.h>
|
||
|
||
#include <traffic_mirror.h>
|
||
#include <MESA/MESA_prof_load.h>
|
||
|
||
const static struct ether_addr ether_addr_broadcast{0xff,0xff,0xff,0xff, 0xff, 0xff};
|
||
|
||
struct traffic_mirror_me
|
||
{
|
||
struct profile_table_ex_data * profile_ex_data;
|
||
struct traffic_mirror_rebuild * rebuild_ctx;
|
||
|
||
/* Make the DEFER data not to mirror twice
|
||
* TODO: the size of (size_t) is enough for a tcp stream offset ? */
|
||
size_t downstream_rx_offset_mirrored;
|
||
size_t upstream_tx_offset_mirrored;
|
||
};
|
||
|
||
struct traffic_mirror_instance __g_traffic_mirror_instance;
|
||
struct traffic_mirror_instance * g_traffic_mirror_instance = &__g_traffic_mirror_instance;
|
||
|
||
void policy_table_ex_data_free(struct policy_table_ex_data * object)
|
||
{
|
||
if ((__sync_sub_and_fetch(&object->atomic_refcnt, 1) == 0)) free(object);
|
||
}
|
||
|
||
void policy_table_ex_data_dup_cb(int table_id, MAAT_PLUGIN_EX_DATA * to,
|
||
MAAT_PLUGIN_EX_DATA * from, long argl, void * argp)
|
||
{
|
||
struct policy_table_ex_data * ex_data = (struct policy_table_ex_data *)*from;
|
||
if(ex_data==NULL)
|
||
{
|
||
*to=NULL;
|
||
}
|
||
else
|
||
{
|
||
__sync_add_and_fetch(&ex_data->atomic_refcnt, 1);
|
||
*to = (void *)ex_data;
|
||
}
|
||
}
|
||
|
||
void policy_table_ex_data_free_cb(int table_id, MAAT_PLUGIN_EX_DATA * ad, long argl, void * argp)
|
||
{
|
||
struct policy_table_ex_data * ex_data = (struct policy_table_ex_data *)*ad;
|
||
if(ex_data)
|
||
{
|
||
policy_table_ex_data_free(ex_data);
|
||
}
|
||
}
|
||
|
||
void policy_table_ex_data_new_cb(int table_id, const char * key, const char * table_line,
|
||
MAAT_PLUGIN_EX_DATA * ad, long argl, void * argp)
|
||
{
|
||
struct traffic_mirror_instance * instance = (struct traffic_mirror_instance *) argp;
|
||
assert(instance != nullptr && instance->logger != nullptr);
|
||
|
||
char * str_json = NULL;
|
||
cJSON * json_root = NULL;
|
||
cJSON * json_subroot = NULL;
|
||
cJSON * json_item = NULL;
|
||
|
||
struct policy_table_ex_data * ex_data = NULL;
|
||
|
||
size_t user_region_offset;
|
||
size_t user_region_len;
|
||
|
||
int result = Maat_helper_read_column(table_line, 7, &user_region_offset, &user_region_len);
|
||
if (unlikely(result < 0))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "Failed at get policy table's user region.");
|
||
goto ignore;
|
||
}
|
||
|
||
str_json = ALLOC(char, user_region_len + 1);
|
||
memcpy(str_json, table_line + user_region_offset, user_region_len);
|
||
|
||
json_root = cJSON_Parse(str_json);
|
||
if (unlikely(!json_root))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at parsing user region as JSON format.");
|
||
goto ignore;
|
||
}
|
||
|
||
json_item=cJSON_GetObjectItem(json_root, "protocol");
|
||
if (unlikely(!json_item || !cJSON_IsString(json_item)))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "invalid JSON, protocol not existed or invalid type.");
|
||
goto ignore;
|
||
}
|
||
if(0!=strcasecmp(json_item->valuestring, "SSL")&& 0!=strcasecmp(json_item->valuestring, "HTTP"))
|
||
{
|
||
goto out;
|
||
}
|
||
|
||
json_subroot = cJSON_GetObjectItem(json_root, "traffic_mirror");
|
||
if (unlikely(!json_subroot))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "invalid format, traffic_mirror is not defined.");
|
||
goto ignore;
|
||
}
|
||
|
||
ex_data = ALLOC(struct policy_table_ex_data, 1);
|
||
ex_data->atomic_refcnt = 1;
|
||
ex_data->enable = 0;
|
||
ex_data->profile_id = 0;
|
||
ex_data->is_profile_set = 0;
|
||
|
||
json_item = cJSON_GetObjectItem(json_subroot, "enable");
|
||
if (unlikely(!json_item || !cJSON_IsNumber(json_item)))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "invalid JSON, traffic_mirror->enable not existed or invalid type.");
|
||
goto ignore;
|
||
}
|
||
|
||
ex_data->enable = json_item->valueint;
|
||
if (!ex_data->enable)
|
||
{
|
||
goto success;
|
||
}
|
||
|
||
json_item = cJSON_GetObjectItem(json_subroot, "mirror_profile");
|
||
if (unlikely(!json_item || !cJSON_IsNumber(json_item)))
|
||
{
|
||
TFE_LOG_DEBUG(instance->logger, "traffic_mirror->mirror_profile not existed, user default vlan id :%d.", instance->default_vlan_id);
|
||
ex_data->is_profile_set = 0;
|
||
ex_data->profile_id = 0;
|
||
}
|
||
else
|
||
{
|
||
ex_data->is_profile_set = 1;
|
||
ex_data->profile_id = json_item->valueint;
|
||
}
|
||
|
||
success:
|
||
TFE_LOG_DEBUG(instance->logger, "traffic mirror policy, key %s: enable = %d, profile = %d",
|
||
key, ex_data->enable, ex_data->profile_id);
|
||
|
||
*ad = ex_data;
|
||
ex_data = nullptr;
|
||
goto out;
|
||
|
||
ignore:
|
||
TFE_LOG_ERROR(instance->logger, "table line in TSG_SECURITY_COMPILE ignored %s: %s", key, table_line);
|
||
goto out;
|
||
|
||
out:
|
||
if (ex_data) policy_table_ex_data_free(ex_data);
|
||
if (json_root) cJSON_Delete(json_root);
|
||
if (str_json) free(str_json);
|
||
}
|
||
|
||
void profile_table_ex_data_free(struct profile_table_ex_data * object)
|
||
{
|
||
if ((__sync_sub_and_fetch(&object->atomic_refcnt, 1) == 0)) free(object);
|
||
}
|
||
|
||
void profile_table_ex_data_dup_cb(int table_id, MAAT_PLUGIN_EX_DATA * to,
|
||
MAAT_PLUGIN_EX_DATA * from, long argl, void * argp)
|
||
{
|
||
struct profile_table_ex_data * ex_data = (struct profile_table_ex_data *)*from;
|
||
if(ex_data)
|
||
{
|
||
__sync_add_and_fetch(&ex_data->atomic_refcnt, 1);
|
||
*to = (void *)ex_data;
|
||
}
|
||
else
|
||
{
|
||
*to = NULL;
|
||
}
|
||
}
|
||
|
||
void profile_table_ex_data_free_cb(int table_id, MAAT_PLUGIN_EX_DATA * ad, long argl, void * argp)
|
||
{
|
||
struct profile_table_ex_data * ex_data = (struct profile_table_ex_data *)*ad;
|
||
if(ex_data)
|
||
{
|
||
profile_table_ex_data_free(ex_data);
|
||
}
|
||
}
|
||
|
||
void profile_table_ex_data_new_cb(int table_id, const char * key, const char * table_line,
|
||
MAAT_PLUGIN_EX_DATA * ad, long argl, void * argp)
|
||
{
|
||
struct traffic_mirror_instance * instance = (struct traffic_mirror_instance *) argp;
|
||
assert(instance != nullptr && instance->logger != nullptr);
|
||
|
||
char * str_json = NULL;
|
||
cJSON * json_root = NULL;
|
||
cJSON * element = NULL;
|
||
unsigned int iter = 0;
|
||
|
||
struct profile_table_ex_data * ex_data = NULL;
|
||
size_t addr_list_offset;
|
||
size_t addr_list_len;
|
||
|
||
int result = Maat_helper_read_column(table_line, 3, &addr_list_offset, &addr_list_len);
|
||
if (unlikely(result < 0))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "Failed at get profile table's addrlist.");
|
||
goto ignore;
|
||
}
|
||
|
||
str_json = ALLOC(char, addr_list_len + 1);
|
||
memcpy(str_json, table_line + addr_list_offset, addr_list_len);
|
||
|
||
json_root = cJSON_Parse(str_json);
|
||
if (unlikely(!json_root))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at parsing addrlist as JSON format.");
|
||
goto ignore;
|
||
}
|
||
|
||
ex_data = ALLOC(struct profile_table_ex_data, 1);
|
||
ex_data->atomic_refcnt = 1;
|
||
ex_data->rewrite_mac = 0;
|
||
ex_data->rewrite_vlan = 0;
|
||
|
||
if (unlikely(!cJSON_IsArray(json_root)))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "invalid JSON, mirror_profile->vlan is not a array, %s.", str_json);
|
||
goto ignore;
|
||
}
|
||
|
||
ex_data->nr_targets = cJSON_GetArraySize(json_root);
|
||
ex_data->vlans = (unsigned int *)calloc(ex_data->nr_targets, sizeof(unsigned int));
|
||
ex_data->ether_addrs = (struct ether_addr *)calloc(ex_data->nr_targets, sizeof(struct ether_addr));
|
||
|
||
cJSON_ArrayForEach(element, json_root)
|
||
{
|
||
if (unlikely(!cJSON_IsNumber(element)))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "invalid JSON, elements in mirror_profile->vlan is not a number, %s.", str_json);
|
||
goto ignore;
|
||
}
|
||
|
||
unsigned int vlan_in_number = element->valueint;
|
||
if (unlikely(vlan_in_number <= 0 || vlan_in_number > 4094))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "invalid JSON, vlan id must between 1 and 4094.");
|
||
goto ignore;
|
||
}
|
||
|
||
TFE_LOG_DEBUG(instance->logger, "traffic mirror profile %s: vlan id[%d]: %d", key, iter, vlan_in_number);
|
||
ex_data->rewrite_vlan = 1;
|
||
ex_data->vlans[iter] = vlan_in_number;
|
||
ex_data->ether_addrs[iter] = ether_addr_broadcast;
|
||
iter++;
|
||
}
|
||
|
||
assert(iter == ex_data->nr_targets);
|
||
|
||
*ad = (void *)ex_data;
|
||
ex_data = nullptr;
|
||
|
||
TFE_LOG_DEBUG(instance->logger, "traffic mirror profile %s: %s", key, str_json);
|
||
goto out;
|
||
|
||
ignore:
|
||
TFE_LOG_ERROR(instance->logger, "table line in TSG_PROFILE_TRAFFIC_MIRROR ignored %s: %s", key, table_line);
|
||
goto out;
|
||
|
||
out:
|
||
if (ex_data)
|
||
{
|
||
profile_table_ex_data_free(ex_data);
|
||
}
|
||
|
||
if (str_json)
|
||
{
|
||
free(str_json);
|
||
}
|
||
|
||
if (json_root)
|
||
{
|
||
cJSON_Delete(json_root);
|
||
}
|
||
}
|
||
|
||
#define MAAT_INPUT_JSON 0
|
||
#define MAAT_INPUT_REDIS 1
|
||
#define MAAT_INPUT_FILE 2
|
||
|
||
static Maat_feather_t maat_feather_create_with_override(const char * instance_name,
|
||
const char * profile, const char * section, const char * override_section,
|
||
unsigned int max_thread, void * logger)
|
||
{
|
||
Maat_feather_t target;
|
||
int input_mode = 0, maat_stat_on = 0, maat_perf_on = 0;
|
||
int ret = 0, scan_detail = 0, effect_interval = 60;
|
||
|
||
char table_info[TFE_STRING_MAX] = {0}, inc_cfg_dir[TFE_STRING_MAX] = {0}, ful_cfg_dir[TFE_STRING_MAX] = {0};
|
||
char redis_server[TFE_STRING_MAX] = {0};
|
||
char redis_port_range[TFE_STRING_MAX] = {0};
|
||
char accept_tags[TFE_STRING_MAX] = {0};
|
||
int redis_port_begin = 0, redis_port_end = 0;
|
||
int redis_port_select = 0, deferred_load_on=0;
|
||
int redis_db_idx = 0;
|
||
|
||
char json_cfg_file[TFE_STRING_MAX] = {0};
|
||
char maat_stat_file[TFE_STRING_MAX] = {0};
|
||
|
||
MESA_load_profile_int_def(profile, section, "maat_input_mode", &(input_mode), 0);
|
||
MESA_load_profile_int_def(profile, section, "stat_switch", &(maat_stat_on), 1);
|
||
MESA_load_profile_int_def(profile, section, "perf_switch", &(maat_perf_on), 1);
|
||
MESA_load_profile_string_def(profile, section, "table_info", table_info, sizeof(table_info), "");
|
||
MESA_load_profile_string_def(profile, section, "accept_tags", accept_tags, sizeof(accept_tags), "");
|
||
MESA_load_profile_string_def(profile, section, "json_cfg_file", json_cfg_file, sizeof(json_cfg_file), "");
|
||
MESA_load_profile_string_def(profile, section, "maat_redis_server", redis_server, sizeof(redis_server), "");
|
||
MESA_load_profile_string_def(profile, section, "maat_redis_port_range", redis_port_range, sizeof(redis_server),
|
||
"6379");
|
||
MESA_load_profile_int_def(profile, section, "deferred_load_on", &(deferred_load_on), 0);
|
||
|
||
ret = sscanf(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
|
||
{
|
||
TFE_LOG_ERROR(logger, "Invalid redis port range %s, MAAT init failed.", redis_port_range);
|
||
}
|
||
|
||
MESA_load_profile_int_def(profile, section, "maat_redis_db_index", &(redis_db_idx), 0);
|
||
MESA_load_profile_string_def(profile, section, "inc_cfg_dir", inc_cfg_dir, sizeof(inc_cfg_dir), "");
|
||
MESA_load_profile_string_def(profile, section, "full_cfg_dir", ful_cfg_dir, sizeof(ful_cfg_dir), "");
|
||
|
||
MESA_load_profile_string_def(profile, section, "stat_file", maat_stat_file, sizeof(maat_stat_file), "");
|
||
MESA_load_profile_int_def(profile, section, "effect_interval_s", &(effect_interval), 60);
|
||
|
||
/* Only override the table info */
|
||
MESA_load_profile_string_def(profile, override_section, "table_info", table_info, sizeof(table_info), table_info);
|
||
MESA_load_profile_string_def(profile, override_section, "stat_file", maat_stat_file, sizeof(maat_stat_file), maat_stat_file);
|
||
|
||
effect_interval *= 1000;//convert s to ms
|
||
assert(strlen(inc_cfg_dir) != 0 || strlen(ful_cfg_dir) != 0 || strlen(redis_server) != 0
|
||
|| strlen(json_cfg_file) != 0);
|
||
|
||
target = Maat_feather(max_thread, table_info, logger);
|
||
Maat_set_feather_opt(target, MAAT_OPT_INSTANCE_NAME, instance_name, strlen(instance_name) + 1);
|
||
switch (input_mode)
|
||
{
|
||
case MAAT_INPUT_JSON:
|
||
Maat_set_feather_opt(target, MAAT_OPT_JSON_FILE_PATH, json_cfg_file, strlen(json_cfg_file) + 1);
|
||
break;
|
||
case MAAT_INPUT_REDIS:Maat_set_feather_opt(target, MAAT_OPT_REDIS_IP, redis_server, strlen(redis_server) + 1);
|
||
Maat_set_feather_opt(target, MAAT_OPT_REDIS_PORT, &redis_port_select, sizeof(redis_port_select));
|
||
Maat_set_feather_opt(target, MAAT_OPT_REDIS_INDEX, &redis_db_idx, sizeof(redis_db_idx));
|
||
break;
|
||
case MAAT_INPUT_FILE: Maat_set_feather_opt(target, MAAT_OPT_FULL_CFG_DIR, ful_cfg_dir, strlen(ful_cfg_dir) + 1);
|
||
Maat_set_feather_opt(target, MAAT_OPT_INC_CFG_DIR, inc_cfg_dir, strlen(inc_cfg_dir) + 1);
|
||
break;
|
||
default: TFE_LOG_ERROR(logger, "Invalid MAAT Input Mode: %d.", input_mode);
|
||
goto error_out;
|
||
break;
|
||
}
|
||
|
||
if (maat_stat_on)
|
||
{
|
||
Maat_set_feather_opt(target, MAAT_OPT_STAT_FILE_PATH, maat_stat_file, strlen(maat_stat_file) + 1);
|
||
Maat_set_feather_opt(target, MAAT_OPT_STAT_ON, NULL, 0);
|
||
if (maat_perf_on)
|
||
{
|
||
Maat_set_feather_opt(target, MAAT_OPT_PERF_ON, NULL, 0);
|
||
}
|
||
}
|
||
|
||
Maat_set_feather_opt(target, MAAT_OPT_DEFERRED_LOAD, &deferred_load_on, sizeof(deferred_load_on));
|
||
Maat_set_feather_opt(target, MAAT_OPT_EFFECT_INVERVAL_MS, &effect_interval, sizeof(effect_interval));
|
||
Maat_set_feather_opt(target, MAAT_OPT_SCAN_DETAIL, &scan_detail, sizeof(scan_detail));
|
||
if (strlen(accept_tags) > 0)
|
||
{
|
||
Maat_set_feather_opt(target, MAAT_OPT_ACCEPT_TAGS, &accept_tags, sizeof(accept_tags));
|
||
}
|
||
|
||
ret = Maat_initiate_feather(target);
|
||
if (ret < 0)
|
||
{
|
||
TFE_LOG_ERROR(logger, "%s MAAT init failed.", __FUNCTION__);
|
||
goto error_out;
|
||
}
|
||
|
||
return target;
|
||
error_out:
|
||
Maat_burn_feather(target);
|
||
return NULL;
|
||
}
|
||
|
||
static int traffic_mirror_ethdev_init(struct traffic_mirror_instance * instance)
|
||
{
|
||
char str_ethdev[TFE_SYMBOL_MAX] = {0};
|
||
const char * profile = "./conf/tfe/tfe.conf";
|
||
|
||
int ret = MESA_load_profile_string_nodef(profile, "traffic_mirror", "device",
|
||
str_ethdev, sizeof(str_ethdev));
|
||
|
||
if (ret < 0)
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at reading conffile, "
|
||
"[traffic_mirror]device is not defined.");
|
||
return -1;
|
||
}
|
||
|
||
MESA_load_profile_uint_def(profile, "traffic_mirror", "default_vlan_id", &(instance->default_vlan_id), 0);
|
||
|
||
unsigned int device_type;
|
||
MESA_load_profile_uint_def(profile, "traffic_mirror", "type", &device_type, TRAFFIC_MIRROR_ETHDEV_AF_PACKET);
|
||
|
||
if (device_type == TRAFFIC_MIRROR_ETHDEV_AF_PACKET)
|
||
{
|
||
instance->ethdev = traffic_mirror_ethdev_pcap_create(str_ethdev, instance->logger);
|
||
}
|
||
else if(device_type == TRAFFIC_MIRROR_ETHDEV_MARSIO)
|
||
{
|
||
instance->ethdev = traffic_mirror_ethdev_mr4_create(str_ethdev,
|
||
tfe_proxy_get_work_thread_count(), instance->logger);
|
||
}
|
||
else
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "invalid traffic mirror device type, [traffic_mirror]type = %d", device_type);
|
||
return -2;
|
||
}
|
||
|
||
if (!instance->ethdev)
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at traffic mirror device init. ");
|
||
return -3;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int traffic_mirror_init(struct tfe_proxy * proxy)
|
||
{
|
||
int result = 0;
|
||
struct traffic_mirror_instance * instance = g_traffic_mirror_instance;
|
||
instance->logger = tfe_proxy_get_error_logger();
|
||
|
||
/* Using PANGU-HTTP's profile */
|
||
MESA_load_profile_uint_def("./conf/tfe/tfe.conf", "traffic_mirror", "enable", &(instance->enable), 1);
|
||
if (!instance->enable)
|
||
{
|
||
TFE_LOG_INFO(instance->logger, "traffic_mirror is disabled.");
|
||
return 0;
|
||
}
|
||
|
||
/* INIT DECRYPT MIRROR INSTANCE */
|
||
instance->nr_threads = tfe_proxy_get_work_thread_count();
|
||
|
||
/* MAAT Feather, the configuration is same with pangu-http */
|
||
instance->maat_feather = maat_feather_create_with_override(
|
||
"traffic-mirror", "./conf/tfe/tfe.conf",
|
||
"maat", "traffic_mirror", instance->nr_threads, instance->logger);
|
||
|
||
if (unlikely(!instance->maat_feather))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at creating maat feather.");
|
||
goto errout;
|
||
}
|
||
|
||
/* REGISTER MAAT FEATHER */
|
||
instance->policy_table_id = Maat_table_register(instance->maat_feather, "TSG_SECURITY_COMPILE");
|
||
if (unlikely(instance->policy_table_id < 0))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at register table TSG_SECURITY_COMPILE, ret = %d",
|
||
instance->policy_table_id); goto errout;
|
||
}
|
||
|
||
instance->profile_table_id = Maat_table_register(instance->maat_feather, "TSG_PROFILE_TRAFFIC_MIRROR");
|
||
if (unlikely(instance->profile_table_id < 0))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at register table TSG_PROFILE_TRAFFIC_MIRROR, ret = %d",
|
||
instance->profile_table_id); goto errout;
|
||
}
|
||
|
||
result = Maat_plugin_EX_register(instance->maat_feather, instance->policy_table_id,
|
||
policy_table_ex_data_new_cb, policy_table_ex_data_free_cb, policy_table_ex_data_dup_cb,
|
||
nullptr, 0, instance);
|
||
|
||
if(unlikely(result < 0))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at Maat_plugin_EX_register(TSG_SECURITY_COMPILE), "
|
||
"table_id = %d, ret = %d", instance->policy_table_id, result);
|
||
goto errout;
|
||
}
|
||
|
||
result = Maat_plugin_EX_register(instance->maat_feather, instance->profile_table_id,
|
||
profile_table_ex_data_new_cb, profile_table_ex_data_free_cb, profile_table_ex_data_dup_cb,
|
||
nullptr, 0, instance);
|
||
|
||
if (unlikely(result < 0))
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at Maat_plugin_EX_register(TSG_PROFILE_TRAFFIC_MIRROR), "
|
||
"table_id = %d, ret = %d", instance->policy_table_id, result);
|
||
}
|
||
|
||
if (traffic_mirror_ethdev_init(instance) < 0)
|
||
{
|
||
goto errout;
|
||
}
|
||
|
||
return 0;
|
||
|
||
errout:
|
||
return -1;
|
||
}
|
||
|
||
const static ether_addr zero_mac = {0};
|
||
const static unsigned char default_src_mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
|
||
const static unsigned char default_dst_mac[6] = {0x00, 0x05, 0x04, 0x03, 0x02, 0x01};
|
||
int traffic_mirror_on_open_cb(const struct tfe_stream * stream, unsigned int thread_id,
|
||
enum tfe_conn_dir dir, void ** pme)
|
||
{
|
||
/* Firstly, fetch destination address of traffic mirror */
|
||
struct traffic_mirror_me * me = NULL;
|
||
struct traffic_mirror_instance * instance = g_traffic_mirror_instance;
|
||
if (!instance->enable)
|
||
{
|
||
return ACTION_FORWARD_DATA;
|
||
}
|
||
struct tfe_cmsg * cmsg = tfe_stream_get0_cmsg(stream);
|
||
|
||
unsigned int target_id;
|
||
struct traffic_mirror_rebuild_target * rebuild_target = NULL;
|
||
|
||
assert(instance != NULL);
|
||
assert(cmsg != NULL);
|
||
|
||
char str_policy_id[TFE_SYMBOL_MAX] = {0};
|
||
char str_profile_id[TFE_SYMBOL_MAX] = {0};
|
||
|
||
unsigned int opt_val;
|
||
uint16_t opt_out_size;
|
||
|
||
struct policy_table_ex_data * policy_ex_data = NULL;
|
||
struct profile_table_ex_data * profile_ex_data = NULL;
|
||
|
||
struct ether_addr c_ether_addr = {};
|
||
struct ether_addr s_ether_addr = {};
|
||
|
||
int ret = tfe_cmsg_get_value(cmsg, TFE_CMSG_POLICY_ID, (unsigned char *) &opt_val, sizeof(opt_val), &opt_out_size);
|
||
if (ret < 0)
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at getting policy id from cmsg, detach the stream.");
|
||
goto detach;
|
||
}
|
||
|
||
snprintf(str_policy_id, sizeof(str_policy_id), "%u", opt_val);
|
||
policy_ex_data = (struct policy_table_ex_data *) Maat_plugin_get_EX_data(instance->maat_feather,
|
||
instance->policy_table_id, str_policy_id);
|
||
if (!policy_ex_data || !policy_ex_data->enable)
|
||
{
|
||
goto detach;
|
||
}
|
||
|
||
ret = tfe_cmsg_get_value(cmsg, TFE_CMSG_SRC_MAC, (unsigned char *) &c_ether_addr,
|
||
sizeof(c_ether_addr), &opt_out_size);
|
||
if (ret < 0 || memcmp(&c_ether_addr, &zero_mac, sizeof(c_ether_addr)) == 0)
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at source mac address, user default src mac: {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}");
|
||
memcpy(&c_ether_addr, &default_src_mac, sizeof(c_ether_addr));
|
||
}
|
||
|
||
ret = tfe_cmsg_get_value(cmsg, TFE_CMSG_DST_MAC, (unsigned char *) &s_ether_addr,
|
||
sizeof(s_ether_addr), &opt_out_size);
|
||
if (ret < 0 || memcmp(&s_ether_addr, &zero_mac, sizeof(s_ether_addr)) == 0)
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at dest mac address, user default dest mac: {0x06, 0x05, 0x04, 0x03, 0x02, 0x01}");
|
||
memcpy(&s_ether_addr, &default_dst_mac, sizeof(s_ether_addr));
|
||
}
|
||
|
||
rebuild_target = ALLOC(struct traffic_mirror_rebuild_target, 1);
|
||
|
||
if (policy_ex_data->is_profile_set)
|
||
{
|
||
snprintf(str_profile_id, sizeof(str_policy_id), "%u", policy_ex_data->profile_id);
|
||
profile_ex_data = (struct profile_table_ex_data *)Maat_plugin_get_EX_data(instance->maat_feather,
|
||
instance->profile_table_id, str_profile_id);
|
||
if (!profile_ex_data)
|
||
{
|
||
TFE_LOG_ERROR(instance->logger, "failed at getting policy %s's profile, profile id = %s, "
|
||
"detach the stream",
|
||
str_policy_id, str_profile_id);
|
||
goto detach;
|
||
}
|
||
|
||
target_id = random() % profile_ex_data->nr_targets;
|
||
rebuild_target->vlan_tci = profile_ex_data->vlans[target_id];
|
||
rebuild_target->ether_addr = profile_ex_data->ether_addrs[target_id];
|
||
rebuild_target->rewrite_as_target_mac = profile_ex_data->rewrite_mac;
|
||
rebuild_target->rewrite_as_target_vlan = profile_ex_data->rewrite_vlan;
|
||
}
|
||
else
|
||
{
|
||
rebuild_target->vlan_tci = instance->default_vlan_id;
|
||
rebuild_target->ether_addr = ether_addr_broadcast;
|
||
rebuild_target->rewrite_as_target_mac = 0;
|
||
rebuild_target->rewrite_as_target_vlan = 1;
|
||
}
|
||
|
||
me = ALLOC(struct traffic_mirror_me, 1);
|
||
me->rebuild_ctx = traffic_mirror_rebuild_create(stream->addr, &c_ether_addr, &s_ether_addr,
|
||
rebuild_target, instance->ethdev);
|
||
|
||
me->profile_ex_data = profile_ex_data;
|
||
*pme = (void *) me;
|
||
|
||
/* the ownership is transfer to struct me and rebuild_target */
|
||
profile_ex_data = NULL;
|
||
rebuild_target = NULL;
|
||
|
||
traffic_mirror_rebuild_handshake(me->rebuild_ctx, thread_id);
|
||
return ACTION_FORWARD_DATA;
|
||
|
||
detach:
|
||
if (me)
|
||
{
|
||
free(me);
|
||
}
|
||
|
||
if (policy_ex_data)
|
||
{
|
||
policy_table_ex_data_free(policy_ex_data);
|
||
}
|
||
|
||
if (profile_ex_data)
|
||
{
|
||
profile_table_ex_data_free(profile_ex_data);
|
||
}
|
||
|
||
if (rebuild_target)
|
||
{
|
||
free(rebuild_target);
|
||
}
|
||
|
||
tfe_stream_detach(stream);
|
||
return ACTION_FORWARD_DATA;
|
||
}
|
||
|
||
enum tfe_stream_action traffic_mirror_on_data_cb(const struct tfe_stream * stream, unsigned int thread_id,
|
||
enum tfe_conn_dir dir, const unsigned char * data, size_t len, void ** pme)
|
||
{
|
||
struct traffic_mirror_instance * instance = g_traffic_mirror_instance;
|
||
if (!instance->enable)
|
||
{
|
||
return ACTION_FORWARD_DATA;
|
||
}
|
||
struct traffic_mirror_me * me = (struct traffic_mirror_me *)(*pme);
|
||
|
||
/* Rx offset of this callback */
|
||
size_t rx_offset_this_time;
|
||
size_t rx_offset_mirrored;
|
||
enum tfe_stream_info rx_offset_type;
|
||
|
||
/* Need to mirrored data */
|
||
const unsigned char * ptr_data_need_to_mirrored;
|
||
size_t sz_data_need_to_mirrored = 0;
|
||
|
||
if (dir == CONN_DIR_DOWNSTREAM)
|
||
{
|
||
rx_offset_type = INFO_FROM_DOWNSTREAM_RX_OFFSET;
|
||
rx_offset_mirrored = me->downstream_rx_offset_mirrored;
|
||
}
|
||
else
|
||
{
|
||
rx_offset_type = INFO_FROM_UPSTREAM_RX_OFFSET;
|
||
rx_offset_mirrored = me->upstream_tx_offset_mirrored;
|
||
}
|
||
|
||
/* Get the offset of this callback */
|
||
int ret = tfe_stream_info_get(stream, rx_offset_type, &rx_offset_this_time, sizeof(rx_offset_this_time));
|
||
if (unlikely(ret < 0))
|
||
{
|
||
TFE_STREAM_LOG_ERROR(stream, "Failed at fetch rx offset, detached.");
|
||
goto errout;
|
||
}
|
||
|
||
/* Mirrored offset must be larger than rx_offset,
|
||
* Otherwise, there is a hole in data stream */
|
||
assert(rx_offset_mirrored >= rx_offset_this_time);
|
||
assert(rx_offset_this_time + len >= rx_offset_mirrored);
|
||
|
||
sz_data_need_to_mirrored = len - (rx_offset_mirrored - rx_offset_this_time);
|
||
ptr_data_need_to_mirrored = data + (len - sz_data_need_to_mirrored);
|
||
|
||
/* Don't need to mirrored, the data has been mirrored in DEFER state */
|
||
if (sz_data_need_to_mirrored == 0)
|
||
goto out;
|
||
|
||
/* Update the mirrored offset */
|
||
if (dir == CONN_DIR_DOWNSTREAM) me->downstream_rx_offset_mirrored += sz_data_need_to_mirrored;
|
||
else me->upstream_tx_offset_mirrored += sz_data_need_to_mirrored;
|
||
|
||
traffic_mirror_rebuild_data(me->rebuild_ctx, thread_id, (const char *) ptr_data_need_to_mirrored,
|
||
(size_t) sz_data_need_to_mirrored, dir);
|
||
|
||
out:
|
||
return ACTION_FORWARD_DATA;
|
||
|
||
errout:
|
||
tfe_stream_detach(stream);
|
||
return ACTION_FORWARD_DATA;
|
||
}
|
||
|
||
void traffic_mirror_on_close_cb(const struct tfe_stream * stream, unsigned int thread_id,
|
||
enum tfe_stream_close_reason reason, void ** pme)
|
||
{
|
||
struct traffic_mirror_instance * instance = g_traffic_mirror_instance;
|
||
if (!instance->enable)
|
||
{
|
||
return;
|
||
}
|
||
|
||
struct traffic_mirror_me * me = (struct traffic_mirror_me *)(*pme);
|
||
traffic_mirror_rebuild_farewell(me->rebuild_ctx, thread_id);
|
||
traffic_mirror_rebuild_destroy(me->rebuild_ctx);
|
||
if (me->profile_ex_data)
|
||
{
|
||
profile_table_ex_data_free(me->profile_ex_data);
|
||
}
|
||
|
||
free(me);
|
||
*pme = NULL;
|
||
}
|
||
|
||
void traffic_mirror_deinit(struct tfe_proxy * proxy){}
|
||
|
||
struct tfe_plugin traffic_mirror_plugin_desc =
|
||
{
|
||
.symbol= "traffic_mirror",
|
||
.type = TFE_PLUGIN_TYPE_BUSINESS,
|
||
.on_init = traffic_mirror_init,
|
||
.on_deinit = traffic_mirror_deinit,
|
||
.on_open = traffic_mirror_on_open_cb,
|
||
.on_data = traffic_mirror_on_data_cb,
|
||
.on_close = traffic_mirror_on_close_cb
|
||
};
|
||
|
||
TFE_PLUGIN_REGISTER(traffic_mirror, traffic_mirror_plugin_desc)
|