🌴 Sync 2026-03-05 23:51:42

This commit is contained in:
github-actions[bot] committed 2026-03-05 23:51:42 +08:00
1 parent 0d155c70be
commit 32600acc60
14258 files changed
+3954570

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
OBJS:=appfilter_user.o appfilter_netlink.o appfilter_ubus.o appfilter_config.o utils.o main.o
EXEC:=oafd
all: $(OBJS)
$(CC) -o $(EXEC) $(OBJS) $(LIBS)
clean:
rm $(EXEC) *.o
+124
View File
@@ -0,0 +1,124 @@
#ifndef __APPFILTER_H__
#define __APPFILTER_H__
#define MIN_INET_ADDR_LEN 7
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include "utils.h"
#define LOG_FILE_PATH "/tmp/log/appfilter.log"
#define OAF_VERSION "6.1.7"
typedef enum {
LOG_LEVEL_ERROR,
LOG_LEVEL_WARN,
LOG_LEVEL_INFO,
LOG_LEVEL_DEBUG
} LogLevel;
extern int current_log_level;
static void af_log(LogLevel level, const char *format, ...){
if (level > current_log_level)
return;
FILE *log_file = fopen(LOG_FILE_PATH, "a");
if (!log_file) {
perror("Failed to open log file");
return;
}
time_t now = time(NULL);
struct tm *t = localtime(&now);
char time_str[20];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", t);
const char *level_str;
switch (level) {
case LOG_LEVEL_DEBUG: level_str = "DEBUG"; break;
case LOG_LEVEL_INFO: level_str = "INFO"; break;
case LOG_LEVEL_WARN: level_str = "WARN"; break;
case LOG_LEVEL_ERROR: level_str = "ERROR"; break;
default: level_str = "UNKNOWN"; break;
}
fprintf(log_file, "[%s] [%s] ", time_str, level_str);
va_list args;
va_start(args, format);
vfprintf(log_file, format, args);
va_end(args);
fclose(log_file);
}
#define LOG_DEBUG(format, ...) af_log(LOG_LEVEL_DEBUG, format, ##__VA_ARGS__)
#define LOG_INFO(format, ...) af_log(LOG_LEVEL_INFO, format, ##__VA_ARGS__)
#define LOG_WARN(format, ...) af_log(LOG_LEVEL_WARN, format, ##__VA_ARGS__)
#define LOG_ERROR(format, ...) af_log(LOG_LEVEL_ERROR, format, ##__VA_ARGS__)
#define MAX_TIME_LIST_LEN 1024
#define MAX_TIME_LIST 64
typedef struct af_time
{
int hour;
int min;
} af_time_t;
typedef struct af_global_config_t{
int enable;
int user_mode;
int work_mode;
int record_enable;
int disable_hnat;
int auto_load_engine;
int tcp_rst;
int disable_quic;
int app_filter_mode; // 0 = specified apps, 1 = all apps
char lan_ifname[16];
}af_global_config_t;
typedef struct time_config{
af_time_t start_time;
af_time_t end_time;
int days[7];
}time_config_t;
typedef struct daily_limit_config {
int enable;
int am_time;
int pm_time;
} daily_limit_config_t;
typedef struct af_time_config_t{
int time_mode;
time_config_t seg_time;
int deny_time;
int allow_time;
int days[7];
int time_num;
time_config_t time_list[MAX_TIME_LIST];
daily_limit_config_t daily_limit[7];
}af_time_config_t;
typedef struct af_config_t{
af_global_config_t global;
af_time_config_t time;
}af_config_t;
typedef struct af_run_time_status{
int deny_time;
int allow_time;
int filter;
int match_time;
int remain_time;
int used_time;
int period_blocked;
}af_run_time_status_t;
extern af_config_t g_af_config;
#endif
+552
View File
@@ -0,0 +1,552 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "appfilter_config.h"
#include "appfilter.h"
#include <uci.h>
app_name_info_t app_name_table[MAX_SUPPORT_APP_NUM];
int g_app_count = 0;
int g_cur_class_num = 0;
char CLASS_NAME_TABLE[MAX_APP_TYPE][MAX_CLASS_NAME_LEN];
const char *config_path = "./config";
static struct uci_context *uci_ctx = NULL;
static struct uci_package *uci_appfilter;
int af_uci_get_int_value(struct uci_context *ctx, char *key)
{
struct uci_element *e;
struct uci_ptr ptr;
int ret = -1;
int dummy;
char *parameters ;
char param_tmp[128] = {0};
strcpy(param_tmp, key);
if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) {
return ret;
}
if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) {
ctx->err = UCI_ERR_NOTFOUND;
goto done;
}
e = ptr.last;
switch(e->type) {
case UCI_TYPE_SECTION:
ret = -1;
goto done;
case UCI_TYPE_OPTION:
ret = atoi(ptr.o->v.string);
goto done;
default:
break;
}
done:
if (ptr.p)
uci_unload(ctx, ptr.p);
return ret;
}
int af_uci_get_value(struct uci_context *ctx, char *key, char *output, int out_len)
{
struct uci_element *e;
struct uci_ptr ptr;
int ret = UCI_OK;
int dummy;
char *parameters ;
char param_tmp[128] = {0};
strcpy(param_tmp, key);
if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) {
ret = 1;
return ret;
}
if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) {
ctx->err = UCI_ERR_NOTFOUND;
ret = 1;
goto done;
}
e = ptr.last;
switch(e->type) {
case UCI_TYPE_SECTION:
snprintf(output, out_len, "%s", ptr.s->type);
break;
case UCI_TYPE_OPTION:
snprintf(output, out_len, "%s", ptr.o->v.string);
break;
default:
ret = 1;
break;
}
done:
if (ptr.p)
uci_unload(ctx, ptr.p);
return ret;
}
int af_uci_delete(struct uci_context *ctx, char *key)
{
struct uci_element *e;
struct uci_ptr ptr;
int ret = UCI_OK;
int dummy;
char *parameters ;
char param_tmp[128] = {0};
strcpy(param_tmp, key);
if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) {
ret = 1;
return ret;
}
ret = uci_delete(ctx, &ptr);
if (ret == UCI_OK)
ret = uci_save(ctx, ptr.p);
if (ptr.p)
uci_unload(ctx, ptr.p);
return ret;
}
int af_uci_add_list(struct uci_context *ctx, char *key, char *value)
{
struct uci_element *e;
struct uci_ptr ptr;
int ret = UCI_OK;
int dummy;
char *parameters;
if (strlen(value) + strlen(key) >= MAX_PARAM_LIST_LEN - 1) {
printf("value too long\n");
return -1;
}
char param_tmp[MAX_PARAM_LIST_LEN] = {0};
sprintf(param_tmp, "%s=%s", key, value);
if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) {
ret = 1;
return ret;
}
ret = uci_add_list(ctx, &ptr);
if (ret == UCI_OK)
ret = uci_save(ctx, ptr.p);
if (ptr.p)
uci_unload(ctx, ptr.p);
return ret;
}
int af_uci_get_list_value(struct uci_context *ctx, char *key, char *output, int out_len, char *delimt)
{
struct uci_element *e;
struct uci_ptr ptr;
int ret = -1;
int dummy;
char *parameters ;
char param_tmp[128] = {0};
strcpy(param_tmp, key);
if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) {
return ret;
}
if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) {
ctx->err = UCI_ERR_NOTFOUND;
goto done;
}
int sep = 0;
e = ptr.last;
int len = 0;
switch(e->type) {
case UCI_TYPE_SECTION:
ret = -1;
goto done;
case UCI_TYPE_OPTION:
if (UCI_TYPE_LIST == ptr.o->type){
memset(output, 0x0, out_len);
uci_foreach_element(&ptr.o->v.list, e) {
len = strlen(output);
if (sep){
strncat(output + len, delimt, out_len);
}
len = strlen(output);
sprintf(output + len, "%s", e->name);
sep = 1;
}
ret = 0;
}
goto done;
default:
break;
}
done:
if (ptr.p)
uci_unload(ctx, ptr.p);
return ret;
}
int af_uci_add_int_list(struct uci_context *ctx, char *key, int value)
{
struct uci_element *e;
struct uci_ptr ptr;
int ret = UCI_OK;
int dummy;
char *parameters ;
char param_tmp[128] = {0};
sprintf(param_tmp, "%s=%d", key, value);
if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) {
ret = 1;
return ret;
}
ret = uci_add_list(ctx, &ptr);
if (ret == UCI_OK)
ret = uci_save(ctx, ptr.p);
if (ptr.p)
uci_unload(ctx, ptr.p);
return ret;
}
int af_uci_del_list(struct uci_context *ctx, char *key, char *value)
{
struct uci_element *e;
struct uci_ptr ptr;
int ret = UCI_OK;
int dummy;
char *parameters ;
char param_tmp[128] = {0};
sprintf(param_tmp, "%s=%s", key, value);
if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) {
ret = 1;
return ret;
}
ret = uci_del_list(ctx, &ptr);
if (ret == UCI_OK)
ret = uci_save(ctx, ptr.p);
if (ptr.p)
uci_unload(ctx, ptr.p);
return ret;
}
int af_uci_set_value(struct uci_context *ctx, char *key, char *value)
{
struct uci_element *e;
struct uci_ptr ptr;
int ret = UCI_OK;
int dummy;
char *parameters ;
char param_tmp[2048] = {0};
sprintf(param_tmp, "%s=%s", key, value);
if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) {
ret = 1;
return ret;
}
e = ptr.last;
ret = uci_set(ctx, &ptr);
if (ret == UCI_OK)
ret = uci_save(ctx, ptr.p);
if (ptr.p)
uci_unload(ctx, ptr.p);
return ret;
}
int af_uci_set_int_value(struct uci_context *ctx, char *key, int value)
{
struct uci_element *e;
struct uci_ptr ptr;
int ret = UCI_OK;
int dummy;
char *parameters ;
char param_tmp[128] = {0};
sprintf(param_tmp, "%s=%d", key, value);
if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) {
ret = 1;
return ret;
}
e = ptr.last;
ret = uci_set(ctx, &ptr);
if (ret == UCI_OK)
ret = uci_save(ctx, ptr.p);
if (ptr.p)
uci_unload(ctx, ptr.p);
return ret;
}
int af_uci_del_array_value(struct uci_context *ctx, char *key_fmt, int index){
char key[128] = {0};
sprintf(key, key_fmt, index);
return af_uci_delete(ctx, key);
}
int af_uci_set_array_value(struct uci_context *ctx, char *key_fmt, int index, char *value){
char key[128] = {0};
sprintf(key, key_fmt, index);
return af_uci_set_value(ctx, key, value);
}
int af_uci_commit(struct uci_context *ctx, const char * package) {
struct uci_ptr ptr;
int ret = UCI_OK;
if (!package){
return -1;
}
if (uci_lookup_ptr(ctx, &ptr, package, true) != UCI_OK) {
return -1;
}
if (uci_commit(ctx, &ptr.p, false) != UCI_OK) {
ret = -1;
goto done;
}
done:
if (ptr.p)
uci_unload(ctx, ptr.p);
return UCI_OK;
}
int af_get_uci_list_num(struct uci_context * ctx, char *package, char *section){
int count = 0;
struct uci_ptr p;
struct uci_element *e;
struct uci_package *pkg = NULL;
if (UCI_OK != uci_load(ctx, package, &pkg)){
return -1;
}
uci_foreach_element(&pkg->sections, e){
struct uci_section *s = uci_to_section(e);
if (strcmp(s->type, section)){
continue;
}
count++;
}
uci_unload(ctx, pkg);
return count;
}
int af_uci_get_array_value(struct uci_context *ctx, char *key_fmt, int index, char *output, int out_len)
{
char key[128] = {0};
sprintf(key, key_fmt, index);
return af_uci_get_value(ctx, key, output, out_len);
}
int af_uci_add_section(struct uci_context * ctx, char *package_name, char *section)
{
struct uci_section *s = NULL;
struct uci_package *p = NULL;
int ret;
ret = uci_load(ctx, package_name , &p);
if (ret != UCI_OK)
goto done;
ret = uci_add_section(ctx, p, section, &s);
if (ret != UCI_OK)
goto done;
ret = uci_save(ctx, p);
done:
if (s)
fprintf(stdout, "%s\n", s->e.name);
return ret;
}
//
static struct uci_package *
config_init_package(const char *config)
{
struct uci_context *ctx = uci_ctx;
struct uci_package *p = NULL;
if (!ctx)
{
ctx = uci_alloc_context();
uci_ctx = ctx;
ctx->flags &= ~UCI_FLAG_STRICT;
//if (config_path)
// uci_set_confdir(ctx, config_path);
}
else
{
p = uci_lookup_package(ctx, config);
if (p)
uci_unload(ctx, p);
}
if (uci_load(ctx, config, &p))
return NULL;
return p;
}
char *get_app_name_by_id(int id)
{
int i;
for (i = 0; i < g_app_count; i++)
{
if (id == app_name_table[i].id)
return app_name_table[i].name;
}
return "";
}
void init_app_name_table(void)
{
int count = 0;
char line_buf[2048] = {0};
FILE *fp = fopen("/tmp/feature.cfg", "r");
if (!fp)
{
printf("open file failed\n");
return;
}
g_app_count = 0;
while (fgets(line_buf, sizeof(line_buf), fp))
{
if (strstr(line_buf, "#"))
continue;
if (strlen(line_buf) < 10)
continue;
if (!strstr(line_buf, ":"))
continue;
char *pos1 = strstr(line_buf, ":");
char app_info_buf[128] = {0};
int app_id;
char app_name[64] = {0};
memset(app_name, 0x0, sizeof(app_name));
strncpy(app_info_buf, line_buf, pos1 - line_buf);
sscanf(app_info_buf, "%d %s", &app_id, app_name);
app_name_table[g_app_count].id = app_id;
strcpy(app_name_table[g_app_count].name, app_name);
g_app_count++;
}
fclose(fp);
}
void init_app_class_name_table(void)
{
char line_buf[2048] = {0};
int class_id;
char class_name[64] = {0};
FILE *fp = fopen("/tmp/app_class.txt", "r");
if (!fp)
{
printf("open file failed\n");
return;
}
g_cur_class_num = 0;
while (fgets(line_buf, sizeof(line_buf), fp))
{
sscanf(line_buf, "%d %*s %s", &class_id, class_name);
strcpy(CLASS_NAME_TABLE[class_id - 1], class_name);
g_cur_class_num++;
}
fclose(fp);
}
//00:00 9:1
int check_time_valid(char *t)
{
if (!t)
return 0;
if (strlen(t) < 3 || strlen(t) > 5 || (!strstr(t, ":")))
return 0;
else
return 1;
}
int config_get_appfilter_enable(void)
{
int enable = 0;
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return -1;
enable = af_uci_get_int_value(ctx, "appfilter.global.enable");
if (enable < 0)
enable = 0;
uci_free_context(ctx);
return enable;
}
int config_get_lan_ip(char *lan_ip, int len)
{
int ret = 0;
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return -1;
ret = af_uci_get_value(ctx, "network.lan.ipaddr", lan_ip, len);
uci_free_context(ctx);
return ret;
}
int config_get_lan_mask(char *lan_mask, int len)
{
int ret = 0;
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return -1;
ret = af_uci_get_value(ctx, "network.lan.netmask", lan_mask, len);
uci_free_context(ctx);
return ret;
}
int appfilter_config_alloc(void)
{
char *err;
uci_appfilter = config_init_package("appfilter");
if (!uci_appfilter)
{
uci_get_errorstr(uci_ctx, &err, NULL);
printf("Failed to load appfilter config (%s)\n", err);
free(err);
return -1;
}
return 0;
}
int appfilter_config_free(void)
{
if (uci_appfilter)
{
uci_unload(uci_ctx, uci_appfilter);
uci_appfilter = NULL;
}
if (uci_ctx)
{
uci_free_context(uci_ctx);
uci_ctx = NULL;
}
}
+68
View File
@@ -0,0 +1,68 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __APPFILTER_CONFIG_H__
#define __APPFILTER_CONFIG_H__
#include <uci.h>
#define MAX_SUPPORT_APP_NUM 1024
#define MAX_CLASS_NAME_LEN 32
#define MAX_PARAM_LIST_LEN 1024
#include "appfilter_user.h"
extern int g_cur_class_num;
extern int g_app_count;
extern char CLASS_NAME_TABLE[MAX_APP_TYPE][MAX_CLASS_NAME_LEN];
typedef struct app_name_info
{
int id;
char name[64];
} app_name_info_t;
void init_app_name_table(void);
void init_app_class_name_table(void);
char *get_app_name_by_id(int id);
int appfilter_config_alloc(void);
int appfilter_config_free(void);
int config_get_appfilter_enable(void);
int config_get_lan_ip(char *lan_ip, int len);
int config_get_lan_mask(char *lan_mask, int len);
int af_uci_delete(struct uci_context *ctx, char *key);
int af_uci_add_list(struct uci_context *ctx, char *key, char *value);
int af_uci_add_int_list(struct uci_context *ctx, char *key, int value);
int af_uci_del_list(struct uci_context *ctx, char *key, char *value);
int af_uci_get_list_value(struct uci_context *ctx, char *key, char *output, int out_len, char *delimt);
int af_uci_set_value(struct uci_context *ctx, char *key, char *value);
int af_uci_set_int_value(struct uci_context *ctx, char *key, int value);
int af_uci_del_array_value(struct uci_context *ctx, char *key_fmt, int index);
int af_uci_set_array_value(struct uci_context *ctx, char *key_fmt, int index, char *value);
int af_get_uci_list_num(struct uci_context * ctx, char *package, char *section);
int af_uci_get_array_value(struct uci_context *ctx, char *key_fmt, int index, char *output, int out_len);
int af_uci_get_int_value(struct uci_context *ctx, char *key);
int af_uci_get_value(struct uci_context *ctx, char *key, char *output, int out_len);
int af_uci_add_section(struct uci_context * ctx, char *package_name, char *section);
int af_uci_commit(struct uci_context *ctx, const char * package);
char *get_app_name_by_id(int id);
#endif
+279
View File
@@ -0,0 +1,279 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json-c/json.h>
#include "appfilter_user.h"
#include "appfilter_netlink.h"
#include "appfilter.h"
#include "appfilter_config.h"
#define MAX_NL_RCV_BUF_SIZE 4096
#define REPORT_INTERVAL_SECS 60
extern int hash_appid(int appid);
extern unsigned int g_feature_update_time;
void appfilter_nl_handler(struct uloop_fd *u, unsigned int ev)
{
int ret;
int i;
char buf[MAX_NL_RCV_BUF_SIZE];
struct sockaddr_nl nladdr;
struct iovec iov = {buf, sizeof(buf)};
struct nlmsghdr *h;
int type;
int id;
char *mac = NULL;
u_int32_t cur_time = get_timestamp();
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
do
{
ret = recvmsg(u->fd, &msg, 0);
} while ((-1 == ret) && (EINTR == errno));
if (ret < 0)
{
printf("recv msg error\n");
return;
}
else if (0 == ret)
{
return;
}
h = (struct nlmsghdr *)buf;
char *kmsg = (char *)NLMSG_DATA(h);
struct af_msg_hdr *af_hdr = (struct af_msg_hdr *)kmsg;
if (af_hdr->magic != 0xa0b0c0d0)
{
printf("magic error %x\n", af_hdr->magic);
return;
}
if (af_hdr->len <= 0 || af_hdr->len >= MAX_OAF_NETLINK_MSG_LEN)
{
printf("data len error\n");
return;
}
char *kdata = kmsg + sizeof(struct af_msg_hdr);
struct json_object *root = json_tokener_parse(kdata);
if (!root)
{
printf("parse json failed:%s", kdata);
return;
}
LOG_DEBUG("report %s\n", kdata);
struct json_object *mac_obj = json_object_object_get(root, "mac");
if (!mac_obj)
{
printf("parse mac obj failed\n");
json_object_put(root);
return;
}
mac = json_object_get_string(mac_obj);
dev_node_t *node = find_dev_node(mac);
if (!node)
{
node = add_dev_node(mac);
if (!node)
{
goto EXIT;
}
}
struct json_object *ip_obj = json_object_object_get(root, "ip");
if (ip_obj)
strncpy(node->ip, json_object_get_string(ip_obj), sizeof(node->ip));
struct json_object *active_obj = json_object_object_get(root, "active");
if (active_obj) {
node->active = json_object_get_int(active_obj);
if (node->active)
{
// 根据当前时间判断是上午还是下午,分别累加
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
int current_hour = tm_info->tm_hour;
if (current_hour < 12) {
// 上午:0:00-11:59
node->today_am_active_time += 1; //min
} else {
// 下午:12:00-23:59
node->today_pm_active_time += 1; //min
}
}
}
struct json_object *up_flow_obj = json_object_object_get(root, "up_flow");
struct json_object *down_flow_obj = json_object_object_get(root, "down_flow");
unsigned long long total_up_bytes = 0;
unsigned long long total_down_bytes = 0;
if (up_flow_obj) {
node->today_up_bytes += (unsigned long long)json_object_get_int64(up_flow_obj) * 1024;
}
if (down_flow_obj) {
node->today_down_bytes += (unsigned long long)json_object_get_int64(down_flow_obj) * 1024;
}
struct json_object *visit_array = json_object_object_get(root, "visit_info");
if (!visit_array)
{
goto EXIT;
}
for (i = 0; i < json_object_array_length(visit_array); i++)
{
struct json_object *visit_obj = json_object_array_get_idx(visit_array, i);
struct json_object *appid_obj = json_object_object_get(visit_obj, "appid");
struct json_object *action_obj = json_object_object_get(visit_obj, "latest_action");
// old appid may be not in the feature list
if (cur_time - g_feature_update_time < 300){
if (strlen(get_app_name_by_id(json_object_get_int(appid_obj))) == 0){
LOG_INFO("ignore appid %d because it is not in the feature list\n", json_object_get_int(appid_obj));
continue;
}
}
int appid = json_object_get_int(appid_obj);
int action = json_object_get_int(action_obj);
type = appid / 1000;
id = appid % 1000;
if (id <= 0 || type <= 0)
continue;
node->stat[type - 1][id - 1].total_time += REPORT_INTERVAL_SECS;
int hash = hash_appid(appid);
visit_info_t *head = node->visit_htable[hash];
visit_info_t *p = head;
while(p){
if((p->appid == appid) && (cur_time - p->latest_time < 300)){
LOG_DEBUG("match appid = %d\n", appid, cur_time - p->latest_time);
break;
}
p = p->next;
}
if (!p){
p = (visit_info_t *)calloc(1, sizeof(visit_info_t));
p->appid = appid;
p->next = NULL;
p->first_time = cur_time;
add_visit_info_node(&node->visit_htable[hash], p);
}
p->action = action;
p->latest_time = cur_time;
}
EXIT:
json_object_put(root);
}
#define MAX_NL_MSG_LEN 1024
int send_msg_to_kernel(int fd, void *msg, int len)
{
struct sockaddr_nl saddr, daddr;
memset(&daddr, 0, sizeof(daddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0; // to kernel
daddr.nl_groups = 0;
int ret = 0;
struct nlmsghdr *nlh = NULL;
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_NL_MSG_LEN));
nlh->nlmsg_len = NLMSG_SPACE(MAX_NL_MSG_LEN);
nlh->nlmsg_flags = 0;
nlh->nlmsg_type = 0;
nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = DEFAULT_USR_NL_PID;
char msg_buf[MAX_NL_MSG_LEN] = {0};
struct af_msg_hdr *hdr = (struct af_msg_hdr *)msg_buf;
hdr->magic = 0xa0b0c0d0;
hdr->len = len;
char *p_data = msg_buf + sizeof(struct af_msg_hdr);
memcpy(p_data, msg, len);
memcpy(NLMSG_DATA(nlh), msg_buf, len + sizeof(struct af_msg_hdr));
ret = sendto(fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
free(nlh);
if (!ret)
{
perror("sendto error\n");
return -1;
}
return 0;
}
int appfilter_nl_init(void)
{
int fd;
struct sockaddr_nl nls;
fd = socket(AF_NETLINK, SOCK_RAW, OAF_NETLINK_ID);
if (fd < 0)
{
LOG_DEBUG("Connect netlink %d failed %s\n", OAF_NETLINK_ID, strerror(errno));
return -1;
}
memset(&nls, 0, sizeof(struct sockaddr_nl));
nls.nl_pid = DEFAULT_USR_NL_PID;
nls.nl_groups = 0;
nls.nl_family = AF_NETLINK;
if (bind(fd, (void *)&nls, sizeof(struct sockaddr_nl)))
{
LOG_DEBUG("Bind failed %s\n", strerror(errno));
return -1;
}
return fd;
}
+57
View File
@@ -0,0 +1,57 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __APPFILTER_NETLINK_H__
#define __APPFILTER_NETLINK_H__
#define DEFAULT_USR_NL_PID 999
#define OAF_NETLINK_ID 29
#define MAX_OAF_NETLINK_MSG_LEN 1024
#define MAX_AF_MSG_DATA_LEN 800
#define MAX_FEATURE_LINE_LEN 600
struct af_msg_hdr
{
int magic;
int len;
};
enum E_MSG_TYPE
{
AF_MSG_INIT,
AF_MSG_ADD_FEATURE,
AF_MSG_CLEAN_FEATURE,
AF_MSG_MAX
};
typedef struct af_msg
{
int action;
} af_msg_t;
typedef struct af_feature_msg{
af_msg_t hdr;
char feature[MAX_FEATURE_LINE_LEN];
} af_feature_msg_t;
int appfilter_nl_init(void);
void appfilter_nl_handler(struct uloop_fd *u, unsigned int ev);
int send_msg_to_kernel(int fd, void *msg, int len);
#endif
File diff suppressed because it is too large. Load diff
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __APPFILTER_UBUS_H__
#define __APPFILTER_UBUS_H__
int appfilter_ubus_init(void);
#endif
+1000
View File
@@ -0,0 +1,1000 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <json-c/json.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include "appfilter_config.h"
#include "appfilter.h"
#include "appfilter_user.h"
dev_node_t *dev_hash_table[MAX_DEV_NODE_HASH_SIZE];
int g_cur_user_num = 0;
unsigned int hash_mac(unsigned char *mac)
{
unsigned int hash = 0;
int i;
if (!mac)
return 0;
for (i = 0; mac[i] != '\0' && i < MAX_MAC_LEN; i++) {
hash = hash * 31 + mac[i];
}
return hash & (MAX_DEV_NODE_HASH_SIZE - 1);
}
int hash_appid(int appid)
{
return appid % (MAX_VISIT_HASH_SIZE - 1);
}
void add_visit_info_node(visit_info_t **head, visit_info_t *node)
{
if (*head == NULL)
{
*head = node;
}
else
{
node->next = *head;
*head = node;
}
}
void init_dev_node_htable()
{
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_hash_table[i] = NULL;
}
printf("init dev node htable ok...\n");
}
dev_node_t *add_dev_node(char *mac)
{
unsigned int hash = 0;
hash = hash_mac(mac);
if (hash >= MAX_DEV_NODE_HASH_SIZE)
{
printf("hash code error %d\n", hash);
return NULL;
}
dev_node_t *node = (dev_node_t *)calloc(1, sizeof(dev_node_t));
if (!node)
return NULL;
strncpy(node->mac, mac, sizeof(node->mac));
node->online = 1;
node->online_time = get_timestamp();
if (dev_hash_table[hash] == NULL)
dev_hash_table[hash] = node;
else
{
node->next = dev_hash_table[hash];
dev_hash_table[hash] = node;
}
g_cur_user_num++;
printf("add mac:%s to htable[%d]....success\n", mac, hash);
return node;
}
dev_node_t *find_dev_node(char *mac)
{
unsigned int hash = 0;
dev_node_t *p = NULL;
hash = hash_mac(mac);
if (hash >= MAX_DEV_NODE_HASH_SIZE)
{
printf("hash code error %d\n", hash);
return NULL;
}
p = dev_hash_table[hash];
while (p)
{
if (0 == strncmp(p->mac, mac, sizeof(p->mac)))
{
return p;
}
p = p->next;
}
return NULL;
}
void dev_foreach(void *arg, iter_func iter)
{
int i, j;
dev_node_t *node = NULL;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
iter(arg, node);
node = node->next;
}
}
}
char *format_time(int timetamp)
{
char time_buf[64] = {0};
time_t seconds = timetamp;
struct tm *auth_tm = localtime(&seconds);
strftime(time_buf, sizeof(time_buf), "%Y %m %d %H:%M:%S", auth_tm);
return strdup(time_buf);
}
void update_dev_hostname(void)
{
char line_buf[256] = {0};
char hostname_buf[128] = {0};
char mac_buf[32] = {0};
char ip_buf[32] = {0};
FILE *fp = fopen("/tmp/dhcp.leases", "r");
if (!fp)
{
printf("open dhcp lease file....failed\n");
return;
}
while (fgets(line_buf, sizeof(line_buf), fp))
{
if (strlen(line_buf) <= 16)
continue;
sscanf(line_buf, "%*s %s %s %s", mac_buf, ip_buf, hostname_buf);
dev_node_t *node = find_dev_node(mac_buf);
if (!node)
{
node = add_dev_node(mac_buf);
if (!node)
continue;
strncpy(node->ip, ip_buf, sizeof(node->ip));
node->online = 0;
node->offline_time = get_timestamp();
}
if (strlen(hostname_buf) > 0 && hostname_buf[0] != '*')
{
strncpy(node->hostname, hostname_buf, sizeof(node->hostname));
}
}
fclose(fp);
}
void clean_dev_nickname_iter(void *arg, dev_node_t *node)
{
node->nickname[0] = '\0';
}
void clean_dev_nickname(void)
{
dev_foreach(NULL, clean_dev_nickname_iter);
}
void update_dev_nickname(void)
{
char nickname_buf[128] = {0};
char mac_str[128] = {0};
struct uci_context *uci_ctx = uci_alloc_context();
clean_dev_nickname();
int num = af_get_uci_list_num(uci_ctx, "user_info", "user_info");
for (int i = 0; i < num; i++) {
af_uci_get_array_value(uci_ctx, "user_info.@user_info[%d].mac", i, mac_str, sizeof(mac_str));
dev_node_t *node = find_dev_node(mac_str);
if (!node)
continue;
af_uci_get_array_value(uci_ctx, "user_info.@user_info[%d].nickname", i, nickname_buf, sizeof(nickname_buf));
strncpy(node->nickname, nickname_buf, sizeof(node->nickname));
}
uci_free_context(uci_ctx);
}
void clean_dev_whitelist_flag_iter(void *arg, dev_node_t *node)
{
node->is_whitelist = 0;
}
void clean_dev_whitelist_flag(void)
{
dev_foreach(NULL, clean_dev_whitelist_flag_iter);
}
void update_dev_whitelist_flag(void)
{
clean_dev_whitelist_flag();
dev_node_t *node = NULL;
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
return;
}
char mac_str[128] = {0};
int num = af_get_uci_list_num(uci_ctx, "appfilter", "whitelist");
for (int i = 0; i < num; i++) {
af_uci_get_array_value(uci_ctx, "appfilter.@whitelist[%d].mac", i, mac_str, sizeof(mac_str));
node = find_dev_node(mac_str);
if (node) {
node->is_whitelist = 1;
}
}
uci_free_context(uci_ctx);
}
void clean_dev_selected_flag_iter(void *arg, dev_node_t *node)
{
node->is_selected = 0;
}
void clean_dev_selected_flag(void)
{
dev_foreach(NULL, clean_dev_selected_flag_iter);
}
void update_dev_selected_flag(void)
{
extern af_config_t g_af_config;
int user_mode = g_af_config.global.user_mode;
clean_dev_selected_flag();
if (user_mode == 0) {
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) {
dev_node_t *node = dev_hash_table[i];
while (node) {
if (!node->is_whitelist) {
node->is_selected = 1;
}
node = node->next;
}
}
LOG_DEBUG("Auto mode: all users except whitelist are selected\n");
} else {
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context in update_dev_selected_flag\n");
return;
}
char mac_str[128] = {0};
int num = af_get_uci_list_num(uci_ctx, "appfilter", "af_user");
for (int i = 0; i < num; i++) {
af_uci_get_array_value(uci_ctx, "appfilter.@af_user[%d].mac", i, mac_str, sizeof(mac_str));
dev_node_t *node = find_dev_node(mac_str);
if (node) {
node->is_selected = 1;
}
}
uci_free_context(uci_ctx);
LOG_DEBUG("Manual mode: %d users from af_user config are selected\n", num);
}
}
void clean_dev_online_status(void)
{
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
if (node->online)
{
node->offline_time = get_timestamp();
node->online = 0;
}
node = node->next;
}
}
}
void update_dev_from_kernel(void)
{
char line_buf[256] = {0};
char mac_buf[32] = {0};
char ip_buf[32] = {0};
char ipv6_buf[128] = {0};
unsigned int up_rate = 0;
unsigned int down_rate = 0;
FILE *fp = fopen("/proc/net/af_client", "r");
if (!fp)
{
printf("open client file....failed\n");
return;
}
fgets(line_buf, sizeof(line_buf), fp); // title
while (fgets(line_buf, sizeof(line_buf), fp))
{
int id;
int parsed = sscanf(line_buf, "%d %s %s %s %u %u", &id, mac_buf, ip_buf, ipv6_buf, &up_rate, &down_rate);
if (parsed < 3)
{
printf("invalid line format:%s\n", line_buf);
continue;
}
if (strlen(mac_buf) < 17)
{
printf("invalid mac:%s\n", mac_buf);
continue;
}
dev_node_t *node = find_dev_node(mac_buf);
if (!node)
{
node = add_dev_node(mac_buf);
if (!node)
continue;
strncpy(node->ip, ip_buf, sizeof(node->ip));
}
strncpy(node->ip, ip_buf, sizeof(node->ip));
if (parsed >= 4 && strlen(ipv6_buf) > 0)
{
strncpy(node->ipv6, ipv6_buf, sizeof(node->ipv6));
}
else
{
node->ipv6[0] = '\0';
}
if (parsed >= 5)
{
node->up_rate = up_rate;
}
else
{
node->up_rate = 0;
}
if (parsed >= 6)
{
node->down_rate = down_rate;
}
else
{
node->down_rate = 0;
}
node->online = 1;
}
fclose(fp);
}
void update_dev_online_status(void)
{
update_dev_from_kernel();
}
#define DEV_OFFLINE_TIME (SECONDS_PER_DAY * 7)
int check_dev_expire(void)
{
int i, j;
int count = 0;
int cur_time = get_timestamp();
int offline_time = 0;
int expire_count = 0;
int visit_count = 0;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
if (node->online)
goto NEXT;
visit_count = 0;
offline_time = cur_time - node->offline_time;
if (offline_time > DEV_OFFLINE_TIME)
{
node->expire = 1;
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
while (p_info)
{
p_info->expire = 1;
visit_count++;
p_info = p_info->next;
}
}
expire_count++;
LOG_WARN("dev:%s expired, offline time = %ds, count=%d, visit_count=%d\n",
node->mac, offline_time, expire_count, visit_count);
}
NEXT:
node = node->next;
}
}
return expire_count;
}
void flush_dev_expire_node(void)
{
int i, j;
int count = 0;
dev_node_t *node = NULL;
dev_node_t *prev = NULL;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
prev = NULL;
while (node)
{
if (node->expire)
{
if (NULL == prev)
{
dev_hash_table[i] = node->next;
free(node);
node = dev_hash_table[i];
prev = NULL;
}
else
{
prev->next = node->next;
free(node);
node = prev->next;
}
}
else
{
prev = node;
node = node->next;
}
}
}
}
void flush_offline_users(void)
{
int i, j;
int count = 0;
dev_node_t *node = NULL;
dev_node_t *prev = NULL;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
prev = NULL;
while (node)
{
if (!node->online) // Clear all offline users
{
// Free visit info first
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
while (p_info)
{
visit_info_t *next = p_info->next;
free(p_info);
p_info = next;
}
}
// Remove node from hash table
if (NULL == prev)
{
dev_hash_table[i] = node->next;
free(node);
node = dev_hash_table[i];
prev = NULL;
}
else
{
prev->next = node->next;
free(node);
node = prev->next;
}
count++;
}
else
{
prev = node;
node = node->next;
}
}
}
LOG_WARN("Cleared %d offline users\n", count);
}
void save_user_time_to_file(void)
{
int i;
int count = 0;
FILE *fp = fopen(OAF_USER_FILE, "w");
if (!fp) {
LOG_ERROR("Failed to open file %s for writing\n", OAF_USER_FILE);
return;
}
fprintf(fp, "MAC,AM_Time,PM_Time\n");
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) {
dev_node_t *node = dev_hash_table[i];
while (node) {
fprintf(fp, "%s,%u,%u\n",
node->mac,
node->today_am_active_time,
node->today_pm_active_time);
count++;
node = node->next;
}
}
fclose(fp);
LOG_DEBUG("Saved %d users' time data to %s\n", count, OAF_USER_FILE);
}
void load_user_time_from_file(void)
{
FILE *fp = fopen(OAF_USER_FILE, "r");
if (!fp) {
LOG_DEBUG("File %s not found or cannot be opened, starting with empty data\n", OAF_USER_FILE);
return;
}
char line_buf[256] = {0};
int count = 0;
if (fgets(line_buf, sizeof(line_buf), fp) == NULL) {
fclose(fp);
return;
}
while (fgets(line_buf, sizeof(line_buf), fp)) {
char mac[32] = {0};
unsigned int am_time = 0;
unsigned int pm_time = 0;
if (sscanf(line_buf, "%31[^,],%u,%u", mac, &am_time, &pm_time) == 3) {
dev_node_t *node = find_dev_node(mac);
if (!node) {
// If node doesn't exist, create a new one
node = add_dev_node(mac);
if (!node) {
LOG_WARN("Failed to create device node for MAC=%s\n", mac);
continue;
}
LOG_DEBUG("Created new device node for MAC=%s\n", mac);
}
node->today_am_active_time = am_time;
node->today_pm_active_time = pm_time;
count++;
LOG_DEBUG("Loaded user time: MAC=%s, AM=%u, PM=%u\n", mac, am_time, pm_time);
} else {
LOG_WARN("Failed to parse line: %s\n", line_buf);
}
}
fclose(fp);
LOG_WARN("Loaded %d users' time data from %s\n", count, OAF_USER_FILE);
}
void update_dev_visiting_info(void){
char line_buf[256] = {0};
char mac_buf[32] = {0};
char url_buf[32] = {0};
char app_buf[32] = {0};
char time_buf[32] = {0};
FILE *fp = fopen("/proc/net/af_visit", "r");
if (!fp)
{
printf("open af_visit file....failed\n");
return;
}
fgets(line_buf, sizeof(line_buf), fp); // title
while (fgets(line_buf, sizeof(line_buf), fp))
{
sscanf(line_buf, "%s %s %s", mac_buf, app_buf, url_buf);
dev_node_t *node = find_dev_node(mac_buf);
if (!node)
continue;
if (strcmp(url_buf, "none") == 0) {
node->visiting_url[0] = '\0';
}
else {
strncpy(node->visiting_url, url_buf, sizeof(node->visiting_url));
}
node->visiting_app = atoi(app_buf);
}
fclose(fp);
}
void update_dev_list(void)
{
clean_dev_online_status();
update_dev_hostname();
update_dev_nickname();
update_dev_online_status();
update_dev_visiting_info();
update_dev_selected_flag();
}
void dump_dev_list(void)
{
int i, j;
int count = 0;
char hostname_buf[MAX_HOSTNAME_SIZE] = {0};
char ip_buf[MAX_IP_LEN] = {0};
FILE *fp = fopen(OAF_DEV_LIST_FILE, "w");
if (!fp)
{
return;
}
fprintf(fp, "%-4s %-20s %-20s %-32s %-8s\n", "Id", "Mac Addr", "Ip Addr", "Hostname", "Online");
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
if (node->online != 0)
{
if (strlen(node->hostname) == 0)
strcpy(hostname_buf, "*");
else
strcpy(hostname_buf, node->hostname);
if (strlen(node->ip) == 0)
strcpy(ip_buf, "*");
else
strcpy(ip_buf, node->ip);
fprintf(fp, "%-4d %-20s %-20s %-32s %-8d\n",
i + 1, node->mac, ip_buf, hostname_buf, node->online);
count++;
}
node = node->next;
}
}
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
if (node->online == 0)
{
if (strlen(node->hostname) == 0)
strcpy(hostname_buf, "*");
else
strcpy(hostname_buf, node->hostname);
if (strlen(node->ip) == 0)
strcpy(ip_buf, "*");
else
strcpy(ip_buf, node->ip);
fprintf(fp, "%-4d %-20s %-20s %-32s %-8d\n",
i + 1, node->mac, ip_buf, hostname_buf, node->online);
}
node = node->next;
}
}
EXIT:
fclose(fp);
}
#define MAX_RECORD_TIME (1 * 24 * 60 * 60) // 1day
#define RECORD_REMAIN_TIME (60 * 60) // 1hour
#define INVALID_RECORD_TIME (5 * 60) // 5min
void check_dev_visit_info_expire(void)
{
int i, j;
int count = 0;
int cur_time = get_timestamp();
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
while (p_info)
{
int total_time = p_info->latest_time - p_info->first_time;
int interval_time = cur_time - p_info->first_time;
if (interval_time > MAX_RECORD_TIME || interval_time < 0)
{
p_info->expire = 1;
}
else if (interval_time > RECORD_REMAIN_TIME)
{
if (total_time < INVALID_RECORD_TIME)
p_info->expire = 1;
}
LOG_DEBUG("[%s] appid:%d total_time:%ds interval:%ds, expire = %d\n", node->mac, p_info->appid, total_time, interval_time, p_info->expire);
p_info = p_info->next;
}
}
node = node->next;
}
}
}
void flush_expire_visit_info(void)
{
int i, j;
int count = 0;
visit_info_t *prev = NULL;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
prev = NULL;
while (p_info)
{
if (p_info->expire)
{
LOG_DEBUG("check expire,flush expire visit info: %s, appid=%d\n", node->mac, p_info->appid);
if (NULL == prev)
{
node->visit_htable[j] = p_info->next;
free(p_info);
p_info = node->visit_htable[j];
prev = NULL;
}
else
{
prev->next = p_info->next;
free(p_info);
p_info = prev->next;
}
}
else
{
prev = p_info;
p_info = p_info->next;
}
}
}
node = node->next;
}
}
}
void dump_dev_visit_list(void)
{
int i, j;
int count = 0;
FILE *fp = fopen(OAF_VISIT_LIST_FILE, "w");
if (!fp)
{
return;
}
fprintf(fp, "%-4s %-20s %-20s %-8s %-32s %-32s %-32s %-8s\n", "Id", "Mac Addr",
"Ip Addr", "Appid", "First Time", "Latest Time", "Total Time(s)", "Expire");
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
while (p_info)
{
char *first_time_str = format_time(p_info->first_time);
char *latest_time_str = format_time(p_info->latest_time);
int total_time = p_info->latest_time - p_info->first_time;
fprintf(fp, "%-4d %-20s %-20s %-8d %-32s %-32s %-32d %-4d\n",
count, node->mac, node->ip, p_info->appid, first_time_str,
latest_time_str, total_time, p_info->expire);
if (first_time_str)
free(first_time_str);
if (latest_time_str)
free(latest_time_str);
p_info = p_info->next;
count++;
if (count > 50)
goto EXIT;
}
}
node = node->next;
}
}
EXIT:
fclose(fp);
}
void clean_invalid_app_records(void)
{
int i, j;
int invalid_count = 0;
int total_count = 0;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
while (p_info)
{
total_count++;
char *app_name = get_app_name_by_id(p_info->appid);
if (app_name && strlen(app_name) == 0)
{
p_info->expire = 1;
invalid_count++;
LOG_DEBUG("clean: MAC=%s, AppID=%d\n", node->mac, p_info->appid);
}
p_info = p_info->next;
}
}
node = node->next;
}
}
if (invalid_count > 0)
{
flush_expire_visit_info();
}
}
void clear_device_app_statistics(void)
{
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
memset(node->stat, 0, sizeof(node->stat));
node = node->next;
}
}
}
void check_and_reset_today_active_time(dev_node_t *node)
{
if (!node)
return;
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
int current_hour = tm_info->tm_hour;
int current_min = tm_info->tm_min;
static int last_reset_hour = -1;
static int last_reset_min = -1;
if (current_hour == 12 && current_min == 0) {
if (last_reset_hour != 12 || last_reset_min != 0) {
LOG_DEBUG("Reset today_am_active_time for %s: %d -> 0 (12:00 reset)\n",
node->mac, node->today_am_active_time);
node->today_am_active_time = 0;
last_reset_hour = 12;
last_reset_min = 0;
}
} else {
last_reset_hour = current_hour;
last_reset_min = current_min;
}
}
void reset_all_users_today_active_time(void)
{
extern af_run_time_status_t g_af_status;
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) {
dev_node_t *node = dev_hash_table[i];
while (node) {
LOG_DEBUG("Reset today active time for %s: am=%d->0, pm=%d->0 (day changed)\n",
node->mac, node->today_am_active_time, node->today_pm_active_time);
node->today_am_active_time = 0;
node->today_pm_active_time = 0;
node = node->next;
}
}
g_af_status.period_blocked = 0;
}
void reset_all_users_today_flow(void)
{
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) {
dev_node_t *node = dev_hash_table[i];
while (node) {
node->today_down_bytes = 0;
node->today_up_bytes = 0;
node = node->next;
}
}
}
void check_all_users_period_time(void)
{
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
check_and_reset_today_active_time(node);
node = node->next;
}
}
}
void save_user_active_time_to_file(void)
{
FILE *fp = fopen(OAF_USER_FILE, "w");
if (!fp) {
LOG_ERROR("Failed to open file for writing: %s\n", OAF_USER_FILE);
return;
}
fprintf(fp, "mac,today_am_active_time,today_pm_active_time\n");
int i;
int count = 0;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) {
dev_node_t *node = dev_hash_table[i];
while (node) {
if (node->today_am_active_time > 0 || node->today_pm_active_time > 0) {
fprintf(fp, "%s,%u,%u\n",
node->mac,
node->today_am_active_time,
node->today_pm_active_time);
count++;
}
node = node->next;
}
}
fclose(fp);
LOG_DEBUG("Saved %d users' active time to %s\n", count, OAF_USER_FILE);
}
+136
View File
@@ -0,0 +1,136 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __FILTER_USER_H__
#define __FILTER_USER_H__
#include <sys/types.h>
#define MAX_IP_LEN 32
#define MAX_MAC_LEN 32
#define MAX_VISIT_HASH_SIZE 64
#define MAX_DEV_NODE_HASH_SIZE 64
#define MAX_HOSTNAME_SIZE 64
#define OAF_VISIT_LIST_FILE "/tmp/visit_list"
#define OAF_DEV_LIST_FILE "/tmp/dev_list"
#define OAF_USER_FILE "/etc/user_list.dat"
#define MIN_VISIT_TIME 5 // default 5s
#define MAX_APP_STAT_NUM 8
#define MAX_VISITLIST_DUMP_NUM 16
#define MAX_APP_TYPE 32
#define MAX_APP_ID_NUM 512
#define MAX_SUPPORT_DEV_NUM 256
#define SECONDS_PER_DAY (24 * 3600)
#define MAX_NICKNAME_SIZE 64
#define MAX_REPORT_URL_LEN 64
typedef struct visit_info
{
int appid;
u_int32_t first_time;
u_int32_t latest_time;
int action;
int expire;
struct visit_info *next;
} visit_info_t;
typedef struct visit_stat
{
u_int32_t total_time;
} visit_stat_t;
typedef struct dev_node
{
char mac[MAX_MAC_LEN];
char ip[MAX_IP_LEN];
char ipv6[64];
char hostname[MAX_HOSTNAME_SIZE];
char nickname[MAX_NICKNAME_SIZE];
int online;
int expire;
u_int32_t offline_time;
u_int32_t online_time;
visit_info_t *visit_htable[MAX_VISIT_HASH_SIZE];
visit_stat_t stat[MAX_APP_TYPE][MAX_APP_ID_NUM]; // todo: list
char visiting_url[MAX_REPORT_URL_LEN];
int visiting_app;
int is_whitelist;
u_int32_t up_rate;
u_int32_t down_rate;
u_int64_t today_up_bytes;
u_int64_t today_down_bytes;
int active;
u_int32_t today_am_active_time;
u_int32_t today_pm_active_time;
int is_selected;
struct dev_node *next;
} dev_node_t;
struct app_visit_info
{
int app_id;
char app_name[32];
int total_time;
};
struct app_visit_stat_info
{
int num;
struct app_visit_info visit_list[MAX_APP_STAT_NUM];
};
typedef void (*iter_func)(void *arg, dev_node_t *dev);
//todo:dev for each
extern dev_node_t *dev_hash_table[MAX_DEV_NODE_HASH_SIZE];
dev_node_t *add_dev_node(char *mac);
void init_dev_node_htable();
void dump_dev_list(void);
void dump_dev_visit_list(void);
dev_node_t *find_dev_node(char *mac);
void dev_foreach(void *arg, iter_func iter);
void add_visit_info_node(visit_info_t **head, visit_info_t *node);
void check_dev_visit_info_expire(void);
void flush_expire_visit_info();
int check_dev_expire(void);
void flush_dev_expire_node(void);
void flush_expire_visit_info(void);
void flush_offline_users(void);
void save_user_time_to_file(void);
void load_user_time_from_file(void);
void update_dev_list(void);
void update_dev_nickname(void);
void update_dev_visiting_info(void);
void update_dev_whitelist_flag(void);
void update_dev_selected_flag(void);
void clean_invalid_app_records(void);
void clear_device_app_statistics(void);
void check_and_reset_today_active_time(dev_node_t *node);
void reset_all_users_today_active_time(void);
void reset_all_users_today_flow(void);
void check_all_users_period_time(void);
void update_dev_online_status(void);
#endif
+883
View File
@@ -0,0 +1,883 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include "appfilter_user.h"
#include "appfilter_netlink.h"
#include "appfilter_ubus.h"
#include "appfilter_config.h"
#include <time.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "appfilter.h"
#include <stdio.h>
#include "utils.h"
#define CMD_GET_LAN_IP_FMT "ifconfig %s | grep 'inet addr' | awk '{print $2}' | awk -F: '{print $2}'"
#define CMD_GET_LAN_MASK_FMT "ifconfig %s | grep 'inet addr' | awk '{print $4}' | awk -F: '{print $2}'"
int current_log_level = LOG_LEVEL_INFO;
af_run_time_status_t g_af_status;
int g_oaf_config_change = 1;
af_config_t g_af_config;
int g_hnat_init = 0;
int g_feature_update = 0;
int g_feature_update_time = 0;
void oaf_timeout_handler(struct uloop_timeout *t);
void af_init_time_status(void){
g_af_status.filter = 0;
g_af_status.deny_time = 0;
g_af_status.allow_time = 0;
g_af_status.match_time = 0;
g_af_status.remain_time = 0;
g_af_status.used_time = 0;
g_af_status.period_blocked = 0;
}
void af_init_status(void){
af_init_time_status();
}
struct uloop_timeout dev_tm = {
.cb = oaf_timeout_handler};
static struct uloop_fd appfilter_nl_fd = {
.cb = appfilter_nl_handler,
};
void apply_time_config_to_uci(af_time_config_t *time_config){
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
printf("Failed to allocate UCI context\n");
return;
}
af_uci_set_int_value(uci_ctx, "appfilter.time.time_mode", time_config->time_mode);
// Build days string from global weekday array (used as fallback)
char days_str[128] = {0};
int first = 1;
int i, j;
for (i = 0; i < 7; i++) {
if (time_config->days[i] == 1) {
if (!first) {
strcat(days_str, " ");
}
char tmp[8];
snprintf(tmp, sizeof(tmp), "%d", i);
strcat(days_str, tmp);
first = 0;
}
}
af_uci_set_value(uci_ctx, "appfilter.time.days", days_str);
if (time_config->time_mode == 0) {
// Manual mode: write time_list
af_uci_delete(uci_ctx, "appfilter.time.time");
int time_list_len = time_config->time_num;
for (i = 0; i < time_list_len; i++) {
char time_str[256] = {0};
// Build weekday string: "1,2,4,5"
char weekday_str[64] = {0};
first = 1;
for (j = 0; j < 7; j++) {
if (time_config->time_list[i].days[j] == 1) {
if (!first) {
strcat(weekday_str, ",");
}
char tmp[8];
snprintf(tmp, sizeof(tmp), "%d", j);
strcat(weekday_str, tmp);
first = 0;
}
}
// Format time: "HH:MM" with zero padding
char start_time_str[16] = {0};
char end_time_str[16] = {0};
snprintf(start_time_str, sizeof(start_time_str), "%02d:%02d",
time_config->time_list[i].start_time.hour,
time_config->time_list[i].start_time.min);
snprintf(end_time_str, sizeof(end_time_str), "%02d:%02d",
time_config->time_list[i].end_time.hour,
time_config->time_list[i].end_time.min);
// Format: "1,2,4,5;00:00-23:59"
snprintf(time_str, sizeof(time_str), "%s;%s-%s", weekday_str, start_time_str, end_time_str);
printf("time_str: %s\n", time_str);
af_uci_add_list(uci_ctx, "appfilter.time.time", time_str);
}
} else {
// Dynamic mode: write seg_time, deny_time, allow_time
af_uci_set_int_value(uci_ctx, "appfilter.time.deny_time", time_config->deny_time);
af_uci_set_int_value(uci_ctx, "appfilter.time.allow_time", time_config->allow_time);
char start_time_str[16] = {0};
char end_time_str[16] = {0};
// Format time: "HH:MM" with zero padding
snprintf(start_time_str, sizeof(start_time_str), "%02d:%02d",
time_config->seg_time.start_time.hour,
time_config->seg_time.start_time.min);
snprintf(end_time_str, sizeof(end_time_str), "%02d:%02d",
time_config->seg_time.end_time.hour,
time_config->seg_time.end_time.min);
af_uci_set_value(uci_ctx, "appfilter.time.start_time", start_time_str);
af_uci_set_value(uci_ctx, "appfilter.time.end_time", end_time_str);
}
af_uci_commit(uci_ctx, "appfilter");
uci_free_context(uci_ctx);
}
int af_load_time_config(af_time_config_t *t_config)
{
char time_list_buf[MAX_TIME_LIST_LEN] = {0};
char days_buf[128] = {0};
char start_time_buf[128] = {0};
char end_time_buf[128] = {0};
struct uci_context *ctx = uci_alloc_context();
int old_ver_config = 0;
printf("af_load_time_config: start\n");
if (!ctx)
return -1;
memset(t_config, 0, sizeof(af_time_config_t));
t_config->time_mode = af_uci_get_int_value(ctx, "appfilter.time.time_mode");
t_config->deny_time = af_uci_get_int_value(ctx, "appfilter.time.deny_time");
t_config->allow_time = af_uci_get_int_value(ctx, "appfilter.time.allow_time");
af_uci_get_value(ctx, "appfilter.time.start_time", start_time_buf, sizeof(start_time_buf));
af_uci_get_value(ctx, "appfilter.time.end_time", end_time_buf, sizeof(end_time_buf));
af_uci_get_value(ctx, "appfilter.time.days", days_buf, sizeof(days_buf));
sscanf(start_time_buf, "%d:%d", &t_config->seg_time.start_time.hour, &t_config->seg_time.start_time.min);
sscanf(end_time_buf, "%d:%d", &t_config->seg_time.end_time.hour, &t_config->seg_time.end_time.min);
t_config->time_num = 0;
// Parse global days (may be empty, continue even if empty)
char *saveptr2 = NULL;
char *p = strtok_r(days_buf, " ", &saveptr2);
if (p) {
do {
t_config->days[atoi(p)] = 1;
printf("af_load_time_config: day[%d] = 1\n", atoi(p));
p = strtok_r(NULL, " ", &saveptr2);
} while (p != NULL);
}
af_uci_get_list_value(ctx, "appfilter.time.time", time_list_buf, sizeof(time_list_buf), " ");
printf("af_load_time_config: time_list_buf from uci: %s\n", time_list_buf);
char time_list_copy[MAX_TIME_LIST_LEN] = {0};
strncpy(time_list_copy, time_list_buf, sizeof(time_list_copy) - 1);
// Use strtok_r to avoid issues with nested strtok calls
char *saveptr1 = NULL;
p = strtok_r(time_list_copy, " ", &saveptr1);
if (!p) {
printf("af_load_time_config: no time periods found\n");
goto EXIT;
}
int period_idx = 0;
do
{
printf("af_load_time_config: parsing period[%d]: %s\n", period_idx, p);
char *time_part = p;
// Initialize days array for this time period (use global days as default)
int i;
for (i = 0; i < 7; i++) {
t_config->time_list[t_config->time_num].days[i] = t_config->days[i];
}
// Check if format is new (with weekdays): "1,2,4,5;00:00-23:59"
char *semicolon = strchr(p, ';');
if (semicolon) {
// New format: parse weekdays and store in this time period's days array
char weekday_str[64] = {0};
strncpy(weekday_str, p, semicolon - p);
weekday_str[semicolon - p] = '\0';
time_part = semicolon + 1;
printf("af_load_time_config: period[%d] has weekdays: %s, time_part: %s\n", period_idx, weekday_str, time_part);
// Clear days array for this time period first
for (i = 0; i < 7; i++) {
t_config->time_list[t_config->time_num].days[i] = 0;
}
// Parse weekdays: "1,2,4,5" using strtok_r
char weekday_copy[64] = {0};
strncpy(weekday_copy, weekday_str, sizeof(weekday_copy) - 1);
char *saveptr2 = NULL;
char *wd = strtok_r(weekday_copy, ",", &saveptr2);
while (wd) {
int day_val = atoi(wd);
if (day_val >= 0 && day_val < 7) {
t_config->time_list[t_config->time_num].days[day_val] = 1;
printf("af_load_time_config: period[%d] set day %d\n", period_idx, day_val);
}
wd = strtok_r(NULL, ",", &saveptr2);
}
} else {
LOG_WARN("af_load_time_config: period[%d] no weekdays, using global days\n", period_idx);
old_ver_config = 1;
}
// If no semicolon, use global days (already copied above)
// Parse time: "00:00-23:59" or "1,2,4,5;00:00-23:59" (time_part already points to time part)
int ret = sscanf(time_part, "%d:%d-%d:%d", &t_config->time_list[t_config->time_num].start_time.hour,
&t_config->time_list[t_config->time_num].start_time.min, &t_config->time_list[t_config->time_num].end_time.hour, &t_config->time_list[t_config->time_num].end_time.min);
if (ret != 4) {
printf("af_load_time_config: period[%d] ERROR: failed to parse time from %s\n", period_idx, time_part);
} else {
printf("af_load_time_config: time[%d] %d:%d-%d:%d, days: ", t_config->time_num, t_config->time_list[t_config->time_num].start_time.hour, t_config->time_list[t_config->time_num].start_time.min,
t_config->time_list[t_config->time_num].end_time.hour, t_config->time_list[t_config->time_num].end_time.min);
for (i = 0; i < 7; i++) {
if (t_config->time_list[t_config->time_num].days[i]) {
printf("%d ", i);
}
}
printf("\n");
t_config->time_num++;
}
period_idx++;
} while (p = strtok_r(NULL, " ", &saveptr1));
printf("af_load_time_config: total periods loaded: %d\n", t_config->time_num);
// Load mode 2 daily limit config (if time_mode is 2)
if (t_config->time_mode == 2) {
int weekday;
for (weekday = 0; weekday < 7; weekday++) {
char uci_key[64] = {0};
snprintf(uci_key, sizeof(uci_key), "appfilter.time.daily_limit_%d", weekday);
char daily_limit_str[128] = {0};
af_uci_get_value(ctx, uci_key, daily_limit_str, sizeof(daily_limit_str));
// Initialize to default values
t_config->daily_limit[weekday].enable = 0;
t_config->daily_limit[weekday].am_time = 0;
t_config->daily_limit[weekday].pm_time = 0;
// Parse format: "enable:am_time:pm_time"
if (strlen(daily_limit_str) > 0) {
char *first_colon = strchr(daily_limit_str, ':');
if (first_colon) {
char *second_colon = strchr(first_colon + 1, ':');
if (second_colon) {
// New format: "enable:am_time:pm_time"
t_config->daily_limit[weekday].enable = atoi(daily_limit_str);
t_config->daily_limit[weekday].am_time = atoi(first_colon + 1);
t_config->daily_limit[weekday].pm_time = atoi(second_colon + 1);
} else {
// Old format: "am_time:pm_time"
t_config->daily_limit[weekday].enable = 1;
t_config->daily_limit[weekday].am_time = atoi(daily_limit_str);
t_config->daily_limit[weekday].pm_time = atoi(first_colon + 1);
}
} else {
t_config->daily_limit[weekday].enable = 1;
t_config->daily_limit[weekday].am_time = atoi(daily_limit_str);
}
}
printf("af_load_time_config: daily_limit[%d] enable=%d, am_time=%d, pm_time=%d\n",
weekday, t_config->daily_limit[weekday].enable,
t_config->daily_limit[weekday].am_time, t_config->daily_limit[weekday].pm_time);
}
}
EXIT:
uci_free_context(ctx);
return old_ver_config;
}
void af_load_global_config(af_global_config_t *config){
int ret = 0;
char lan_ifname[32] = {0};
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return;
ret = af_uci_get_int_value(ctx, "appfilter.global.enable");
if (ret < 0)
config->enable = 0;
else
config->enable = ret;
ret = af_uci_get_int_value(ctx, "appfilter.global.record_enable");
if (ret < 0)
config->record_enable = 0;
else
config->record_enable = ret;
ret = af_uci_get_int_value(ctx, "appfilter.global.user_mode");
if (ret < 0)
config->user_mode = 0;
else
config->user_mode = ret;
ret = af_uci_get_int_value(ctx, "appfilter.global.work_mode");
if (ret < 0)
config->work_mode = 0;
else
config->work_mode = ret;
ret = af_uci_get_int_value(ctx, "appfilter.global.tcp_rst");
if (ret < 0)
config->tcp_rst = 1;
else
config->tcp_rst = ret;
ret = af_uci_get_int_value(ctx, "appfilter.global.disable_hnat");
if (ret < 0)
config->disable_hnat = 1;
else
config->disable_hnat = ret;
ret = af_uci_get_int_value(ctx, "appfilter.global.auto_load_engine");
if (ret < 0)
config->auto_load_engine = 0;
else
config->auto_load_engine = ret;
ret = af_uci_get_int_value(ctx, "appfilter.global.disable_quic");
if (ret < 0)
config->disable_quic = 0;
else
config->disable_quic = ret;
ret = af_uci_get_int_value(ctx, "appfilter.global.app_filter_mode");
if (ret < 0)
config->app_filter_mode = 0; // Default to specified apps mode
else
config->app_filter_mode = ret;
ret = af_uci_get_value(ctx, "appfilter.global.lan_ifname", lan_ifname, sizeof(lan_ifname));
if (ret < 0)
strncpy(config->lan_ifname, "br-lan", sizeof(config->lan_ifname) - 1);
else
strncpy(config->lan_ifname, lan_ifname, sizeof(config->lan_ifname) - 1);
uci_free_context(ctx);
LOG_DEBUG("enable=%d, user_mode=%d, work_mode=%d, disable_quic=%d, app_filter_mode=%d\n", config->enable, config->user_mode, config->work_mode, config->disable_quic, config->app_filter_mode);
}
void af_load_config(af_config_t *config){
memset(config, 0, sizeof(af_config_t));
af_load_global_config(&config->global);
if (1 == af_load_time_config(&config->time)){
apply_time_config_to_uci(&config->time);
}
}
void update_oaf_proc_value(char *key, char *value){
char cmd_buf[128] = {0};
char file_path[128] = {0};
char old_value[128] = {0};
sprintf(file_path, "/proc/sys/oaf/%s", key);
if (af_read_file_value(file_path, old_value, sizeof(old_value)) == -1)
return;
if (strcmp(old_value, value) != 0){
sprintf(cmd_buf, "echo %s >/proc/sys/oaf/%s", value, key);
system(cmd_buf);
LOG_DEBUG("update %s %s-->%s\n", key, old_value, value);
}
}
void update_oaf_proc_u32_value(char *key, u_int32_t value){
char buf[32] = {0};
sprintf(buf, "%u", value);
update_oaf_proc_value(key, buf);
}
void update_lan_ip(void){
char ip_str[32] = {0};
char mask_str[32] = {0};
struct in_addr addr;
struct in_addr mask_addr;
char cmd_buf[128] = {0};
u_int32_t lan_ip = 0;
u_int32_t lan_mask = 0;
char lan_ifname[32] = {0};
char ip_cmd_buf[128] = {0};
char mask_cmd_buf[128] = {0};
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return;
int ret = af_uci_get_value(ctx, "appfilter.global.lan_ifname", lan_ifname, sizeof(lan_ifname) - 1);
if (ret != 0){
strcpy(lan_ifname, "br-lan");
}
sprintf(ip_cmd_buf, CMD_GET_LAN_IP_FMT , lan_ifname);
sprintf(mask_cmd_buf, CMD_GET_LAN_MASK_FMT , lan_ifname);
exec_with_result_line(ip_cmd_buf, ip_str, sizeof(ip_str));
if (strlen(ip_str) < MIN_INET_ADDR_LEN){
update_oaf_proc_u32_value("lan_ip", 0);
}
else{
inet_aton(ip_str, &addr);
lan_ip = addr.s_addr;
update_oaf_proc_u32_value("lan_ip", lan_ip);
}
exec_with_result_line(mask_cmd_buf, mask_str, sizeof(mask_str));
if (strlen(mask_str) < MIN_INET_ADDR_LEN){
update_oaf_proc_u32_value("lan_mask", 0);
}
else{
inet_aton(mask_str, &mask_addr);
lan_mask = mask_addr.s_addr;
update_oaf_proc_u32_value("lan_mask", lan_mask);
}
uci_free_context(ctx);
}
int af_check_time_manual(af_time_config_t *t_config) {
time_t now = time(NULL);
struct tm *current_time = localtime(&now);
int current_minutes = current_time->tm_hour * 60 + current_time->tm_min;
int current_wday = current_time->tm_wday;
int i;
for (i = 0; i < t_config->time_num; i++) {
if (!t_config->time_list[i].days[current_wday]) {
printf("current day %d not in time[%d] days\n", current_wday, i);
continue;
}
int start_minutes = t_config->time_list[i].start_time.hour * 60 + t_config->time_list[i].start_time.min;
int end_minutes = t_config->time_list[i].end_time.hour * 60 + t_config->time_list[i].end_time.min;
printf("check time: %02d:%02d-%02d:%02d\n",
t_config->time_list[i].start_time.hour, t_config->time_list[i].start_time.min,
t_config->time_list[i].end_time.hour, t_config->time_list[i].end_time.min);
if (current_minutes >= start_minutes && current_minutes <= end_minutes) {
printf("current time in time list\n");
g_af_status.match_time = 1;
return 1;
}
}
g_af_status.match_time = 0;
return 0;
}
int af_check_time_dynamic(af_time_config_t *t_config) {
return g_af_status.filter;
}
int update_dynamic_used_time(af_time_config_t *t_config){
if (t_config->time_mode != 1)
return -1;
time_t now = time(NULL);
struct tm *current_time = localtime(&now);
if (!t_config->days[current_time->tm_wday]) {
LOG_DEBUG("current day not in configured days\n");
af_init_time_status();
return -1;
}
int current_minutes = current_time->tm_hour * 60 + current_time->tm_min;
int start_minutes = t_config->seg_time.start_time.hour * 60 + t_config->seg_time.start_time.min;
int end_minutes = t_config->seg_time.end_time.hour * 60 + t_config->seg_time.end_time.min;
LOG_DEBUG("check seg_time: %02d:%02d-%02d:%02d\n",
t_config->seg_time.start_time.hour, t_config->seg_time.start_time.min,
t_config->seg_time.end_time.hour, t_config->seg_time.end_time.min);
if (!(current_minutes >= start_minutes && current_minutes <= end_minutes)) {
LOG_DEBUG("current time not in seg_time\n");
af_init_time_status();
return 0;
}
g_af_status.match_time = 1;
if (g_af_status.filter == 1) {
g_af_status.deny_time++;
if (g_af_status.deny_time >= t_config->deny_time) {
g_af_status.filter = 0;
g_af_status.deny_time = 0;
}
} else {
g_af_status.allow_time++;
if (g_af_status.allow_time >= t_config->allow_time) {
g_af_status.filter = 1;
g_af_status.allow_time = 0;
}
}
return 0;
}
int af_check_time_period_limit(af_time_config_t *t_config) {
int total_active_time = 0;
int selected_user_count = 0;
int i;
time_t now = time(NULL);
struct tm *current_time = localtime(&now);
int current_weekday = current_time->tm_wday;
int current_hour = current_time->tm_hour;
LOG_DEBUG("check period limit mode: weekday=%d, hour=%d\n", current_weekday, current_hour);
daily_limit_config_t *daily_limit = &t_config->daily_limit[current_weekday];
if (!daily_limit->enable) {
LOG_DEBUG("Time limit not enabled for weekday %d\n", current_weekday);
g_af_status.match_time = 0;
g_af_status.remain_time = 0;
g_af_status.used_time = 0;
g_af_status.period_blocked = 0;
return 0;
}
int max_allowed_time = 0;
int is_morning = (current_hour < 12);
if (is_morning) {
max_allowed_time = daily_limit->am_time;
LOG_DEBUG("Morning period: max_allowed_time=%d\n", max_allowed_time);
} else {
max_allowed_time = daily_limit->pm_time;
LOG_DEBUG("Afternoon period: max_allowed_time=%d\n", max_allowed_time);
}
if (max_allowed_time <= 0) {
LOG_DEBUG("No time limit set for current period\n");
g_af_status.match_time = 0;
g_af_status.remain_time = 0;
g_af_status.used_time = 0;
g_af_status.period_blocked = 0;
return 0;
}
check_all_users_period_time();
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) {
dev_node_t *node = dev_hash_table[i];
while (node) {
if (node->is_selected) {
if (is_morning) {
total_active_time += node->today_am_active_time;
LOG_DEBUG("Selected user %s (online=%d): today_am_active_time=%d, total=%d\n",
node->mac, node->online, node->today_am_active_time, total_active_time);
} else {
total_active_time += node->today_pm_active_time;
LOG_DEBUG("Selected user %s (online=%d): today_pm_active_time=%d, total=%d\n",
node->mac, node->online, node->today_pm_active_time, total_active_time);
}
if (node->online) {
selected_user_count++;
}
}
node = node->next;
}
}
g_af_status.used_time = total_active_time;
int remain_time = max_allowed_time - total_active_time;
if (remain_time < 0) {
remain_time = 0;
}
g_af_status.remain_time = remain_time;
LOG_DEBUG("Selected users count: %d, total_active_time=%d, max_allowed=%d, remain_time=%d\n",
selected_user_count, total_active_time, max_allowed_time, remain_time);
if (total_active_time >= max_allowed_time) {
g_af_status.match_time = 1;
g_af_status.period_blocked = 1;
LOG_DEBUG("Period limit mode: enable filter (total time exceeded: %d >= %d)\n",
total_active_time, max_allowed_time);
return 1;
} else {
g_af_status.match_time = 1;
g_af_status.period_blocked = 0;
LOG_DEBUG("Period limit mode: disable filter (total time: %d < %d, remain: %d)\n",
total_active_time, max_allowed_time, remain_time);
return 0;
}
}
int af_check_time_valid(af_time_config_t *t_config) {
time_t now = time(NULL);
struct tm *current_time = localtime(&now);
if (t_config->time_mode == 0) {
return af_check_time_manual(t_config);
} else if (t_config->time_mode == 1) {
return af_check_time_dynamic(t_config);
} else if (t_config->time_mode == 2) {
return af_check_time_period_limit(t_config);
}else{
return 0;
}
}
void update_oaf_status(void){
int ret = 0;
int cur_enable = 0;
if(g_af_config.global.enable == 1){
ret = af_check_time_valid(&g_af_config.time);
}
update_oaf_proc_value("enable", ret == 1 ? "1" : "0");
}
void update_oaf_record_status(void){
update_oaf_proc_value("record_enable", g_af_config.global.record_enable==1?"1":"0");
}
void update_oaf_disable_quic_status(void){
update_oaf_proc_value("disable_quic", g_af_config.global.disable_quic==1?"1":"0");
}
void update_oaf_app_filter_mode_status(void){
update_oaf_proc_value("app_filter_mode", g_af_config.global.app_filter_mode==1?"1":"0");
}
int af_nl_clean_feature(void){
af_msg_t msg;
if (appfilter_nl_fd.fd < 0){
return -1;
}
msg.action = AF_MSG_CLEAN_FEATURE;
send_msg_to_kernel(appfilter_nl_fd.fd,(void *)&msg, sizeof(msg));
return 0;
}
int af_nl_add_feature(char *feature){
char msg_buf[1024] = {0};
if (appfilter_nl_fd.fd < 0){
return -1;
}
char *p_data = msg_buf + sizeof(af_msg_t);
memset(msg_buf, 0, sizeof(msg_buf));
af_msg_t *hdr = (af_msg_t *)msg_buf;
hdr->action = AF_MSG_ADD_FEATURE;
strncpy(p_data, feature, strlen(feature));
send_msg_to_kernel(appfilter_nl_fd.fd,(void *)msg_buf, sizeof(af_msg_t) + strlen(feature) + 1);
return 0;
}
int af_load_feature_to_kernel(void){
char line_buf[MAX_FEATURE_LINE_LEN] = {0};
FILE *fp = fopen("/tmp/feature.cfg", "r");
if (!fp)
{
printf("open file failed\n");
return -1;
}
if (af_nl_clean_feature() < 0){
return -1;
}
while (fgets(line_buf, sizeof(line_buf), fp))
{
str_trim(line_buf);
if (strlen(line_buf) < 8)
continue;
if (strstr(line_buf, "#"))
continue;
if (strlen(line_buf) >= MAX_FEATURE_LINE_LEN - 1){
continue;
}
af_nl_add_feature(line_buf);
}
fclose(fp);
return 0;
}
int reload_feature(void){
system("gen_class.sh /tmp/feature.cfg");
init_app_name_table();
init_app_class_name_table();
if (af_load_feature_to_kernel() < 0){
LOG_ERROR("Failed to load feature to kernel\n");
return -1;
}
clean_invalid_app_records();
clear_device_app_statistics();
LOG_WARN("reload feature success\n");
g_feature_update_time = get_timestamp();
return 0;
}
void check_date_change(void)
{
static int last_day = -1;
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
int current_day = tm_info->tm_mday;
if (last_day != current_day )
{
LOG_WARN("day changed: %d -> %d\n",last_day, current_day);
if (last_day != -1){
clear_device_app_statistics();
reset_all_users_today_active_time();
reset_all_users_today_flow();
}
last_day = current_day;
}
}
void oaf_timeout_handler(struct uloop_timeout *t)
{
static int count = 0;
if (count % 10 == 0){
update_dev_list();
update_oaf_status();
}
if (count % 60 == 0){
LOG_DEBUG("begin check dev count = %d\n", count);
check_dev_visit_info_expire();
flush_expire_visit_info();
update_dynamic_used_time(&g_af_config.time);
update_oaf_status();
update_lan_ip();
if (check_dev_expire()){
flush_dev_expire_node();
}
check_date_change();
check_all_users_period_time();
dump_dev_list();
}
if (count % 300 == 0 && count > 0 && g_af_config.time.time_mode == 2){
save_user_time_to_file();
}
if (g_oaf_config_change == 1){
LOG_WARN("config changed\n");
update_lan_ip();
af_load_config(&g_af_config);
update_dev_selected_flag();
update_dynamic_used_time(&g_af_config.time);
update_oaf_status();
update_oaf_record_status();
update_oaf_disable_quic_status();
update_oaf_app_filter_mode_status();
g_oaf_config_change = 0;
}
if (appfilter_nl_fd.fd < 0 && access("/proc/sys/oaf", F_OK) == 0){
appfilter_nl_fd.fd = appfilter_nl_init();
if (appfilter_nl_fd.fd > 0){
uloop_fd_add(&appfilter_nl_fd, ULOOP_READ);
system("oaf_rule reload &");
LOG_INFO("netlink connect success\n");
}
}
if (g_feature_update == 1 && appfilter_nl_fd.fd > 0){
if (0 == reload_feature()){
g_feature_update = 0;
}
}
count++;
uloop_timeout_set(t, 1000);
}
void af_load_engine(void){
if (g_af_config.global.auto_load_engine == 1){
if (access("/lib/modules/oaf.ko", F_OK) == 0) {
system("insmod /lib/modules/oaf.ko");
LOG_WARN("insmod /lib/modules/oaf.ko");
} else {
system("modprobe oaf");
LOG_WARN("modprobe oaf");
}
}
else{
LOG_WARN("auto load disabled, not load oaf.ko\n");
}
}
void handle_sigusr1(int sig) {
LOG_WARN("Received SIGUSR1 signal\n");
g_feature_update = 1;
}
void handle_sigusr2(int sig) {
LOG_INFO("Received SIGUSR2 signal\n");
if (current_log_level >= LOG_LEVEL_ERROR)
current_log_level = LOG_LEVEL_DEBUG;
else
current_log_level++;
LOG_WARN("change log level to %d\n", current_log_level);
}
int main(int argc, char **argv)
{
int ret = 0;
LOG_INFO("appfilter start");
g_feature_update = 1;
af_load_config(&g_af_config);
af_load_engine();
af_init_status();
uloop_init();
signal(SIGUSR1, handle_sigusr1);
signal(SIGUSR2, handle_sigusr2);
signal(SIGCHLD, SIG_IGN);
init_dev_node_htable();
load_user_time_from_file();
if (appfilter_ubus_init() < 0)
{
LOG_ERROR("Failed to connect to ubus\n");
return 1;
}
appfilter_nl_fd.fd = -1;
uloop_timeout_set(&dev_tm, 5000);
uloop_timeout_add(&dev_tm);
uloop_run();
uloop_done();
return 0;
}
+107
View File
@@ -0,0 +1,107 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/time.h>
char *str_trim(char *s) {
char *start, *last, *bk;
int len;
start = s;
while (isspace(*start))
start++;
bk = last = s + strlen(s) - 1;
while (last > start && isspace(*last))
last--;
if ((s != start) || (bk != last)) {
len = last - start + 1;
strncpy(s, start, len);
s[len] = '\0';
}
return s;
}
int exec_with_result_line(char *cmd, char *result, int len)
{
FILE *fp = NULL;
if (!cmd || !result || !len)
return -1;
fp = popen(cmd, "r");
if (!fp)
return -1;
fgets(result, len, fp);
str_trim(result);
pclose(fp);
return 0;
}
unsigned int get_timestamp(void)
{
struct timeval cur_time;
gettimeofday(&cur_time, NULL);
return cur_time.tv_sec;
}
int check_same_network(char *ip1, char *netmask, char *ip2) {
struct in_addr addr1, addr2, mask;
if (inet_pton(AF_INET, ip1, &addr1) != 1) {
printf("Invalid IP address: %s\n", ip1);
return -1;
}
if (inet_pton(AF_INET, netmask, &mask) != 1) {
printf("Invalid netmask: %s\n", netmask);
return -1;
}
if (inet_pton(AF_INET, ip2, &addr2) != 1) {
printf("Invalid IP address: %s\n", ip2);
return -1;
}
if ((addr1.s_addr & mask.s_addr) == (addr2.s_addr & mask.s_addr)) {
return 1;
} else {
return 0;
}
}
int af_read_file_value(const char *file_path, char *value, int value_len) {
FILE *file = fopen(file_path, "r");
if (!file) {
//perror("Failed to open file");
return -1;
}
if (fgets(value, value_len, file) == NULL) {
perror("Failed to read line from file");
fclose(file);
return -2;
}
size_t len = strlen(value);
if (len > 0 && value[len - 1] == '\n') {
value[len - 1] = '\0';
}
fclose(file);
return 0;
}
int af_read_file_int_value(const char *file_path, int *value) {
char line_buf[128] = {0};
if (af_read_file_value(file_path, line_buf, sizeof(line_buf)) < 0){
return -1;
}
*value = atoi(line_buf);
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef __UTILS_H__
#define __UTILS_H__
char *str_trim(char *s);
int exec_with_result_line(char *cmd, char *result, int len);
int check_same_network(char *ip1, char *netmask, char *ip2);
int af_read_file_value(const char *file_path, char *value, int value_len);
int af_read_file_int_value(const char *file_path, int *value);
unsigned int get_timestamp(void);
#endif