secgateway/Platform/user/configm/config-server/nat_config/config.c

1305 lines
38 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.

//
// config.c
//
// Created by sunguosong on 2019/8/13.
//
#include "config.h"
/*==============================================================================*
* TEST-DATA |
*==============================================================================*/
// 测试数据iptables-save 配置文件路径
static const char *__iptables_conf_dir = "./iptables-conf.txt";
// 测试数据json
static const char *__json = "{ \"config_type\":3, \"content\":[ \
{\"action\":1,\"i_device\":\"ens33\",\"source\":\"192.168.12.12\",\
\"target\":\"SNAT\",\"to\":\"172.18.2.72\"}, \
{ \
\"action\": 1,\
\"target\": \"SNAT\", \
\"prot\": \"udp\", \
\"source\": \"192.168.80.0/24\", \
\"destination\": \"\", \
\"dport\": \"\", \
\"to\": \"10.10.10.1:450700-45100\", \
\"i_device\": \"\", \
\"o_device\": \"ens33\", \
\"match\": \"\", \
\"match_info\": \"\" \
}, \
{ \
\"action\": 1,\
\"target\": \"SNAT\", \
\"to\": \"10.10.10.1\", \
\"i_device\": \"\", \
\"o_device\": \"\", \
\"match\": \"mark\", \
\"match_info\": \"0x64\" \
}, \
{ \
\"action\": 0,\
\"target\": \"SNAT\", \
\"id\": 100 \
}, \
{ \
\"action\": 0,\
\"target\": \"SNAT\", \
\"id\": 1 \
}, \
{ \
\"action\": 0,\
\"target\": \"SNAT\", \
\"id\": 1 \
}, \
{ \
\"action\": 0,\
\"target\": \"SNAT\", \
\"id\": 1 \
}, \
{ \
\"action\": 0,\
\"target\": \"SNAT\", \
\"id\": 1 \
}\
]}";
/*==============================================================================*
* MACRO |
*==============================================================================*/
#define FILE_FGETS(BUF, MAX_LEN, FP) while(fgets(BUF, MAX_LEN, FP) != NULL)
#define FILL_IPT_OPTION(OPTION, MAX) \
strncpy(conf->OPTION, *(_argv + _optind++), (MAX) - 1)
#define CMP_IPT_OPTION(OPTION) \
{ if (strcmp(condition->OPTION, "") != 0 && \
strcmp(condition->OPTION, _argv[_optind]) != 0) { \
*match = FAIL; goto pass;} \
}
#define isempty_str(ptr) (ptr[0] == '\0') // 字符串判空
#define isempty_int(v) (v == 0x7fffffff) // int数据未赋值判别
#define add_space(s) strncat(s, " ", 1)
#define is_snat(ptr) (strcmp(ptr->target, "SNAT") == 0)
#define is_dnat(ptr) (strcmp(ptr->target, "DNAT") == 0)
#define is_masquerade(ptr) (strcmp(ptr->target, "MASQUERADE") == 0)
#define set_chain(ptr, c) strcpy(ptr->chain, c)
#define get_chain(ptr) (ptr->chain)
#define get_id(ptr) (ptr->id)
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
/*
* fopen的包裹包含异常处理
* parameters:
* @__filename: fopen文件路径
* @__mode: fopen打开模式
*/
#define Fopen(fp, __filename, __mode, msg) do { \
fp = fopen(__filename, __mode); \
if (fp == NULL) { \
__error_report(msg, -1, "Can't open the iptables-save file!\n"); \
return NULL; \
} \
} while (0)
/*
* cJSON_Parse的包裹将字符串形式的json转化为cJSON结构包含异常处理
* parameters:
* @json: 字符串形式的json
* @value: 字符串形式的json数据
* @msg: 信息返回
*/
#define cJSON_Parse_e(json, value, msg) do { \
json = cJSON_Parse(value); \
if (!json) { \
__error_report(msg, -1, "Error before: [%s]\n", cJSON_GetErrorPtr()); \
return NULL; \
} \
} while (0)
#define _IPT_STR_ITEM_TOJSON(CE, E) do { \
if (*(conf->CE) != 0) \
cJSON_AddStringToObject(obj, #E, conf->CE); \
/* else cJSON_AddNullToObject(obj, #E); */ \
} while (0)
#define _IPT_INT_ITEM_TOJSON(CE, E) do { \
if (conf->CE != 0) \
cJSON_AddNumberToObject(obj, #E, conf->CE); \
/* else cJSON_AddNullToObject(obj, #E); */ \
} while (0)
#define _JSON_STR_TO_IPT_ITEM(E, CE, MAX) do { \
cJSON *__json = cJSON_GetObjectItem(json, #E); \
if (__json) { \
if (strlen(__json->valuestring) >= MAX) {*ret = RET_INPUTERR; goto err;} \
else strcpy(conf->CE, __json->valuestring); \
} \
else strcpy(conf->CE, ""); \
} while (0)
#define _JSON_INT_TO_IPT_ITEM(E) do { \
cJSON *__json = cJSON_GetObjectItem(json, #E); \
if (__json) conf->E = __json->valueint; \
else conf->E = 0x7fffffff;\
} while (0)
// 宏: str拼接到字符串并防止拼接越界
#define strcat_str_free_count(des, src) do { \
int len = strlen(src); \
if (free_space <= len) {ret = RET_NOMEM; goto err;} \
else { \
strncat(des, src, free_space-1); \
free_space -= len; \
} \
} while (0)
// 宏: int拼接到字符串并防止拼接越界
#define strcat_int_free_count(des, src) do { \
char s[21]; \
if (src == -1) s[0] = '\0'; else itoa(src, s); \
int len = strlen(s); \
if (free_space <= len) {ret = RET_NOMEM; goto err;} \
else { \
strncat(des, s, free_space-1); \
free_space -= len; \
} \
} while (0)
/*
* 宏: 命令拼接
* @rule: 已完成拼接命令
* @E: 待拼接字段
* @EX: 拓展拼接字符串
* @type: 带拼接字段类型可选strint
*/
#define splice_item(rule, E, CE, EX, type) do { \
enum ipt_config_item item = E; \
if (!isempty_##type(conf->CE)) { \
strcat_str_free_count(rule, __prefix[item]); \
if (!isempty_str(EX)) { \
strcat_str_free_count(rule, EX); \
strcat_str_free_count(rule, " "); \
} \
strcat_##type##_free_count(rule, conf->CE); \
} \
} while (0)
/*
* malloc宏异常信息处理通过chk_malloc_oom_handlergoto MEMerr
* parameters:
* @ptr 待分配指针
* @struct: 指针结构类型
* @size 分配空间字节数
* @ret: ret_code返回码
* @msg: 信息返回
*/
#define __chk_malloc(ptr, struct, size, ret, msg) do { \
ptr = (struct)malloc(size); \
if(!(ptr)) { \
if(msg) chk_malloc_oom_handler(size, msg);\
ret = RET_NOMEM; \
goto MEMerr; \
} \
} while (0)
#define xfree(X) do{ \
if(X) { \
free(X); \
X = NULL; \
} \
} while(0)
/*==============================================================================*
* VARIABLES |
*==============================================================================*/
// 全局返回信息最大长度
static int free_space = MAX_ERR_MSG;
// 当前执行的命令id
static int current_id = -1;
typedef void *(*jsonfunc)(cJSON *json_arr, int *output_len, ret_code *ret, char **msg);
// iptables 命令参数拼接前缀
static char * __prefix[IPT_PARANUM] = {
" -D ",
" -I ",
" -A ",
" -s ",
" -d ",
" -p ",
" --sport ",
" --dport ",
" -i ",
" -o ",
" -m ",
" --",
" -j ",
" --to-"
};
static char *__field_match[NF_PARANUM] = {
"src=",
"dst=",
"sport=",
"dport=",
"src=",
"dst=",
"sport=",
"dport=",
};
// json字段枚举action不参与命令拼接
enum ipt_config_item {
del, insert, chain, source, destination, prot, sport, dport, i_device,
o_device, match, match_info, target, to
};
// set配置枚举
enum action {
DELETE, SET, SAVE, RESTORE, UNKNOWN
};
/*==============================================================================*
* PRIVATE-API |
*==============================================================================*/
// 从iptables-save配置文件读取iptables中nat的配置信息,可指定范围
iptables_rule *__read_iptables_conf(const char *filename, int *output_len, int begin,
int offset, ret_code *ret, char **msg);
// 使用一条ipt_config结构配置信息拼接成linux命令
ret_code __command_splice(struct ipt_config *conf, iptables_rule rule, char **msg);
// 按顺序解析参数序列中的参数标记
uchar __extract_parm(int *_optind, const int _argc, char **_argv);
// 将字符串按空格(space)拆分成字符串指针,可处理连续空格
char **__str2str_array(const char *str, int *output_len);
// 将字符串按指定分隔符拆分成字符串指针,可处理连续分隔符
char** split(const char * const targetString, const char * const delimiter,int * const length);
// 将int整数转化成char*字符串
void itoa(int a, char s[]);
// ipt_config结构的配置信息合法性检查完成缺省配置推导
boolean __iptconfig_chk(struct ipt_config *conf, char **msg);
// 将一条ipt_config类型的配置信息转化为json数据
ret_code __ipt_config_tojson(cJSON *root, const struct ipt_config *conf);
// 将字符串格式的json配置信息解析为多种数据类型
void *__jsontxt_to_struct(const char *json_text, int *output_len, jsonfunc func,
ret_code *ret, char **msg);
// 将nat模块使用的cjson配置信息解析为ipt_config类型
void *__cjsonarr_to_ipt_config(cJSON *json_arr, int *output_len,
ret_code *ret, char **msg);
// 将nat模块使用的cjson配置信息解析为range_ipt_config类型
void *__cjsonarr_to_range_ipt_config(cJSON *json_arr, int *output_len,
ret_code *ret, char **msg);
// 解析ipt_config的action字段
enum action __parse_action(const struct ipt_config * const conf);
// 解析一条字符串形式的nat配置信息,并与规则进行匹配
ret_code __ipt_conf_parse(const iptables_rule rule, const struct ipt_config *condition,
boolean *match, struct ipt_config *conf);
// 从/proc/net/nf_conntrack中读取连接跟踪信息,暂不使用
char **__read_nf_conntrack(const char *filename, int *output_len,
ret_code *ret, char **msg);
// 连接跟踪信息解析,暂不使用,未完成
ret_code __nf_conntrack_parse(const char *conn, nf_conntrack *conf);
// 异常处理函数
char *__error_report(char** msg, int id, char *fmt, ...)
__attribute__((format(printf, 3, 4)));
// 字符串指针char**)释放函数
void __free_dptr_char(char ***ptr, int len);
/*==============================================================================*
* FUNCTION |
*==============================================================================*/
/*
* 异常处理函数
* parameters:
* @msg: 异常信息返回
* @id: 命令序号
* @fmt, ...
*
* return: 错误信息字符串
*/
char* __error_report (char **msg, int id, char *fmt, ...) {
if (*msg == NULL) return NULL;
char _msg_head[MAX_LINE_LEN - 1];
char _msg_tail[MAX_LINE_LEN - 30];
va_list va;
// 使用宏strcat_str_free_count不返回
ret_code ret;
va_start(va, fmt);
vsnprintf(_msg_tail, MAX_LINE_LEN - 30, fmt, va);
va_end(va);
snprintf(_msg_head, MAX_LINE_LEN - 1, "[err] JSON id: %03d, msg:", id);
strcat_str_free_count(_msg_head, _msg_tail);
if (_msg_head[strlen(_msg_head) - 1] != '\n') {
strcat_str_free_count(_msg_head, "\n");
}
strcat_str_free_count(*msg, _msg_head);
printf("%s\n", *msg);
err:
return NULL;
}
/*
* 将int整数转化成char*字符串
* parameters:
* @a: 待转化int类型整数
* @s: 返回转化后字符串数组
*
* return: void
*/
void itoa(int a, char s[]) {
snprintf(s, 20, "%d", a);
}
static void chk_malloc_default_oom(size_t size, char **msg) {
__error_report(msg, -1, "malloc: out of memory trying to allocate %zu bytes\n", size);
//abort();
}
static void(*chk_malloc_oom_handler)(size_t, char**) = chk_malloc_default_oom;
/*
* 字符串指针char**)释放函数
* parameters:
* @ptr: 待释放字符串指针
* @len: 所包含一维指针个数
*
* return: 成功则返回void*
*/
void __free_dptr_char(char ***ptr, int len) {
int i = 0;
if (*ptr == NULL) return;
for ( ; i < len; ++i) xfree((*ptr)[i]);
xfree(*ptr);
}
/*
* linux系统命令执行函数
* parameters:
* @cmd char*形式的linux命令
* @id: 命令执行序号
* @msg: 信息返回
*
* return: ret_code返回码
*/
ret_code run_command( char *const cmd, char **msg) {
char* result = NULL;
FILE *fpRead = NULL;
ret_code ret = RET_OK;
char buf[MAX_LINE_LEN] = {0};
//char result[MAX_LINE_LEN] = {0};
if(!cmd){
goto pass;
}
__chk_malloc(result, char*, MAX_LINE_LEN * sizeof(char), ret, msg);
result = (char *)malloc(MAX_LINE_LEN);
if(!result){
goto pass;
}
//printf("1cmd: addr is %x, content is %s\n", cmd, cmd);
//printf("1result: addr is %x, content is %s\n", result, result);
memset(result, 0, MAX_LINE_LEN);
printf("%s\n", cmd);
// 执行cmd命令fpRead指向命令执行后的shell信息
fpRead = popen(cmd, "r");
if (fpRead == NULL) goto pass;
FILE_FGETS(buf, MAX_LINE_LEN - 1,fpRead) {
strncat(result, buf, MAX_LINE_LEN - 1);
// 只返回第一行错误信息
break;
}
//printf("2cmd: addr is %x, content is %s\n", cmd, cmd);
//printf("2result: addr is %x, content is %s\n", result, result);
if (!isempty_str(result)) {
__error_report(msg, current_id, "%s", result);
ret = RET_ERR;
goto pass;
}
MEMerr:
pass:
if(fpRead!=NULL)
pclose(fpRead);
xfree(result);
return ret;
}
/*
* 从iptables-save配置文件读取iptables中nat的配置信息,可指定范围
* parameters:
* @filename: iptables-save配置文件路径
* @output_len: 返回值中nat配置信息条目数无需初始化
* @ret: ret_code返回码
* @begin: 指定nat配置开始位置
* @offset: 指定nat配置结束位置,相对begin偏移量;若为-1,则读取到nat配置结束
* @msg: 信息返回
*
* return: 包含nat配置信息的二级指针
*/
iptables_rule *__read_iptables_conf(const char *filename, int *output_len, int begin,
int offset, ret_code *ret, char **msg) {
FILE *fp = NULL;
char buf[MAX_LINE_LEN];
int nat_start_flag = 0, nat_offset = -1;
int lines = 0, count = 0, i = 0;
iptables_rule *__rules = NULL;
*output_len = 0;
Fopen(fp, filename, "r", msg);
FILE_FGETS(buf, MAX_LINE_LEN - 1,fp) {
if (strncmp("*nat", buf, 4) == 0) {
nat_start_flag = 1;
break;
}
}
// 定位nat配置信息在iptables-save文件中的起止位置获取配置条目数count
if (nat_offset == -1) nat_offset = ftell(fp);
if (nat_start_flag == 0) {
fclose(fp);
return NULL;
}
else { // 定位nat配置结束位置
FILE_FGETS(buf, MAX_LINE_LEN - 1,fp) {
if (strncmp("COMMIT", buf, 6) == 0) break;
count += 1;
}
}
if (offset < 0) offset = count;
*output_len = max(min(count - begin, offset), 0);
if (*output_len == 0) goto pass;
__chk_malloc(__rules, iptables_rule*, sizeof(iptables_rule) * (*output_len), *ret, msg);
// FILE指针重置到nat部分起始位置
fseek(fp, nat_offset, SEEK_SET);
FILE_FGETS(buf, MAX_LINE_LEN - 1, fp) {
if (i == count || i == begin + offset) break;
if (i < begin) continue;
__chk_malloc(__rules[i], iptables_rule, sizeof(char) * MAX_LINE_LEN, *ret, msg);
memset(__rules[i], 0, sizeof(char)*MAX_LINE_LEN);
strncpy(__rules[i], buf, MAX_LINE_LEN - 1);
i += 1;
}
if(fp!=NULL)
fclose(fp);
return __rules;
MEMerr:
__free_dptr_char(&__rules, *output_len);
*output_len = 0;
pass:
if(fp!=NULL)
fclose(fp);
return NULL;
}
/*
* 从/proc/net/nf_conntrack中读取连接跟踪信息,暂不使用
* parameters:
* @filename: iptables-save配置文件路径
* @output_len: 返回值中nat配置信息条目数无需初始化
* @ret: ret_code返回码
* @msg: 信息返回
*
* return: 包含连接跟踪信息的二级指针
*/
char **__read_nf_conntrack(const char *filename, int *output_len,
ret_code *ret, char **msg) {
FILE *fp = NULL;
int count = 0, i = 0;
char ch, buf[MAX_LINE_LEN];
char **result = NULL;
Fopen(fp, filename, "r", msg);
while((ch = fgetc(fp)) != EOF) {
if(ch == '\n') count++;
}
__chk_malloc(result, char**, sizeof(char*) * count, *ret, msg);
fseek(fp, 0, SEEK_SET);
FILE_FGETS(buf, MAX_LINE_LEN - 1, fp) {
__chk_malloc(result[i], char*, MAX_LINE_LEN, *ret, msg);
memset(result[i], 0, MAX_LINE_LEN);
strncpy(result[i], buf, MAX_LINE_LEN - 1);
if (++i == count) break;
}
*output_len = count;
return result;
MEMerr:
__free_dptr_char(&result, count);
if(fp!=NULL)
fclose(fp);
return NULL;
}
/*
* 将字符串按空格(space)拆分成字符串指针,可处理连续空格
* parameters:
* @str: 待拆分字符串
* @output_len: 拆分后的字符串数目初始化0
*/
char **__str2str_array(const char *str, int *output_len) {
char * ch1 = "\n";
char * ch2 = " ";
int length1 = 0;
int length2 = 0;
char **result1 = split(str, ch1, &length1);
if (!result1) return NULL;
char **result2 = split(result1[0], ch2, &length2);
*output_len = length2;
__free_dptr_char(&result1, length1);
return result2;
}
/*
* 将字符串按指定分隔符拆分成字符串指针,可处理连续分隔符
* parameters:
* @targetString: 待拆分字符串
* @delimiter: 指定分隔符
* @length: 拆分后的字符串数目初始化0
*/
char **split(const char * const targetString, const char * const delimiter,
int * const length){
*length = 0;
char ** resultString = NULL;
char inputString[strlen(targetString)];
strcpy(inputString,targetString);
char inputDelimiter[strlen(delimiter)];
strcpy(inputDelimiter,delimiter);
char * splitedString = strtok(inputString, inputDelimiter);
while (splitedString){
(*length)++;
char *resultChar = NULL;
// 不使用__chk_malloc将报错延迟到命令解析函数
resultChar = malloc(strlen(splitedString) + 1);
if (!resultChar) {
__free_dptr_char(&resultString, *length - 1);
return NULL;
}
strcpy(resultChar,splitedString);
resultString = realloc(resultString, (*length) * sizeof(char *));
if (!resultString) {
__free_dptr_char(&resultString, *length);
return NULL;
}
resultString[(*length) - 1] = resultChar;
splitedString = strtok(NULL, delimiter);
}
return resultString;
}
/*
* 按顺序解析参数序列中的参数标记
* parameters:
* @_optind: 正在处理的参数在参数序列中的位置, 函数返回前该值+1指向下一参数
* @argc: 参数数目
* @argv: 参数序列指针
*
* return: 返回unsigned char类型的唯一参数标记
*/
uchar __extract_parm(int *_optind, const int _argc, char **_argv) {
char *_locate_2 = NULL;
// 当前参数位置超过参数序列中参数数目
if (*_optind >= _argc) return FAIL;
if (**(_argv + *_optind) == '-') {
//参数标记前只有一个“-”
if(*(*(_argv + *_optind) + 1) != '-') {
return *(*(_argv + (*_optind)++) + 1);
}
// 遇到两个连续的“--”
else {
_locate_2 = *(_argv + (*_optind)++) + 2;
// 非单字符参数标记重新映射
if (strcmp(_locate_2, "to-destination") == 0 || \
strcmp(_locate_2, "to-source") == 0) return 128;
if (strcmp(_locate_2, "sport") == 0) return 129;
if (strcmp(_locate_2, "dport") == 0) return 130;
if (strcmp(_locate_2, "mark") == 0) return 131;
else return FAIL;
}
}
else return FAIL;
}
/*
* 解析一条字符串形式的nat配置信息,并与规则进行匹配
* parameters:
* @rule: 一条字符串形式的nat配置信息
* @condition: 匹配规则
* @match: 若与condition匹配成功,则赋值为success,否则fail
* @conf: 返回指向解析结果的ipt_config指针,需要预分配空间
*
* return: ret_code返回码
*/
ret_code __ipt_conf_parse(const iptables_rule rule, const struct ipt_config *condition,
boolean *match, struct ipt_config *conf) {
int _optind = 0, _argc;
uchar option;
ret_code ret = RET_OK;
// 将字符串格式的配置信息分解为参数序列
// argv: 参数序列argc: argv中参数个数
char **_argv = __str2str_array(rule, &_argc);
if (!_argv) {
ret = RET_NOMEM;
goto MEMerr;
}
*match = SUCCESS;
while(option = __extract_parm(&_optind, _argc, _argv)) {
if (_optind >= _argc) break;
switch(option) {
case 'A':
CMP_IPT_OPTION(chain);
FILL_IPT_OPTION(chain, MAX_CHAIN);
break;
case 'd':
CMP_IPT_OPTION(destination);
FILL_IPT_OPTION(destination, MAX_ADDR);
break;
case 's':
CMP_IPT_OPTION(source);
FILL_IPT_OPTION(source, MAX_ADDR);
break;
case 'i':
CMP_IPT_OPTION(i_device);
FILL_IPT_OPTION(i_device, MAX_DEVICE);
break;
case 'o':
CMP_IPT_OPTION(o_device);
FILL_IPT_OPTION(o_device, MAX_DEVICE);
break;
case 'p':
CMP_IPT_OPTION(prot);
FILL_IPT_OPTION(prot, MAX_PROT);
break;
case 'j':
//CMP_IPT_OPTION(target);
FILL_IPT_OPTION(target, MAX_TARGET);
break;
case 128:
CMP_IPT_OPTION(to);
FILL_IPT_OPTION(to, MAX_ADDR);
break;
case 129:
CMP_IPT_OPTION(sport);
FILL_IPT_OPTION(sport, MAX_PORT);
break;
case 130:
CMP_IPT_OPTION(dport);
FILL_IPT_OPTION(dport, MAX_PORT);
break;
case 'm':
CMP_IPT_OPTION(match);
FILL_IPT_OPTION(match, MAX_MATCH);
break;
case 131:
CMP_IPT_OPTION(match_info);
FILL_IPT_OPTION(match_info, MAX_MATCH_INFO);
break;
default:
_optind++;
break;
}
}
pass:
__free_dptr_char(&_argv, _argc);
MEMerr:
return ret;
}
// 连接跟踪信息解析,暂不使用,未完成
ret_code __nf_conntrack_parse(const char *conn, nf_conntrack *conf) {
int _argc = 0, i = 0, match_id = 0;
ret_code ret = RET_OK;
char **_argv = __str2str_array(conn, &_argc);
if (!_argv) {
ret = RET_NOMEM;
goto MEMerr;
}
strncpy(conf->prot, _argv[2], MAX_PROT - 1);
for (i = 3; i < _argc; ++i) {
if (strncmp(_argv[i], __field_match[match_id],
strlen(__field_match[match_id])) == 0) {
// TO DO
++match_id;
}
}
__free_dptr_char(&_argv, _argc);
MEMerr:
return ret;
}
// 解析ipt_config的action字段
enum action __parse_action(const struct ipt_config * const conf) {
enum action act;
if ((strcmp(conf->action, "ADD") == 0)) act = SET;
else if ((strcmp(conf->action, "DEL") == 0)) act = DELETE;
else if ((strcmp(conf->action, "SAVE") == 0)) act = SAVE;
else if ((strcmp(conf->action, "RESTORE") == 0)) act = RESTORE;
else act = UNKNOWN;
return act;
}
/*
* 将nat模块使用的cjson配置信息解析为ipt_config类型
* parameters:
* @json_arr: 待解析cjson配置信息
* @output_len: 返回值中ipt_config配置信息条数赋值0
* @ret: ret_code返回码
* @msg: 返回信息
*
* return: 指向ipt_config格式配置信息的指针,可能包含多条配置信息
*/
void *__cjsonarr_to_ipt_config(cJSON *json_arr, int *output_len,
ret_code *ret, char **msg) {
size_t json_arr_size, i;
struct ipt_config *conf = NULL, *conf_start = NULL;
cJSON *json = NULL;
json_arr_size = cJSON_GetArraySize(json_arr);
__chk_malloc(conf, struct ipt_config*, sizeof(*conf) * json_arr_size, *ret, msg);
memset(conf, 0, json_arr_size * sizeof(struct ipt_config));
conf_start = conf;
for (i = 0; i < json_arr_size; ++i, ++conf) {
json = cJSON_GetArrayItem(json_arr, i);
//__JSON_STR_TO_IPT_ITEM(chain, MAX_CHAIN);
_JSON_STR_TO_IPT_ITEM(action, action, MAX_ACTION);
_JSON_STR_TO_IPT_ITEM(id, id, MAX_ID);
_JSON_STR_TO_IPT_ITEM(target, target, MAX_TARGET);
_JSON_STR_TO_IPT_ITEM(prot, prot, MAX_PROT);
_JSON_STR_TO_IPT_ITEM(source, source, MAX_ADDR);
_JSON_STR_TO_IPT_ITEM(destination, destination, MAX_ADDR);
_JSON_STR_TO_IPT_ITEM(sport, sport, MAX_PORT);
_JSON_STR_TO_IPT_ITEM(dport, dport, MAX_PORT);
_JSON_STR_TO_IPT_ITEM(to, to, MAX_ADDR);
_JSON_STR_TO_IPT_ITEM(device, i_device, MAX_DEVICE);
_JSON_STR_TO_IPT_ITEM(device, o_device, MAX_DEVICE);
_JSON_STR_TO_IPT_ITEM(match, match, MAX_MATCH);
_JSON_STR_TO_IPT_ITEM(match_info, match_info, MAX_MATCH_INFO);
}
goto pass;
err:
__error_report(msg, i, "json item is too long to save in ipt_config.");
MEMerr:
xfree(conf_start);
pass:
*output_len = json_arr_size;
return conf_start;
}
/*
* 将nat模块使用的cjson配置信息解析为range_ipt_config类型
* parameters:
* @json_arr: 待解析cjson配置信息
* @output_len: 返回值中range_ipt_config配置信息条数赋值0
* @ret: ret_code返回码
* @msg: 返回信息
*
* return: 指向range_ipt_config格式配置信息的指针,可能包含多条配置信息
*/
void *__cjsonarr_to_range_ipt_config(cJSON *json_arr, int *output_len,
ret_code *ret, char **msg){
size_t json_arr_size, i;
struct ipt_config *conf = NULL, *conf_start = NULL;
struct range_ipt_config *rwconf = NULL, *rwconf_start = NULL;
cJSON *json = NULL, *__json = NULL;
json_arr_size = cJSON_GetArraySize(json_arr);
__chk_malloc(rwconf, struct range_ipt_config*, sizeof(*rwconf) * json_arr_size, *ret, msg);
memset(rwconf, 0, json_arr_size * sizeof(struct range_ipt_config));
rwconf_start = rwconf;
__chk_malloc(conf, struct ipt_config*, sizeof(*conf) * json_arr_size, *ret, msg);
memset(conf, 0, json_arr_size * sizeof(struct ipt_config));
conf_start = conf;
conf = __cjsonarr_to_ipt_config(json_arr, output_len, ret, msg);
for (i = 0; i < json_arr_size; ++i, ++conf, ++rwconf) {
memcpy(&(rwconf->conf), conf, sizeof(struct ipt_config));
json = cJSON_GetArrayItem(json_arr, i);
__json = cJSON_GetObjectItem(json, "begin");
if (__json) rwconf->begin = atoi(__json->valuestring);
else {
*ret = RET_INPUTERR;
goto MEMerr;
}
json = cJSON_GetArrayItem(json_arr, i);
__json = cJSON_GetObjectItem(json, "offset");
if (__json) rwconf->offset = atoi(__json->valuestring);
else {
*ret = RET_INPUTERR;
goto MEMerr;
}
}
goto pass;
err:
__error_report(msg, i, "json item is too long to save in range_ipt_config.");
MEMerr:
xfree(rwconf_start);
pass:
xfree(conf_start);
*output_len = json_arr_size;
return rwconf_start;
}
/*
* 将字符串格式的json配置信息解析为多种数据类型
* parameters:
* @json_text: 字符串格式的json配置信息
* @output_len: 返回值中ipt_config配置信息条数赋值0
* @func: 指向具体解析方法的函数指针
* @ret: ret_code返回码
* @msg: 返回信息
*
* return: 指向void*类型配置信息的指针,可能包含多条配置信息
*/
void *__jsontxt_to_struct(const char *json_text, int *output_len, jsonfunc func,
ret_code *ret, char **msg) {
cJSON *json_all = NULL, *json_arr = NULL;
void *conf = NULL;
*output_len = 0;
cJSON_Parse_e(json_all, json_text, msg);
json_arr = cJSON_GetObjectItem(json_all, "content");
if (!json_arr) {
__error_report(msg, -1, "%s", "json without content!");
*ret = RET_NOMEM;
goto MEMerr;
}
conf = func(json_arr, output_len, ret, msg);
MEMerr:
cJSON_Delete(json_all);
return conf;
}
/*
* 使用一条ipt_config结构配置信息拼接成linux命令
* parameters:
* @conf: 一条ipt_config结构的的nat配置信息
* @rule: 返回拼接后的字符串格式linux命令,需要预分配空间
* @msg: 信息返回
*
* return: ret_code返回码
*/
ret_code __command_splice(struct ipt_config *conf, iptables_rule rule, char **msg) {
iptables_rule head = "sudo iptables -t nat";
size_t free_space = MAX_LINE_LEN;
enum action option;
ret_code ret = RET_OK;
switch (option = __parse_action(conf)) {
// delete
case DELETE:
if (!isempty_str(conf->id)) {
strcat_str_free_count(rule, head);
splice_item(rule, del, id, get_chain(conf), str);
}
else {
strcpy(conf->id, " ");
strcat_str_free_count(rule, head);
splice_item(rule, del, id, get_chain(conf), str);
goto setmode;
}
break;
// set
case SET:
strcat_str_free_count(rule, head);
if (isempty_str(conf->id)) splice_item(rule, chain, chain, "", str);
else splice_item(rule, insert, id, get_chain(conf), str);
setmode:
splice_item(rule, source, source, "", str);
splice_item(rule, destination, destination, "", str);
splice_item(rule, prot, prot, "", str);
splice_item(rule, sport, sport, "", str);
splice_item(rule, dport, dport, "", str);
splice_item(rule, i_device, i_device, "", str);
splice_item(rule, o_device, o_device, "", str);
splice_item(rule, match, match, "", str);
splice_item(rule, match_info, match_info, conf->match, str);
splice_item(rule, target, target, "", str);
if (is_snat(conf)) splice_item(rule, to, to, "source", str);
else if (is_dnat(conf)) splice_item(rule, to, to, "destination", str);
break;
// save
case SAVE:
// TO DO
break;
// restore
case RESTORE:
// TO DO
break;
default:
// TO DO
break;
err:
__error_report(msg, current_id, "the iptables command is too long to splice!\n");
}
return ret;
}
/*
* ipt_config结构的配置信息合法性检查完成缺省配置推导
* parameters:
* @conf: 一条ipt_config结构的nat配置信息
*
* return: 配置完成且合法返回SUCCESS
*/
boolean __iptconfig_chk(struct ipt_config *conf, char **msg) {
// TO DO
enum action option;
if (conf == NULL) return FAIL;
if (isempty_str(conf->target)) {
__error_report(msg, current_id, "Blank target is not allowed!\n");
return FAIL;
}
if (is_snat(conf)) {
if (isempty_str(conf->to)) strcpy(conf->target, "MASQUERADE");
set_chain(conf, "POSTROUTING");
conf->i_device[0] = '\0';
}
else if (is_dnat(conf)) {
set_chain(conf, "PREROUTING");
conf->o_device[0] = '\0';
}
else if (is_masquerade(conf)) {
set_chain(conf, "POSTROUTING");
conf->i_device[0] = '\0';
}
else {
__error_report(msg, current_id, "\"%s\" is not is valid target!\n", conf->target);
return FAIL;
}
switch (option = __parse_action(conf)) {
case DELETE:
break;
case SET:
break;
case SAVE:
return FAIL;
break;
case RESTORE:
return FAIL;
break;
}
return SUCCESS;
}
/*
* 使用json数据格式配置ipables nat
* parameters:
* @json: 字符串格式的json配置信息
* @msg: 信息返回
*
* return: ret_code返回码
*/
ret_code set_iptables_config(const char *json, char **msg) {
int len = 0, i;
iptables_rule rule = NULL;
ret_code ret = RET_OK;
struct ipt_config *conf = NULL, *conf_start = NULL;
free_space = MAX_ERR_MSG;
__chk_malloc((*msg), char*, sizeof(char) * free_space, ret, msg);
memset(*msg, 0, sizeof(char) * free_space);
conf = (struct ipt_config*)__jsontxt_to_struct(json, &len,
__cjsonarr_to_ipt_config, &ret, msg);
if (ret != RET_OK) goto err;
conf_start = conf;
for (i = 0; i < len; ++i, ++conf) {
current_id = i;
if (__iptconfig_chk(conf, msg)) {
__chk_malloc(rule, iptables_rule, MAX_LINE_LEN + 1, ret, msg);
memset(rule, 0, sizeof(char) * (MAX_LINE_LEN + 1));
if ((ret = __command_splice(conf, rule, msg)) != RET_OK) goto err;
#if 0
// shell输出拼接后的linux配置命令
printf("%s\n", rule);
#endif
// 错误输出重定向
strncat(rule, " 2>&1", MAX_LINE_LEN - 1);
if ((ret = run_command(rule, msg)) != RET_OK) goto err;
xfree(rule);
}
else {
ret = RET_INPUTERR;
goto err;
}
}
MEMerr:
err:
xfree(rule);
xfree(conf_start);
return ret;
}
/*
* 将一条ipt_config类型的配置信息转化为json数据
* parameters:
* @root: json数据插入根节点指针
* @conf: 一条ipt_config类型的配置信息的指针,需要预分配空间
*
* return: ret_code返回码
*/
ret_code __ipt_config_tojson(cJSON *root, const struct ipt_config *conf) {
if (root == NULL) return FAIL;
cJSON *obj = NULL;
cJSON_AddItemToArray(root, obj=cJSON_CreateObject());
//_IPT_INT_ITEM_TOJSON(id);
_IPT_STR_ITEM_TOJSON(chain, chain);
_IPT_STR_ITEM_TOJSON(target, target);
_IPT_STR_ITEM_TOJSON(prot, prot);
_IPT_STR_ITEM_TOJSON(source, source);
_IPT_STR_ITEM_TOJSON(destination, destination);
_IPT_STR_ITEM_TOJSON(sport, sport);
_IPT_STR_ITEM_TOJSON(dport, dport);
_IPT_STR_ITEM_TOJSON(to,to);
_IPT_STR_ITEM_TOJSON(i_device, device);
_IPT_STR_ITEM_TOJSON(o_device, device);
_IPT_STR_ITEM_TOJSON(match, match);
_IPT_STR_ITEM_TOJSON(match_info, match_info);
return RET_OK;
}
/*
* 从iptables-save配置文件中获取json格式的nat配置信息
* parameters:
* @__filename: iptables-save配置文件路径
* @output: 返回字符串类型的json格式nat配置信息,需要预分配空间
* @outlen: 返回配置信息字符串长度
* @msg: 信息返回
*
* return: ret_code返回码
*/
ret_code get_iptables_config(const char *json, const char * __restrict__ __filename,
char *output, int *outlen, char **msg) {
cJSON *root = NULL;
iptables_rule *rules = NULL, *rules_start = NULL;
struct ipt_config *conf = NULL;
struct range_ipt_config *rwconf = NULL;
int len = 0, i;
int count = 0, offset = 0;
char* out = NULL;
free_space = MAX_ERR_MSG;
boolean match = FAIL;
ret_code ret = RET_OK;
__chk_malloc((*msg), char*, sizeof(char) * free_space, ret, msg);
memset(*msg, 0, sizeof(char)*free_space);
ret = run_command("sudo iptables-save -t nat > ./iptables-conf.txt 2>&1", msg);
if (ret != RET_OK) goto err;
root = cJSON_CreateArray();
if (!root) {
__error_report(msg, -1, "Error before: [%s]\n", cJSON_GetErrorPtr());
ret = RET_NOMEM;
goto MEMerr;
}
rwconf = (struct range_ipt_config*)__jsontxt_to_struct(json, &len,
__cjsonarr_to_range_ipt_config, &ret, msg);
if (ret != RET_OK) goto err;
rules = __read_iptables_conf(__filename, &len, 0, -1, &ret, msg);
rules_start = rules;
if (!__iptconfig_chk(&(rwconf->conf), msg)) goto err;
if (rwconf->begin < 0) rwconf->begin = 0;
if (rwconf->offset < 0) rwconf->offset = len;
for (i = 0; i < len; ++i, ++rules) {
if (count >= rwconf->begin + rwconf->offset) break;
if (**rules == ':') continue;
match = FAIL;
__chk_malloc(conf, struct ipt_config*, sizeof(*conf), ret, msg);
memset(conf, 0, sizeof(struct ipt_config));
if ((ret = __ipt_conf_parse(*rules, &(rwconf->conf), &match, conf)) != RET_OK) {
goto err;
}
if (match) {
if (count >= rwconf->begin) __ipt_config_tojson(root, conf);
count += 1;
}
xfree(conf);
}
out = cJSON_PrintUnformatted(root);
*outlen = strlen(out) + 1;
memcpy(output, out, *outlen);
MEMerr:
err:
xfree(conf);
xfree(rwconf);
xfree(out);
cJSON_Delete(root);
__free_dptr_char(&rules_start, len);
return ret;
}
ret_code get_nf_conntrack(const char * __restrict__ __filename,
char *output, int *outlen, char **msg) {
char **conntrack = NULL;
int len = 0, i;
char* out = NULL;
free_space = MAX_ERR_MSG;
ret_code ret = RET_OK;
//__chk_malloc((*msg), char*, free_space, ret, msg);
memset(*msg, 0, sizeof(char)*free_space);
conntrack = __read_nf_conntrack(__filename, &len, &ret, msg);
// TO DO
MEMerr:
return ret;
}
/*
* 原始调试程序,待修改,暂不可使用
*/
#ifdef NAT_DEBUG
int main(int argc, char *argv[]) {
int len;
char *out = NULL;
// 返回错误信息
char *err1 = NULL, *err2 = NULL;
//out = get_iptables_config(__iptables_conf_dir, &err1);
printf("%s\n", out);
printf("%s", err1);
__chk_free(out);
//set_iptables_config(__json, &err2);
printf("%s", err2);
free(err1);
free(err2);
return 0;
}
#endif