This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
tango-maat/src/maat_ip.cpp
2023-02-03 17:28:14 +08:00

694 lines
22 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
**********************************************************************************************
* File: maat_ip.cpp
* Description:
* Authors: Liu WenTan <liuwentan@geedgenetworks.com>
* Date: 2022-10-31
* Copyright: (c) 2018-2022 Geedge Networks, Inc. All rights reserved.
***********************************************************************************************
*/
#include <stdint.h>
#include <assert.h>
#include "log/log.h"
#include "cJSON/cJSON.h"
#include "maat_utils.h"
#include "maat_ex_data.h"
#include "IPMatcher.h"
#include "maat_ip.h"
#include "maat_rule.h"
#include "maat_compile.h"
#include "alignment.h"
#include "maat_garbage_collection.h"
#define MODULE_IP module_name_str("maat.ip")
struct port_range {
uint16_t min_port;
uint16_t max_port;
};
struct ip_schema {
int item_id_column;
int group_id_column;
int addr_type_column;
int saddr_format_column;
int sip1_column;
int sip2_column;
int sport_format_column;
int sport1_column;
int sport2_column;
int proto_column;
int direction_column;
int table_id; //ugly
struct table_manager *tbl_mgr;
};
struct ipv4_item_rule {
uint32_t min_sip; /* 源地址下界0表示忽略本字段 */
uint32_t max_sip; /* 源地址上界0表示固定IP=min_saddr */
uint16_t min_sport; /* 源端口范围下界0表示忽略本字段 */
uint16_t max_sport; /* 源端口范围上界0表示固定端口=min_sport */
uint16_t proto; /* 传输层协议6表示TCP17表示UDP0表示忽略本字段 */
uint16_t direction; /* 方向0表示双向1表示单向 */
};
struct ipv6_item_rule {
uint32_t min_sip[4]; /* 源地址下界全0表示忽略本字段 */
uint32_t max_sip[4]; /* 源地址上界全0表示固定IP=min_saddr */
uint16_t min_sport; /* 源端口范围下界0表示忽略本字段 */
uint16_t max_sport; /* 源端口范围上界0表示固定端口=min_sport */
uint16_t proto; /* 传输层协议6表示TCP17表示UDP无限制默认为0 */
uint16_t direction; /* 方向0表示双向1表示单向 */
};
struct ip_item {
int item_id;
int group_id;
int addr_type;
union {
struct ipv4_item_rule ipv4;
struct ipv6_item_rule ipv6;
};
};
struct ip_runtime {
struct ip_matcher* ip_matcher;
struct ex_data_runtime* ex_data_rt;
uint32_t rule_num;
uint32_t updating_rule_num;
struct maat_item *item_hash;
void (*item_user_data_free)(void *);
struct maat_garbage_bin *ref_garbage_bin;
struct log_handle *logger;
long long *scan_cnt;
long long *hit_cnt;
};
void *ip_schema_new(cJSON *json, struct table_manager *tbl_mgr,
const char *table_name, struct log_handle *logger)
{
size_t read_cnt = 0;
struct ip_schema *ip_schema = ALLOC(struct ip_schema, 1);
cJSON *custom_item = NULL;
cJSON *item = cJSON_GetObjectItem(json, "table_id");
if (item != NULL && item->type == cJSON_Number) {
ip_schema->table_id = item->valueint;
read_cnt++;
}
item = cJSON_GetObjectItem(json, "custom");
if (NULL == item || item->type != cJSON_Object) {
log_error(logger, MODULE_IP,
"ip table %s has no custom column", table_name);
goto error;
}
custom_item = cJSON_GetObjectItem(item, "item_id");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->item_id_column = custom_item->valueint;
read_cnt++;
}
custom_item = cJSON_GetObjectItem(item, "group_id");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->group_id_column = custom_item->valueint;
read_cnt++;
}
custom_item = cJSON_GetObjectItem(item, "addr_type");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->addr_type_column = custom_item->valueint;
read_cnt++;
}
custom_item = cJSON_GetObjectItem(item, "saddr_format");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->saddr_format_column = custom_item->valueint;
read_cnt++;
}
custom_item = cJSON_GetObjectItem(item, "sip1");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->sip1_column = custom_item->valueint;
read_cnt++;
}
custom_item = cJSON_GetObjectItem(item, "sip2");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->sip2_column = custom_item->valueint;
read_cnt++;
}
custom_item = cJSON_GetObjectItem(item, "sport_format");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->sport_format_column = custom_item->valueint;
read_cnt++;
}
custom_item = cJSON_GetObjectItem(item, "sport1");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->sport1_column = custom_item->valueint;
read_cnt++;
}
custom_item = cJSON_GetObjectItem(item, "sport2");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->sport2_column = custom_item->valueint;
read_cnt++;
}
custom_item = cJSON_GetObjectItem(item, "proto");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->proto_column = custom_item->valueint;
read_cnt++;
}
custom_item = cJSON_GetObjectItem(item, "direction");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->direction_column = custom_item->valueint;
read_cnt++;
}
ip_schema->tbl_mgr = tbl_mgr;
if (read_cnt < 12) {
goto error;
}
return ip_schema;
error:
FREE(ip_schema);
return NULL;
}
void ip_schema_free(void *ip_schema)
{
FREE(ip_schema);
}
void *ip_runtime_new(void *ip_schema, int max_thread_num,
struct maat_garbage_bin *garbage_bin,
struct log_handle *logger)
{
if (NULL == ip_schema) {
return NULL;
}
struct ip_schema *schema = (struct ip_schema *)ip_schema;
struct ip_runtime *ip_rt = ALLOC(struct ip_runtime, 1);
ip_rt->ex_data_rt = ex_data_runtime_new(schema->table_id,
ex_data_container_free,
logger);
ip_rt->item_user_data_free = maat_item_inner_free;
ip_rt->ref_garbage_bin = garbage_bin;
ip_rt->logger = logger;
ip_rt->hit_cnt = alignment_int64_array_alloc(max_thread_num);
ip_rt->scan_cnt = alignment_int64_array_alloc(max_thread_num);
return ip_rt;
}
void ip_runtime_free(void *ip_runtime)
{
if (NULL == ip_runtime) {
return;
}
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
if (ip_rt->ip_matcher != NULL) {
ip_matcher_free(ip_rt->ip_matcher);
}
if (ip_rt->ex_data_rt != NULL) {
ex_data_runtime_free(ip_rt->ex_data_rt);
}
struct maat_item *item = NULL, *tmp_item = NULL;
HASH_ITER(hh, ip_rt->item_hash, item, tmp_item) {
HASH_DELETE(hh, ip_rt->item_hash, item);
maat_item_free(item, ip_rt->item_user_data_free);
}
if (ip_rt->hit_cnt != NULL) {
alignment_int64_array_free(ip_rt->hit_cnt);
ip_rt->hit_cnt = NULL;
}
if (ip_rt->scan_cnt != NULL) {
alignment_int64_array_free(ip_rt->scan_cnt);
ip_rt->scan_cnt = NULL;
}
FREE(ip_rt);
}
struct ip_item *ip_item_new(const char *line, struct ip_schema *ip_schema,
struct log_handle *logger)
{
size_t column_offset = 0;
size_t column_len = 0;
char saddr_format[16] = {0};
char sport_format[16] = {0};
char sip1_str[40] = {0};
char sip2_str[40] = {0};
uint16_t sport1 = 0;
uint16_t sport2 = 0;
uint16_t protocol = 0;
uint16_t direction = 0;
struct ip_item *ip_item = ALLOC(struct ip_item, 1);
int ret = get_column_pos(line, ip_schema->item_id_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP,
"ip plus table(table_id:%d) line:%s has no item_id",
ip_schema->table_id, line);
goto error;
}
ip_item->item_id = atoi(line + column_offset);
ret = get_column_pos(line, ip_schema->group_id_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP,
"ip plus table(table_id:%d) line:%s has no group_id",
ip_schema->table_id, line);
goto error;
}
ip_item->group_id = atoi(line + column_offset);
ret = get_column_pos(line, ip_schema->addr_type_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP,
"ip plus table(table_id:%d) line:%s has no addr_type",
ip_schema->table_id, line);
goto error;
}
ip_item->addr_type = atoi(line + column_offset);
if (ip_item->addr_type != IPv4 && ip_item->addr_type != IPv6) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s has invalid addr type:%d",
ip_schema->table_id, line, ip_item->addr_type);
goto error;
}
ret = get_column_pos(line, ip_schema->saddr_format_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s has no saddr_format",
ip_schema->table_id, line);
goto error;
}
memcpy(saddr_format, (line + column_offset), column_len);
if (IP_FORMAT_UNKNOWN == ip_format_str2int(saddr_format)) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s has invalid saddr_format, should be range/mask/CIDR",
ip_schema->table_id, line);
goto error;
}
ret = get_column_pos(line, ip_schema->sip1_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP, "ip table(table_id:%d) line:%s has no sip1",
ip_schema->table_id, line);
goto error;
}
memcpy(sip1_str, (line + column_offset), column_len);
ret = get_column_pos(line, ip_schema->sip2_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP, "ip table(table_id:%d) line:%s has no sip2",
ip_schema->table_id, line);
goto error;
}
memcpy(sip2_str, (line + column_offset), column_len);
ret = get_column_pos(line, ip_schema->sport_format_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s has no sport_format",
ip_schema->table_id, line);
goto error;
}
memcpy(sport_format, (line + column_offset), column_len);
if (IP_FORMAT_UNKNOWN == ip_format_str2int(sport_format)) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s has invalid sport_format, should be range/mask/CIDR",
ip_schema->table_id, line);
goto error;
}
ret = get_column_pos(line, ip_schema->sport1_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP, "ip table(table_id:%d) line:%s has no sport1",
ip_schema->table_id, line);
goto error;
}
sport1 = atoi(line + column_offset);
ret = get_column_pos(line, ip_schema->sport2_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s has no sport2",
ip_schema->table_id, line);
goto error;
}
sport2 = atoi(line + column_offset);
if (IPv4 == ip_item->addr_type) {
ret = ip_format2range(ip_item->addr_type, ip_format_str2int(saddr_format), sip1_str, sip2_str,
&ip_item->ipv4.min_sip, &ip_item->ipv4.max_sip);
if (ret < 0) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s ip_format2range(ip4) failed",
ip_schema->table_id, line);
goto error;
}
if(IP_FORMAT_MASK == ip_format_str2int(sport_format)) {
ip_item->ipv4.min_sport = sport1 & sport2;
ip_item->ipv4.max_sport = sport1 | ~sport2;
} else {
ip_item->ipv4.min_sport = sport1;
ip_item->ipv4.max_sport = sport2;
}
ret = get_column_pos(line, ip_schema->proto_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s has no proto",
ip_schema->table_id, line);
goto error;
}
ip_item->ipv4.proto = atoi(line + column_offset);
protocol = ip_item->ipv4.proto;
ret = get_column_pos(line, ip_schema->direction_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s has no direction",
ip_schema->table_id, line);
goto error;
}
ip_item->ipv4.direction = atoi(line + column_offset);
direction = ip_item->ipv4.direction;
} else {
//ipv6
ret = ip_format2range(ip_item->addr_type, ip_format_str2int(saddr_format), sip1_str, sip2_str,
ip_item->ipv6.min_sip, ip_item->ipv6.max_sip);
if (ret < 0) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s ip_format2range(ip6) failed",
ip_schema->table_id, line);
goto error;
}
if(IP_FORMAT_MASK == ip_format_str2int(sport_format)) {
ip_item->ipv6.min_sport = sport1 & sport2;
ip_item->ipv6.max_sport = sport1 | ~sport2;
} else {
ip_item->ipv6.min_sport = sport1;
ip_item->ipv6.max_sport = sport2;
}
ret = get_column_pos(line, ip_schema->proto_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP, "ip table(table_id:%d) line:%s has no proto",
ip_schema->table_id, line);
goto error;
}
ip_item->ipv6.proto = atoi(line + column_offset);
protocol = ip_item->ipv6.proto;
ret = get_column_pos(line, ip_schema->direction_column, &column_offset, &column_len);
if (ret < 0) {
log_error(logger, MODULE_IP, "ip table(table_id:%d) line:%s has no direction",
ip_schema->table_id, line);
goto error;
}
ip_item->ipv6.direction = atoi(line + column_offset);
direction = ip_item->ipv6.direction;
}
if (protocol > 65535 || protocol < 0) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s has invalid proto:%d",
ip_schema->table_id, line, protocol);
goto error;
}
if (direction != 0 && direction != 1) {
log_error(logger, MODULE_IP,
"ip table(table_id:%d) line:%s has invalid direction:%d",
ip_schema->table_id, line, direction);
goto error;
}
return ip_item;
error:
FREE(ip_item);
return NULL;
}
void ip_item_free(struct ip_item *ip_item)
{
FREE(ip_item);
}
void ip_item_to_ip_rule(struct ip_item *item, struct ip_rule *rule)
{
struct port_range *sport_range = ALLOC(struct port_range, 1);
if (IPv4 == item->addr_type) {
rule->type = IPv4;
sport_range->min_port = item->ipv4.min_sport;
sport_range->max_port = item->ipv4.max_sport;
rule->ipv4_rule.start_ip = item->ipv4.min_sip;
rule->ipv4_rule.end_ip = item->ipv4.max_sip;
} else {
rule->type = IPv6;
sport_range->min_port = item->ipv6.min_sport;
sport_range->max_port = item->ipv6.max_sport;
memcpy(rule->ipv6_rule.start_ip, item->ipv6.min_sip,
sizeof(item->ipv6.min_sip));
memcpy(rule->ipv6_rule.end_ip, item->ipv6.max_sip,
sizeof(item->ipv6.max_sip));
}
rule->rule_id = item->item_id;
rule->user_tag = sport_range;
}
struct ex_data_runtime *ip_runtime_get_ex_data_rt(struct ip_runtime *ip_rt)
{
return ip_rt->ex_data_rt;
}
int ip_runtime_update_row(struct ip_runtime *rt, char *key, size_t key_len,
struct ip_item *item, int is_valid)
{
int ret = -1;
struct ex_data_runtime *ex_data_rt = rt->ex_data_rt;
if (0 == is_valid) {
// delete
ret = ex_data_runtime_del_ex_container(ex_data_rt, key, key_len);
if (ret < 0) {
return -1;
}
} else {
// add
struct ex_data_container *ex_container = ex_data_container_new(NULL, (void *)item);
ret = ex_data_runtime_add_ex_container(ex_data_rt, key, key_len, ex_container);
if (ret < 0) {
return -1;
}
}
return 0;
}
int ip_runtime_update(void *ip_runtime, void *ip_schema,
const char *line, int valid_column)
{
if (NULL == ip_runtime || NULL == ip_schema || NULL == line) {
return -1;
}
struct maat_item *item = NULL;
struct ip_item *ip_item = NULL;
struct maat_item_inner *u_para = NULL;
struct ip_schema *schema = (struct ip_schema *)ip_schema;
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
int item_id = get_column_value(line, schema->item_id_column);
int is_valid = get_column_value(line, valid_column);
if (is_valid < 0) {
return -1;
} else if (0 == is_valid) {
//delete
HASH_FIND_INT(ip_rt->item_hash, &item_id, item);
if (NULL == item) {
return -1;
}
u_para = (struct maat_item_inner *)item->user_data;
item->user_data = NULL;
if (NULL == u_para) {
return -1;
}
HASH_DELETE(hh, ip_rt->item_hash, item);
maat_garbage_bagging(ip_rt->ref_garbage_bin, u_para,
(void (*)(void *))maat_item_inner_free);
} else {
//add
HASH_FIND_INT(ip_rt->item_hash, &item_id, item);
if (item) {
log_error(ip_rt->logger, MODULE_IP,
"ip runtime add item %d to item_hash failed, already exist",
item_id);
return -1;
}
ip_item = ip_item_new(line, schema, ip_rt->logger);
if (NULL == ip_item) {
return -1;
}
u_para = maat_item_inner_new(ip_item->group_id, item_id, 0);
item = maat_item_new(item_id, ip_item->group_id, u_para);
HASH_ADD_INT(ip_rt->item_hash, item_id, item);
}
char *key = (char *)&item_id;
int ret = ip_runtime_update_row(ip_rt, key, sizeof(int), ip_item, is_valid);
if (ret < 0) {
if (ip_item != NULL) {
ip_item_free(ip_item);
ip_item = NULL;
}
return -1;
} else {
if (0 == is_valid) {
ip_rt->rule_num--;
} else {
ip_rt->rule_num++;
}
}
return 0;
}
int ip_runtime_commit(void *ip_runtime)
{
if (NULL == ip_runtime) {
return -1;
}
int ret = 0;
struct ex_data_container **ex_container = NULL;
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
struct ex_data_runtime *ex_data_rt = ip_rt->ex_data_rt;
size_t rule_cnt = ex_data_runtime_list_updating_ex_container(ex_data_rt, &ex_container);
if (0 == rule_cnt) {
FREE(ex_container);
return 0;
}
struct ip_rule *rules = ALLOC(struct ip_rule, rule_cnt);
for (size_t i = 0; i < rule_cnt; i++) {
struct ip_item *item = (struct ip_item *)ex_container[i]->custom_data;
ip_item_to_ip_rule(item, &rules[i]);
}
struct ip_matcher *new_ip_matcher = NULL;
struct ip_matcher *old_ip_matcher = NULL;
size_t mem_used = 0;
if (rule_cnt > 0) {
log_info(ip_rt->logger, MODULE_IP,
"committing %zu ip rules for rebuilding ip_matcher engine",
rule_cnt);
new_ip_matcher = ip_matcher_new(rules, rule_cnt, &mem_used);
if (NULL == new_ip_matcher) {
log_error(ip_rt->logger, MODULE_IP,
"rebuild ip_matcher engine failed when update %zu ip rules",
rule_cnt);
ret = -1;
}
}
old_ip_matcher = ip_rt->ip_matcher;
ip_rt->ip_matcher = new_ip_matcher;
maat_garbage_bagging(ip_rt->ref_garbage_bin, old_ip_matcher,
(void (*)(void*))ip_matcher_free);
ex_data_runtime_commit(ex_data_rt);
FREE(rules);
FREE(ex_container);
return ret;
}
int ip_runtime_scan_ip(struct ip_runtime *ip_rt, int thread_id, int ip_type,
uint8_t *ip_addr, int *group_ids, size_t group_id_size,
int vtable_id, struct maat_state *state)
{
int n_hit_item = 0;
struct scan_result scan_results[MAX_SCANNER_HIT_ITEM_NUM] = {0};
struct ip_data scan_data;
if (ip_type == IPv4) {
scan_data.type = IPv4;
scan_data.ipv4 = *(uint32_t *)ip_addr;
} else {
scan_data.type = IPv6;
for (int i = 0; i < 4; i++) {
scan_data.ipv6[i] = *((uint32_t *)ip_addr + i);
}
}
n_hit_item = ip_matcher_match(ip_rt->ip_matcher, &scan_data, scan_results, sizeof(scan_result));
if (n_hit_item <= 0) {
return n_hit_item;
}
if (n_hit_item > MAX_SCANNER_HIT_ITEM_NUM) {
log_info(ip_rt->logger, MODULE_IP,
"hit ip item count:%d exceed maxium:%d",
n_hit_item, MAX_SCANNER_HIT_ITEM_NUM);
n_hit_item = MAX_SCANNER_HIT_ITEM_NUM;
}
int hit_item_ids[MAX_SCANNER_HIT_ITEM_NUM] = {-1};
for (int i = 0; i < n_hit_item; i++) {
hit_item_ids[i] = scan_results[i].rule_id;
}
size_t group_hit_cnt = 0;
int ret = maat_compile_state_update(ip_rt->item_hash, vtable_id, hit_item_ids, n_hit_item,
group_ids, group_id_size, &group_hit_cnt, state);
if (ret < 0) {
return -1;
}
return group_hit_cnt;
}
void ip_runtime_scan_hit_inc(struct ip_runtime *ip_rt, int thread_id)
{
alignment_int64_array_add(ip_rt->hit_cnt, thread_id, 1);
}
long long ip_runtime_scan_hit_sum(struct ip_runtime *ip_rt, int n_thread)
{
return alignment_int64_array_sum(ip_rt->hit_cnt, n_thread);
}