#include #include #include #include #include #include #include "tcp_policy.h" struct tcp_policy_enforcer { struct maat *maat; int table_id; void *logger; }; struct side_conn_param { int maxseg_enable; int maxseg_vaule; int nodelay; int keepalive; int keepcnt; int keepidle; int keepintvl; int ttl; int user_timeout; }; struct tcp_profile_param { int ref_cnt; int tcp_passthrough; int bypass_duplicated_packet; // TODO no use, need delete from CM struct side_conn_param client_side; struct side_conn_param server_side; }; static int parser_side_conn_param(const char *json_str, struct side_conn_param *out_val, void *logger) { cJSON *json = NULL; cJSON *object = NULL; cJSON *item = NULL; json = cJSON_Parse(json_str); if (json == NULL) { TFE_LOG_ERROR(logger, "Invalid tcp option param %s", json_str); return -1; } object = cJSON_GetObjectItem(json, "tcp_maxseg"); if (object) { item = cJSON_GetObjectItem(object, "enable"); if (item && item->type == cJSON_Number) { out_val->maxseg_enable = item->valueint; } item = cJSON_GetObjectItem(object, "maxseg"); if (item && item->type == cJSON_Number) { out_val->maxseg_vaule = item->valueint; } } item = cJSON_GetObjectItem(json, "nodelay"); if (item && item->type == cJSON_Number) { out_val->nodelay = item->valueint; } object = cJSON_GetObjectItem(json, "keep_alive"); if (object) { item = cJSON_GetObjectItem(object, "enable"); if (item && item->type == cJSON_Number) { out_val->keepalive = item->valueint; } item = cJSON_GetObjectItem(object, "tcp_keepcnt"); if (item && item->type == cJSON_Number) { out_val->keepcnt = item->valueint; } item = cJSON_GetObjectItem(object, "tcp_keepidle"); if (item && item->type == cJSON_Number) { out_val->keepidle = item->valueint; } item = cJSON_GetObjectItem(object, "tcp_keepintvl"); if (item && item->type == cJSON_Number) { out_val->keepintvl = item->valueint; } } item = cJSON_GetObjectItem(json, "ttl"); if (item && item->type == cJSON_Number) { out_val->ttl = item->valueint; } item = cJSON_GetObjectItem(json, "user_timeout"); if (item && item->type == cJSON_Number) { out_val->user_timeout = item->valueint; } cJSON_Delete(json); return 0; } static void profile_param_new_cb(const char *table_name, int table_id, const char *key, const char *table_line, void **ad, long argl, void *argp) { int ret = 0; int profile_id = 0; int tcp_passthrough = 0; int bypass_duplicated_packet = 0; char client_side_conn_param[512] = {0}; char server_side_conn_param[512] = {0}; int is_valid = 0; struct tcp_profile_param *param = NULL; struct tcp_policy_enforcer *enforcer = (struct tcp_policy_enforcer *)argp; ret = sscanf(table_line, "%d\t%d\t%d\t%s\t%s\t%d", &profile_id, &tcp_passthrough, &bypass_duplicated_packet, client_side_conn_param, server_side_conn_param, &is_valid); if (ret != 6) { TFE_LOG_ERROR(enforcer->logger, "Invalid tcp option profile: %s", table_line); goto error_out; } param = ALLOC(struct tcp_profile_param, 1); param->ref_cnt = 1; param->tcp_passthrough = tcp_passthrough; param->bypass_duplicated_packet = bypass_duplicated_packet; if (parser_side_conn_param(client_side_conn_param, ¶m->client_side, enforcer->logger) == -1) { goto error_out; } if (parser_side_conn_param(server_side_conn_param, ¶m->server_side, enforcer->logger) == -1) { goto error_out; } *ad = param; TFE_LOG_INFO(enforcer->logger, "Add tcp option profile: %s", key); return; error_out: if (param) { free(param); } } static void profile_param_free_cb(int table_id, void **ad, long argl, void *argp) { struct tcp_profile_param *param = (struct tcp_profile_param *)*ad; if (param == NULL) { return; } if ((__sync_sub_and_fetch(¶m->ref_cnt, 1) == 0)) { free(param); *ad = NULL; } } static void profile_param_dup_cb(int table_id, void **to, void **from, long argl, void *argp) { struct tcp_profile_param *param = (struct tcp_profile_param *)*from; if (param) { __sync_add_and_fetch(&(param->ref_cnt), 1); *to = param; } else { *to = NULL; } } static void profile_param_free(struct tcp_profile_param *param) { profile_param_free_cb(0, (void **)¶m, 0, NULL); } struct tcp_policy_enforcer *tcp_policy_enforcer_create(void *logger) { int ret = 0; struct tcp_policy_enforcer *enforcer = ALLOC(struct tcp_policy_enforcer, 1); enforcer->maat = (struct maat *)tfe_bussiness_resouce_get(STATIC_MAAT); enforcer->logger = logger; enforcer->table_id = maat_get_table_id(enforcer->maat, "PXY_PROFILE_TCP_OPTION"); if (enforcer->table_id < 0) { TFE_LOG_ERROR(enforcer->logger, "failed at register table of PXY_PROFILE_TCP_OPTION, ret = %d", enforcer->table_id); goto error_out; } ret = maat_plugin_table_ex_schema_register(enforcer->maat, "PXY_PROFILE_TCP_OPTION", profile_param_new_cb, profile_param_free_cb, profile_param_dup_cb, 0, enforcer); if (ret < 0) { TFE_LOG_ERROR(enforcer->logger, "failed at register callback of PXY_PROFILE_TCP_OPTION, ret = %d", ret); goto error_out; } return enforcer; error_out: tcp_policy_enforcer_destory(enforcer); return NULL; } void tcp_policy_enforcer_destory(struct tcp_policy_enforcer *enforcer) { if (enforcer) { free(enforcer); enforcer = NULL; } } // return 0 : success // return -1 : error (need passthrough) int tcp_policy_enforce(struct tcp_policy_enforcer *tcp_enforcer, struct tfe_cmsg *cmsg) { int ret = 0; int profile_id = 0; uint16_t size = 0; char buffer[16] = {0}; ret = tfe_cmsg_get_value(cmsg, TFE_CMSG_TCP_OPTION_PROFILE_ID, (unsigned char *)&profile_id, sizeof(profile_id), &size); if (ret < 0) { TFE_LOG_ERROR(g_default_logger, "Failed at fetch tcp_option_profile from cmsg: %s", strerror(-ret)); return -1; } snprintf(buffer, sizeof(buffer), "%d", profile_id); struct tcp_profile_param *param = (struct tcp_profile_param *)maat_plugin_table_get_ex_data(tcp_enforcer->maat, tcp_enforcer->table_id, buffer); if (param == NULL) { TFE_LOG_INFO(tcp_enforcer->logger, "Failed to get tcp option parameter of profile %d.", profile_id); return -1; } tfe_cmsg_set(cmsg, TFE_CMSG_TCP_PASSTHROUGH, (unsigned char *)¶m->tcp_passthrough, sizeof(param->tcp_passthrough)); struct side_conn_param *client_side = ¶m->client_side; tfe_cmsg_set(cmsg, TFE_CMSG_DOWNSTREAM_TCP_MSS_ENABLE, (unsigned char *)&client_side->maxseg_enable, sizeof(client_side->maxseg_enable)); tfe_cmsg_set(cmsg, TFE_CMSG_DOWNSTREAM_TCP_MSS_VALUE, (unsigned char *)&client_side->maxseg_vaule, sizeof(client_side->maxseg_vaule)); tfe_cmsg_set(cmsg, TFE_CMSG_DOWNSTREAM_TCP_NODELAY, (unsigned char *)&client_side->nodelay, sizeof(client_side->nodelay)); tfe_cmsg_set(cmsg, TFE_CMSG_DOWNSTREAM_TCP_TTL, (unsigned char *)&client_side->ttl, sizeof(client_side->ttl)); tfe_cmsg_set(cmsg, TFE_CMSG_DOWNSTREAM_TCP_KEEPALIVE, (unsigned char *)&client_side->keepalive, sizeof(client_side->keepalive)); tfe_cmsg_set(cmsg, TFE_CMSG_DOWNSTREAM_TCP_KEEPCNT, (unsigned char *)&client_side->keepcnt, sizeof(client_side->keepcnt)); tfe_cmsg_set(cmsg, TFE_CMSG_DOWNSTREAM_TCP_KEEPIDLE, (unsigned char *)&client_side->keepidle, sizeof(client_side->keepidle)); tfe_cmsg_set(cmsg, TFE_CMSG_DOWNSTREAM_TCP_KEEPINTVL, (unsigned char *)&client_side->keepidle, sizeof(client_side->keepintvl)); tfe_cmsg_set(cmsg, TFE_CMSG_DOWNSTREAM_TCP_USER_TIMEOUT, (unsigned char *)&client_side->user_timeout, sizeof(client_side->user_timeout)); struct side_conn_param *server_side = ¶m->server_side; tfe_cmsg_set(cmsg, TFE_CMSG_UPSTREAM_TCP_MSS_ENABLE, (unsigned char *)&server_side->maxseg_enable, sizeof(server_side->maxseg_enable)); tfe_cmsg_set(cmsg, TFE_CMSG_UPSTREAM_TCP_MSS_VALUE, (unsigned char *)&server_side->maxseg_vaule, sizeof(server_side->maxseg_vaule)); tfe_cmsg_set(cmsg, TFE_CMSG_UPSTREAM_TCP_NODELAY, (unsigned char *)&server_side->nodelay, sizeof(server_side->nodelay)); tfe_cmsg_set(cmsg, TFE_CMSG_UPSTREAM_TCP_TTL, (unsigned char *)&server_side->ttl, sizeof(server_side->ttl)); tfe_cmsg_set(cmsg, TFE_CMSG_UPSTREAM_TCP_KEEPALIVE, (unsigned char *)&server_side->keepalive, sizeof(server_side->keepalive)); tfe_cmsg_set(cmsg, TFE_CMSG_UPSTREAM_TCP_KEEPCNT, (unsigned char *)&server_side->keepcnt, sizeof(server_side->keepcnt)); tfe_cmsg_set(cmsg, TFE_CMSG_UPSTREAM_TCP_KEEPIDLE, (unsigned char *)&server_side->keepidle, sizeof(server_side->keepidle)); tfe_cmsg_set(cmsg, TFE_CMSG_UPSTREAM_TCP_KEEPINTVL, (unsigned char *)&server_side->keepintvl, sizeof(server_side->keepintvl)); tfe_cmsg_set(cmsg, TFE_CMSG_UPSTREAM_TCP_USER_TIMEOUT, (unsigned char *)&server_side->user_timeout, sizeof(server_side->user_timeout)); TFE_LOG_INFO(tcp_enforcer->logger, "hit tcp_option_profile %d tcp_passthrough %d " "client_side={maxseg_enable:%d, maxseg_vaule:%d, nodelay:%d, ttl:%d, keepalive:%d, keepcnt:%d, keepidle:%d, keepintvl:%d, user_timeout:%d} " "server_side={maxseg_enable:%d, maxseg_vaule:%d, nodelay:%d, ttl:%d, keepalive:%d, keepcnt:%d, keepidle:%d, keepintvl:%d, user_timeout:%d} ", profile_id, param->tcp_passthrough, client_side->maxseg_enable, client_side->maxseg_vaule, client_side->nodelay, client_side->ttl, client_side->keepalive, client_side->keepcnt, client_side->keepidle, client_side->keepidle, client_side->user_timeout, server_side->maxseg_enable, server_side->maxseg_vaule, server_side->nodelay, server_side->ttl, server_side->keepalive, server_side->keepcnt, server_side->keepidle, server_side->keepidle, server_side->user_timeout); profile_param_free(param); return 0; }