// // 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: 带拼接字段类型,可选str,int */ #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_handler,goto 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