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.c
2024-08-09 01:52:13 +00:00

707 lines
21 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.c
* Description:
* Authors: Liu WenTan <liuwentan@geedgenetworks.com>
* Date: 2022-10-31
* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
***********************************************************************************************
*/
#include <stdint.h>
#include <assert.h>
#include "log/log.h"
#include "maat_utils.h"
#include "maat_ex_data.h"
#include "ip_matcher.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 ip_schema {
int item_id_column;
int group_id_column;
int ip_column;
int table_id;
int port_column;
struct table_manager *ref_tbl_mgr;
};
struct ipv4_item_rule {
uint32_t min_ip; /* 源地址下界0表示忽略本字段 */
uint32_t max_ip; /* 源地址上界0表示固定IP=min_addr */
};
struct ipv6_item_rule {
uint32_t min_ip[4]; /* 源地址下界全0表示忽略本字段 */
uint32_t max_ip[4]; /* 源地址上界全0表示固定IP=min_addr */
};
struct ip_item {
long long item_id;
long long group_id;
int addr_type;
union {
struct ipv4_item_rule ipv4;
struct ipv6_item_rule ipv6;
};
int port_start;
int port_end;
};
struct ip_runtime {
struct ip_matcher *ip_matcher;
struct rcu_hash_table *item_hash; // <item_id, struct ip_item>
long long rule_num;
long long ipv6_rule_num;
size_t n_worker_thread;
struct log_handle *logger;
struct maat_garbage_bin *ref_garbage_bin;
long long *scan_times;
long long *scan_cpu_time;
long long *hit_times;
long long *hit_item_num;
long long update_err_cnt;
};
void *ip_schema_new(cJSON *json, struct table_manager *tbl_mgr,
const char *table_name, struct log_handle *logger)
{
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;
} else {
log_fatal(logger, MODULE_IP,
"[%s:%d] ip table:<%s> schema has no table_id column",
__FUNCTION__, __LINE__, table_name);
goto error;
}
item = cJSON_GetObjectItem(json, "custom");
if (NULL == item || item->type != cJSON_Object) {
log_fatal(logger, MODULE_IP,
"[%s:%d] ip table:<%s> schema has no custom column",
__FUNCTION__, __LINE__, 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;
} else {
log_fatal(logger, MODULE_IP,
"[%s:%d] ip table:<%s> schema has no item_id column",
__FUNCTION__, __LINE__, table_name);
goto error;
}
custom_item = cJSON_GetObjectItem(item, "group_id");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->group_id_column = custom_item->valueint;
} else {
log_fatal(logger, MODULE_IP,
"[%s:%d] ip table:<%s> schema has no group_id column",
__FUNCTION__, __LINE__, table_name);
goto error;
}
custom_item = cJSON_GetObjectItem(item, "ip");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->ip_column = custom_item->valueint;
} else {
log_fatal(logger, MODULE_IP,
"[%s:%d] ip table:<%s> schema has no ip column",
__FUNCTION__, __LINE__, table_name);
goto error;
}
custom_item = cJSON_GetObjectItem(item, "port");
if (custom_item != NULL && custom_item->type == cJSON_Number) {
ip_schema->port_column = custom_item->valueint;
} else {
ip_schema->port_column = 0;
}
ip_schema->ref_tbl_mgr = tbl_mgr;
return ip_schema;
error:
FREE(ip_schema);
return NULL;
}
void ip_schema_free(void *ip_schema)
{
FREE(ip_schema);
}
static struct ip_item *
ip_item_new(struct ip_schema *ip_schema, const char *table_name,
const char *line, struct log_handle *logger)
{
size_t column_offset = 0;
size_t column_len = 0;
char ip_str[128] = {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_fatal(logger, MODULE_IP,
"[%s:%d] ip table:<%s> has no item_id in line:%s",
__FUNCTION__, __LINE__, table_name, line);
goto error;
}
ip_item->item_id = atoll(line + column_offset);
ret = get_column_pos(line, ip_schema->group_id_column, &column_offset,
&column_len);
if (ret < 0) {
log_fatal(logger, MODULE_IP,
"[%s:%d] ip table:<%s> has no group_id in line:%s",
__FUNCTION__, __LINE__, table_name, line);
goto error;
}
ip_item->group_id = atoll(line + column_offset);
ret = get_column_pos(line, ip_schema->ip_column, &column_offset,
&column_len);
if (ret < 0) {
log_fatal(logger, MODULE_IP,
"[%s:%d] ip table:<%s> has no ip1 in line:%s",
__FUNCTION__, __LINE__, table_name, line);
goto error;
}
memcpy(ip_str, (line + column_offset), column_len);
if (strchr(ip_str, ':') != NULL) {
ip_item->addr_type = IPV6;
} else {
ip_item->addr_type = IPV4;
}
if (IPv4 == ip_item->addr_type) {
ret = ip_format2range(ip_str, ip_item->addr_type, &ip_item->ipv4.min_ip, &ip_item->ipv4.max_ip);
if (ret < 0) {
log_fatal(logger, MODULE_IP,
"[%s:%d] ip table:<%s> ip_format2range(ip4) failed in line:%s",
__FUNCTION__, __LINE__, table_name, line);
goto error;
}
} else {
//ipv6
ret = ip_format2range(ip_str, ip_item->addr_type, ip_item->ipv6.min_ip, ip_item->ipv6.max_ip);
if (ret < 0) {
log_fatal(logger, MODULE_IP,
"[%s:%d] ip table:<%s> ip_format2range(ip6) failed in line:%s",
__FUNCTION__, __LINE__, table_name, line);
goto error;
}
}
if(ip_schema->port_column>0)
{
ret = get_column_pos(line, ip_schema->port_column, &column_offset,
&column_len);
if (ret < 0) {
log_fatal(logger, MODULE_IP,
"[%s:%d] ip table:<%s> has no port in line:%s",
__FUNCTION__, __LINE__, table_name, line);
goto error;
}
char port_range[20] = {0};
memcpy(port_range, (line + column_offset), column_len);
//port range is port or port_start-port_end
if(strchr(port_range,'-')!=NULL){
char *saveptr = NULL;
char *port_start = strtok_r(port_range,"-", &saveptr);
char *port_end = strtok_r(NULL,"-", &saveptr);
ip_item->port_start = atoi(port_start);
ip_item->port_end = atoi(port_end);
} else {
ip_item->port_start = atoi(port_range);
ip_item->port_end = atoi(port_range);
}
} else {
ip_item->port_start = 0;
ip_item->port_end = 65535;
}
return ip_item;
error:
FREE(ip_item);
return NULL;
}
static void ip_item_free(struct ip_item *item)
{
if (NULL == item) {
return;
}
FREE(item);
}
static void ip_item_free_cb(void *user_ctx, void *data)
{
struct ip_item *item = (struct ip_item *)data;
ip_item_free(item);
}
void *ip_runtime_new(void *ip_schema, size_t max_thread_num,
struct maat_garbage_bin *garbage_bin,
struct log_handle *logger)
{
if (NULL == ip_schema) {
return NULL;
}
struct ip_runtime *ip_rt = ALLOC(struct ip_runtime, 1);
ip_rt->item_hash = rcu_hash_new(ip_item_free_cb, NULL, 0);
ip_rt->n_worker_thread = max_thread_num;
ip_rt->ref_garbage_bin = garbage_bin;
ip_rt->logger = logger;
ip_rt->scan_times = alignment_int64_array_alloc(max_thread_num);
ip_rt->scan_cpu_time = alignment_int64_array_alloc(max_thread_num);
ip_rt->hit_times = alignment_int64_array_alloc(max_thread_num);
ip_rt->hit_item_num = 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);
ip_rt->ip_matcher = NULL;
}
if (ip_rt->item_hash != NULL) {
rcu_hash_free(ip_rt->item_hash);
ip_rt->item_hash = NULL;
}
if (ip_rt->scan_times != NULL) {
alignment_int64_array_free(ip_rt->scan_times);
ip_rt->scan_times = NULL;
}
if (ip_rt->scan_cpu_time != NULL) {
alignment_int64_array_free(ip_rt->scan_cpu_time);
ip_rt->scan_cpu_time = NULL;
}
if (ip_rt->hit_times != NULL) {
alignment_int64_array_free(ip_rt->hit_times);
ip_rt->hit_times = NULL;
}
if (ip_rt->hit_item_num != NULL) {
alignment_int64_array_free(ip_rt->hit_item_num);
ip_rt->hit_item_num = NULL;
}
FREE(ip_rt);
}
static void ip_item_to_ip_rule(struct ip_item *item, struct ip_rule *rule)
{
if (IPv4 == item->addr_type) {
rule->type = IPv4;
rule->ipv4_rule.start_ip = item->ipv4.min_ip;
rule->ipv4_rule.end_ip = item->ipv4.max_ip;
} else {
rule->type = IPv6;
memcpy(rule->ipv6_rule.start_ip, item->ipv6.min_ip,
sizeof(item->ipv6.min_ip));
memcpy(rule->ipv6_rule.end_ip, item->ipv6.max_ip,
sizeof(item->ipv6.max_ip));
}
rule->rule_id = item->item_id;
}
static int ip_runtime_update_row(struct ip_runtime *ip_rt, char *key, size_t key_len,
struct ip_item *item, int is_valid)
{
int ret = -1;
if (0 == is_valid) {
// delete
rcu_hash_del(ip_rt->item_hash, key, key_len);
} else {
// add
ret = rcu_hash_add(ip_rt->item_hash, key, key_len, (void *)item);
if (ret < 0) {
log_debug(ip_rt->logger, MODULE_IP,
"[%s:%d] ip item(item_id:%lld) add to ip runtime htable failed",
__FUNCTION__, __LINE__, item->item_id);
return -1;
}
}
return 0;
}
int ip_runtime_update(void *ip_runtime, void *ip_schema,
const char *table_name, const char *line,
int valid_column)
{
if (NULL == ip_runtime || NULL == ip_schema || NULL == line) {
return -1;
}
struct ip_schema *schema = (struct ip_schema *)ip_schema;
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
long long item_id = get_column_value(line, schema->item_id_column);
if (item_id < 0) {
log_fatal(ip_rt->logger, MODULE_IP,
"[%s:%d] ip table:<%s> has no item_id(column seq:%d)"
" in table_line:%s", __FUNCTION__, __LINE__, table_name,
schema->item_id_column, line);
ip_rt->update_err_cnt++;
return -1;
}
int is_valid = get_column_value(line, valid_column);
if (is_valid < 0) {
log_fatal(ip_rt->logger, MODULE_IP,
"[%s:%d] ip table:<%s> has no is_valid(column seq:%d)"
" in table_line:%s", __FUNCTION__, __LINE__, table_name,
valid_column, line);
ip_rt->update_err_cnt++;
return -1;
}
struct ip_item *ip_item = NULL;
if (1 == is_valid) {
//add
ip_item = ip_item_new(schema, table_name, line, ip_rt->logger);
if (NULL == ip_item) {
ip_rt->update_err_cnt++;
return -1;
}
}
int ret = ip_runtime_update_row(ip_rt, (char *)&item_id, sizeof(long long),
ip_item, is_valid);
if (ret < 0) {
if (ip_item != NULL) {
ip_item_free(ip_item);
}
//don't return failed, ignore the case of adding duplicate keys
}
return 0;
}
void garbage_ip_matcher_free(void *ip_matcher, void *arg)
{
struct ip_matcher *matcher = (struct ip_matcher *)ip_matcher;
ip_matcher_free(matcher);
}
int ip_runtime_commit(void *ip_runtime, const char *table_name,
long long maat_rt_version)
{
if (NULL == ip_runtime) {
return -1;
}
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
int updating_flag = rcu_hash_is_updating(ip_rt->item_hash);
if (0 == updating_flag) {
return 0;
}
ip_rt->ipv6_rule_num = 0;
struct ip_rule *rules = NULL;
void **ex_data_array = NULL;
size_t rule_cnt = rcu_updating_hash_list(ip_rt->item_hash, &ex_data_array);
if (rule_cnt > 0) {
rules = ALLOC(struct ip_rule, rule_cnt);
for (size_t i = 0; i < rule_cnt; i++) {
struct ip_item *item = (struct ip_item *)ex_data_array[i];
if (item->addr_type == IPv6) {
ip_rt->ipv6_rule_num++;
}
ip_item_to_ip_rule(item, &rules[i]);
}
}
int ret = 0;
size_t mem_used = 0;
struct ip_matcher *new_ip_matcher = NULL;
struct ip_matcher *old_ip_matcher = NULL;
if (rule_cnt > 0) {
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
new_ip_matcher = ip_matcher_new(rules, rule_cnt, &mem_used);
clock_gettime(CLOCK_MONOTONIC, &end);
long long time_elapse_ms = (end.tv_sec - start.tv_sec) * 1000 +
(end.tv_nsec - start.tv_nsec) / 1000000;
if (NULL == new_ip_matcher) {
log_fatal(ip_rt->logger, MODULE_IP,
"[%s:%d] table[%s] rebuild ip_matcher engine failed "
"when update %zu ip rules", __FUNCTION__, __LINE__,
table_name, rule_cnt);
ret = -1;
} else {
log_info(ip_rt->logger, MODULE_IP,
"table[%s] commit %zu ip rules and rebuild ip_matcher completed"
", version:%lld, consume:%lldms", table_name, rule_cnt,
maat_rt_version, time_elapse_ms);
}
}
old_ip_matcher = ip_rt->ip_matcher;
ip_rt->ip_matcher = new_ip_matcher;
rcu_hash_commit(ip_rt->item_hash);
if (old_ip_matcher != NULL) {
maat_garbage_bagging(ip_rt->ref_garbage_bin, old_ip_matcher, NULL,
garbage_ip_matcher_free);
}
ip_rt->rule_num = rule_cnt;
if (rules != NULL) {
FREE(rules);
}
if (ex_data_array != NULL) {
FREE(ex_data_array);
}
return ret;
}
long long ip_runtime_rule_count(void *ip_runtime)
{
if (NULL == ip_runtime) {
return 0;
}
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
return ip_rt->rule_num;
}
long long ip_runtime_ipv6_rule_count(void *ip_runtime)
{
if (NULL == ip_runtime) {
return 0;
}
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
return ip_rt->ipv6_rule_num;
}
int ip_runtime_scan(struct ip_runtime *ip_rt, int thread_id, int ip_type,
uint8_t *ip_addr, int port, int vtable_id, struct maat_state *state)
{
//clear compile_state->last_hit_group
if (state != NULL && state->compile_state != NULL) {
compile_state_clear_last_hit_group(state->compile_state);
}
if (0 == ip_rt->rule_num) {
//empty ip table
return 0;
}
if (NULL == ip_rt->ip_matcher) {
return 0;
}
struct ip_data scan_data;
struct scan_result ip_results[MAX_HIT_ITEM_NUM];
if (ip_type == IPv4) {
scan_data.type = IPv4;
scan_data.ipv4 = ntohl(*(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);
}
ipv6_ntoh(scan_data.ipv6);
}
size_t real_hit_item_cnt = 0;
struct maat_item hit_maat_items[MAX_HIT_ITEM_NUM];
int n_hit_ip_item = ip_matcher_match(ip_rt->ip_matcher, &scan_data,
ip_results, MAX_HIT_ITEM_NUM);
if (n_hit_ip_item < 0) {
return -1;
}
if (0 == n_hit_ip_item) {
goto next;
}
for (size_t i = 0; i < n_hit_ip_item; i++) {
long long item_id = ip_results[i].rule_id;
struct ip_item *ip_item = (struct ip_item *)rcu_hash_find(ip_rt->item_hash,
(char *)&item_id,
sizeof(long long));
if (!ip_item) {
// item config has been deleted
continue;
}
if (port < 0 && ip_item->port_start != 0 && ip_item->port_end != 65535) {
//If port is not speicified, an IP should NOT match rules with port range.
continue;
}
if (port >= 0 && (port < ip_item->port_start || port > ip_item->port_end)) {
//If port is specified, the port should within the port range.
continue;
}
hit_maat_items[real_hit_item_cnt].item_id = ip_results[i].rule_id;
hit_maat_items[real_hit_item_cnt].group_id = ip_item->group_id;
real_hit_item_cnt++;
}
if (real_hit_item_cnt > 0) {
alignment_int64_array_add(ip_rt->hit_item_num, state->thread_id,
real_hit_item_cnt);
}
next:
if (NULL == state->compile_state) {
state->compile_state = compile_state_new();
alignment_int64_array_add(state->maat_inst->stat->compile_state_cnt,
state->thread_id, 1);
}
return compile_state_update(state->compile_state, state->maat_inst, vtable_id,
state->compile_table_id, state->Nth_scan,
hit_maat_items, real_hit_item_cnt);
}
void ip_runtime_perf_stat(struct ip_runtime *ip_rt, struct timespec *start,
struct timespec *end, int thread_id)
{
if (NULL == ip_rt || thread_id < 0) {
return;
}
if (start != NULL && end != NULL) {
long long consume_time = (end->tv_sec - start->tv_sec) * 1000000000 +
(end->tv_nsec - start->tv_nsec);
alignment_int64_array_add(ip_rt->scan_cpu_time, thread_id, consume_time);
}
}
void ip_runtime_scan_times_inc(struct ip_runtime *ip_rt, int thread_id)
{
if (NULL == ip_rt || thread_id < 0) {
return;
}
alignment_int64_array_add(ip_rt->scan_times, thread_id, 1);
}
long long ip_runtime_scan_times(void *ip_runtime)
{
if (NULL == ip_runtime) {
return 0;
}
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
long long sum = alignment_int64_array_sum(ip_rt->scan_times,
ip_rt->n_worker_thread);
alignment_int64_array_reset(ip_rt->scan_times,
ip_rt->n_worker_thread);
return sum;
}
long long ip_runtime_scan_cpu_time(void *ip_runtime)
{
if (NULL == ip_runtime) {
return 0;
}
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
long long sum = alignment_int64_array_sum(ip_rt->scan_cpu_time,
ip_rt->n_worker_thread);
alignment_int64_array_reset(ip_rt->scan_cpu_time, ip_rt->n_worker_thread);
return sum;
}
void ip_runtime_hit_times_inc(struct ip_runtime *ip_rt, int thread_id)
{
if (NULL == ip_rt || thread_id < 0) {
return;
}
alignment_int64_array_add(ip_rt->hit_times, thread_id, 1);
}
long long ip_runtime_hit_times(void *ip_runtime)
{
if (NULL == ip_runtime) {
return 0;
}
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
long long sum = alignment_int64_array_sum(ip_rt->hit_times,
ip_rt->n_worker_thread);
alignment_int64_array_reset(ip_rt->hit_times,
ip_rt->n_worker_thread);
return sum;
}
long long ip_runtime_hit_item_num(void *ip_runtime)
{
if (NULL == ip_runtime) {
return 0;
}
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
long long sum = alignment_int64_array_sum(ip_rt->hit_item_num,
ip_rt->n_worker_thread);
alignment_int64_array_reset(ip_rt->hit_item_num,
ip_rt->n_worker_thread);
return sum;
}
long long ip_runtime_update_err_count(void *ip_runtime)
{
if (NULL == ip_runtime) {
return 0;
}
struct ip_runtime *ip_rt = (struct ip_runtime *)ip_runtime;
return ip_rt->update_err_cnt;
}