mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-30 19:45:13 +08:00
🌴 Sync 2026-03-05 23:51:42
This commit is contained in:
1 parent
0d155c70be
commit
32600acc60
14258 files changed
+3954570
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in new issue
Block a user