update 2026-08-26 20:40:17

This commit is contained in:
action committed 2026-08-26 20:40:17 +08:00
1 parent d64e131165
commit 1bfa02b7c5
397 files changed
+52332 -16577

No files matched your search

+3 -1
View File
@@ -1,2 +1,4 @@
oaf-objs := app_filter.o af_utils.o af_config.o regexp.o cJSON.o af_log.o af_client.o af_client_fs.o af_conntrack.o af_rule_config.o af_user_config.o af_whitelist_config.o
oaf-objs := fwx_main.o fwx_utils.o regexp.o k_json.o fwx_log.o fwx_client.o fwx_client_fs.o
oaf-objs += fwx_conntrack.o fwx_mac.o fwx_config.o fwx_mac_filter.o
oaf-objs += fwx_app_filter.o
obj-m += oaf.o
-293
View File
@@ -1,293 +0,0 @@
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/netlink.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/version.h>
#include <net/sock.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/netfilter_bridge.h>
#include <linux/version.h>
#include <linux/time.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <linux/etherdevice.h>
#include <linux/proc_fs.h>
#include <linux/sysctl.h>
#include "af_utils.h"
#include "cJSON.h"
#include "af_log.h"
#include "af_client.h"
#include "af_client_fs.h"
extern struct list_head af_client_list_table[MAX_AF_CLIENT_HASH_SIZE];
struct af_client_iter_state
{
unsigned int bucket;
void *head;
};
static void *af_client_get_first(struct seq_file *seq)
{
struct af_client_iter_state *st = seq->private;
for (st->bucket = 0; st->bucket < MAX_AF_CLIENT_HASH_SIZE; st->bucket++)
{
if (!list_empty(&(af_client_list_table[st->bucket])))
{
st->head = &(af_client_list_table[st->bucket]);
return af_client_list_table[st->bucket].next;
}
}
return NULL;
}
static void *af_client_get_next(struct seq_file *seq,
void *head)
{
struct af_client_iter_state *st = seq->private;
struct hlist_node *node = (struct hlist_node *)head;
node = node->next;
if (node != st->head)
{
return node;
}
else
{
st->bucket++;
for (; st->bucket < MAX_AF_CLIENT_HASH_SIZE; st->bucket++)
{
if (!list_empty(&(af_client_list_table[st->bucket])))
{
st->head = &(af_client_list_table[st->bucket]);
return af_client_list_table[st->bucket].next;
}
}
return NULL;
}
}
static void *af_client_get_idx(struct seq_file *seq, loff_t pos)
{
void *head = af_client_get_first(seq);
if (head)
while (pos && (head = af_client_get_next(seq, head)))
pos--;
return pos ? NULL : head;
}
static void *af_client_seq_start(struct seq_file *s, loff_t *pos)
{
AF_CLIENT_LOCK_R();
if (*pos == 0)
{
return SEQ_START_TOKEN;
}
return af_client_get_idx(s, *pos - 1);
}
static void *af_client_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
if (v == SEQ_START_TOKEN)
return af_client_get_idx(s, 0);
return af_client_get_next(s, v);
}
static void af_client_seq_stop(struct seq_file *s, void *v)
{
AF_CLIENT_UNLOCK_R();
}
static int af_client_seq_show(struct seq_file *s, void *v)
{
unsigned char mac_str[32] = {0};
unsigned char ip_str[32] = {0};
unsigned char ipv6_str[128];
static int index = 0;
af_client_info_t *node = (af_client_info_t *)v;
if (v == SEQ_START_TOKEN)
{
index = 0;
seq_printf(s, "%-4s %-20s %-20s %-32s %-16s %-16s\n", "Id", "Mac", "IP", "IPv6", "UpRate", "DownRate");
return 0;
}
index++;
sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac));
sprintf(ip_str, "%pI4", &node->ip);
ipv6_to_str(&node->ipv6, ipv6_str);
seq_printf(s, "%-4d %-20s %-20s %-32s %-16d %-16d\n", index, mac_str, ip_str, ipv6_str, node->rate.up_rate, node->rate.down_rate);
return 0;
}
static const struct seq_operations nf_client_seq_ops = {
.start = af_client_seq_start,
.next = af_client_seq_next,
.stop = af_client_seq_stop,
.show = af_client_seq_show};
static int af_client_open(struct inode *inode, struct file *file)
{
struct seq_file *seq;
struct af_client_iter_state *iter;
int err;
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
if (!iter)
return -ENOMEM;
err = seq_open(file, &nf_client_seq_ops);
if (err)
{
kfree(iter);
return err;
}
seq = file->private_data;
seq->private = iter;
return 0;
}
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0)
static const struct file_operations af_client_fops = {
.owner = THIS_MODULE,
.open = af_client_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
#else
static const struct proc_ops af_client_fops = {
.proc_flags = PROC_ENTRY_PERMANENT,
.proc_read = seq_read,
.proc_open = af_client_open,
.proc_lseek = seq_lseek,
.proc_release = seq_release_private,
};
#endif
#define AF_CLIENT_PROC_STR "af_client"
static int af_visiting_seq_show(struct seq_file *s, void *v)
{
unsigned char mac_str[32] = {0};
static int index = 0;
af_client_info_t *node = (af_client_info_t *)v;
if (v == SEQ_START_TOKEN)
{
index = 0;
seq_printf(s, "%-20s %-12s %-32s\n", "Mac", "Appid", "Url");
return 0;
}
index++;
sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac));
int visiting_app = 0;
char visiting_url[64] = {0};
if (af_get_timestamp_sec() - node->visiting.app_time < 120){
visiting_app = node->visiting.visiting_app;
}
if ( af_get_timestamp_sec() - node->visiting.url_time < 120 ){
strncpy(visiting_url, node->visiting.visiting_url, sizeof(visiting_url));
}
else{
strcpy(visiting_url, "none");
}
seq_printf(s, "%-20s %-12d %-32s\n", mac_str, visiting_app, visiting_url);
return 0;
}
static const struct seq_operations nf_visiting_seq_ops = {
.start = af_client_seq_start,
.next = af_client_seq_next,
.stop = af_client_seq_stop,
.show = af_visiting_seq_show
};
static int af_visiting_open(struct inode *inode, struct file *file)
{
struct seq_file *seq;
struct af_client_iter_state *iter;
int err;
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
if (!iter)
return -ENOMEM;
err = seq_open(file, &nf_visiting_seq_ops);
if (err)
{
kfree(iter);
return err;
}
seq = file->private_data;
seq->private = iter;
return 0;
}
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0)
static const struct file_operations af_visiting_fops = {
.owner = THIS_MODULE,
.open = af_visiting_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
#else
static const struct proc_ops af_visiting_fops = {
.proc_flags = PROC_ENTRY_PERMANENT,
.proc_read = seq_read,
.proc_open = af_visiting_open,
.proc_lseek = seq_lseek,
.proc_release = seq_release_private,
};
#endif
#define AF_VISIT_INFO "af_visit"
int init_af_client_procfs(void)
{
struct proc_dir_entry *pde;
struct net *net = &init_net;
pde = proc_create(AF_CLIENT_PROC_STR, 0440, net->proc_net, &af_client_fops);
if (!pde)
{
AF_ERROR("nf_client proc file created error\n");
return -1;
}
pde = proc_create(AF_VISIT_INFO, 0440, net->proc_net, &af_visiting_fops);
if (!pde)
{
AF_ERROR("client visiting proc file created error\n");
return -1;
}
return 0;
}
void finit_af_client_procfs(void)
{
struct net *net = &init_net;
remove_proc_entry(AF_CLIENT_PROC_STR, net->proc_net);
remove_proc_entry(AF_VISIT_INFO, net->proc_net);
}
-7
View File
@@ -1,7 +0,0 @@
#ifndef __AF_CLIENT_FS_H__
#define __AF_CLIENT_FS_H__
int init_af_client_procfs(void);
void finit_af_client_procfs(void);
#endif
-234
View File
@@ -1,234 +0,0 @@
#include <linux/init.h>
#include <linux/module.h>
#include <net/tcp.h>
#include <linux/netfilter.h>
#include <net/netfilter/nf_conntrack.h>
#include <linux/skbuff.h>
#include <net/ip.h>
#include <linux/types.h>
#include <net/sock.h>
#include <linux/etherdevice.h>
#include <linux/cdev.h>
#include <linux/vmalloc.h>
#include <linux/device.h>
#include <linux/version.h>
#include "cJSON.h"
#include "app_filter.h"
#include "af_config.h"
#include "af_utils.h"
#include "af_log.h"
#include "af_rule_config.h"
#include "af_user_config.h"
#include "af_whitelist_config.h"
#define AF_DEV_NAME "appfilter"
extern u_int32_t g_update_jiffies;
static struct mutex af_cdev_mutex;
struct af_config_dev
{
dev_t id;
struct cdev char_dev;
struct class *c;
};
struct af_config_dev g_af_dev;
struct af_cdev_file
{
size_t size;
char buf[256 << 10];
};
static struct af_config_interface af_config_interfaces[] = {
{AF_CMD_ADD_APPID, af_config_add_appid, "Add App ID"},
{AF_CMD_DEL_APPID, af_config_del_appid, "Delete App ID"},
{AF_CMD_CLEAN_APPID, af_config_clean_appid, "Clean App ID"},
{AF_CMD_SET_MAC_LIST, af_config_set_mac_list, "Set MAC List"},
{AF_CMD_SET_WHITELIST_MAC_LIST, af_config_set_whitelist_mac_list, "Set Whitelist MAC List"},
{0, NULL, NULL}
};
static af_config_handler_t af_find_handler(enum AF_CONFIG_CMD cmd)
{
struct af_config_interface *interface = af_config_interfaces;
while (interface->handler != NULL) {
if (interface->cmd == cmd) {
return interface->handler;
}
interface++;
}
return NULL;
}
/*
add:
{
"op":1,
"data":{
"apps":[]
}
}
clean
{
"op":3,
}
*/
static int af_config_handle(char *config, unsigned int len)
{
cJSON *config_obj = NULL;
cJSON *cmd_obj = NULL;
cJSON *data_obj = NULL;
int ret = 0;
af_config_handler_t handler = NULL;
if (!config || len == 0)
{
AF_ERROR("config or len is invalid\n");
return -1;
}
AF_DEBUG("config = %s\n", config);
config_obj = cJSON_Parse(config);
if (!config_obj)
{
AF_ERROR("config_obj is NULL\n");
return -1;
}
cmd_obj = cJSON_GetObjectItem(config_obj, "op");
if (!cmd_obj)
{
AF_ERROR("not find op object\n");
cJSON_Delete(config_obj);
return -1;
}
data_obj = cJSON_GetObjectItem(config_obj, "data");
handler = af_find_handler(cmd_obj->valueint);
if (handler) {
ret = handler(data_obj);
g_update_jiffies = jiffies;
cJSON_Delete(config_obj);
return ret;
} else {
AF_ERROR("invalid cmd %d\n", cmd_obj->valueint);
cJSON_Delete(config_obj);
return -1;
}
}
static int af_cdev_open(struct inode *inode, struct file *filp)
{
struct af_cdev_file *file;
file = vzalloc(sizeof(*file));
if (!file)
return -EINVAL;
mutex_lock(&af_cdev_mutex);
filp->private_data = file;
return 0;
}
static ssize_t af_cdev_read(struct file *filp, char *buf, size_t count, loff_t *off)
{
return 0;
}
static int af_cdev_release(struct inode *inode, struct file *filp)
{
struct af_cdev_file *file = filp->private_data;
AF_DEBUG("config size: %d,data = %s\n", (int)file->size, file->buf);
af_config_handle(file->buf, file->size);
filp->private_data = NULL;
mutex_unlock(&af_cdev_mutex);
vfree(file);
return 0;
}
static ssize_t af_cdev_write(struct file *filp, const char *buffer, size_t count, loff_t *off)
{
struct af_cdev_file *file = filp->private_data;
int ret;
if (file->size + count > sizeof(file->buf))
{
AF_ERROR("config overflow, cur_size: %d, block_size: %d, max_size: %d",
(int)file->size, (int)count, (int)sizeof(file->buf));
return -EINVAL;
}
ret = copy_from_user(file->buf + file->size, buffer, count);
if (ret != 0)
return -EINVAL;
file->size += count;
return count;
}
static struct file_operations af_cdev_ops = {
owner : THIS_MODULE,
release : af_cdev_release,
open : af_cdev_open,
write : af_cdev_write,
read : af_cdev_read,
};
int af_register_dev(void)
{
struct device *dev;
int res;
mutex_init(&af_cdev_mutex);
res = alloc_chrdev_region(&g_af_dev.id, 0, 1, AF_DEV_NAME);
if (res != 0)
{
return -EINVAL;
}
cdev_init(&g_af_dev.char_dev, &af_cdev_ops);
res = cdev_add(&g_af_dev.char_dev, g_af_dev.id, 1);
if (res < 0)
{
goto REGION_OUT;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
g_af_dev.c = class_create(THIS_MODULE, AF_DEV_NAME);
#else
g_af_dev.c = class_create(AF_DEV_NAME);
#endif
if (IS_ERR_OR_NULL(g_af_dev.c))
{
goto CDEV_OUT;
}
dev = device_create(g_af_dev.c, NULL, g_af_dev.id, NULL, AF_DEV_NAME);
if (IS_ERR_OR_NULL(dev))
{
goto CLASS_OUT;
}
AF_INFO("register char dev....ok\n");
return 0;
CLASS_OUT:
class_destroy(g_af_dev.c);
CDEV_OUT:
cdev_del(&g_af_dev.char_dev);
REGION_OUT:
unregister_chrdev_region(g_af_dev.id, 1);
AF_ERROR("register char dev....fail\n");
return -EINVAL;
}
void af_unregister_dev(void)
{
device_destroy(g_af_dev.c, g_af_dev.id);
class_destroy(g_af_dev.c);
cdev_del(&g_af_dev.char_dev);
unregister_chrdev_region(g_af_dev.id, 1);
AF_INFO("unregister char dev....ok\n");
}
-33
View File
@@ -1,33 +0,0 @@
#ifndef __AF_CONFIG_H__
#define __AF_CONFIG_H__
#include "app_filter.h"
enum AF_CONFIG_CMD
{
AF_CMD_ADD_APPID = 1,
AF_CMD_DEL_APPID,
AF_CMD_CLEAN_APPID,
AF_CMD_SET_MAC_LIST,
AF_CMD_SET_WHITELIST_MAC_LIST,
};
typedef int (*af_config_handler_t)(cJSON *data);
struct af_config_interface
{
enum AF_CONFIG_CMD cmd;
af_config_handler_t handler;
const char *description;
};
int af_register_dev(void);
void af_unregister_dev(void);
int af_config_add_appid(cJSON *data);
int af_config_del_appid(cJSON *data);
int af_config_clean_appid(cJSON *data);
int af_config_set_mac_list(cJSON *data);
int af_config_set_whitelist_mac_list(cJSON *data);
#endif
-44
View File
@@ -1,44 +0,0 @@
#ifndef __AF_DEBUG_H__
#define __AF_DEBUG_H__
extern int af_log_lvl;
extern int af_test_mode;
extern int af_work_mode;
extern int g_oaf_filter_enable;
extern int g_oaf_record_enable;
extern int g_by_pass_accl;
extern unsigned int af_lan_ip;
extern unsigned int af_lan_mask;
extern int g_feature_init;
extern int g_user_mode;
extern int g_disable_quic;
extern int g_app_filter_mode;
extern char g_lan_ifname[64];
extern int g_tcp_rst;
#define LOG(level, fmt, ...) do { \
if ((level) <= af_log_lvl) { \
printk(fmt, ##__VA_ARGS__); \
} \
} while (0)
#define LLOG(level, fmt, ...) do { \
if ((level) <= af_log_lvl) { \
pr_info_ratelimited(fmt, ##__VA_ARGS__); \
} \
} while (0)
#define AF_ERROR(...) LOG(0, ##__VA_ARGS__)
#define AF_WARN(...) LOG(1, ##__VA_ARGS__)
#define AF_INFO(...) LOG(2, ##__VA_ARGS__)
#define AF_DEBUG(...) LOG(3, ##__VA_ARGS__)
#define AF_LMT_ERROR(...) LLOG(0, ##__VA_ARGS__)
#define AF_LMT_WARN(...) LLOG(1, ##__VA_ARGS__)
#define AF_LMT_INFO(...) LLOG(2, ##__VA_ARGS__)
#define AF_LMT_DEBUG(...) LLOG(3, ##__VA_ARGS__)
#define TEST_MODE() (af_test_mode)
int af_log_init(void);
int af_log_exit(void);
#endif
-100
View File
@@ -1,100 +0,0 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/version.h>
#include "cJSON.h"
#include "app_filter.h"
#include "af_utils.h"
#include "af_log.h"
#include "af_config.h"
#include "af_rule_config.h"
#define AF_MAX_APP_TYPE_NUM 32
#define AF_MAX_APP_NUM 512
DEFINE_RWLOCK(af_rule_lock);
#define af_rule_read_lock() read_lock_bh(&af_rule_lock);
#define af_rule_read_unlock() read_unlock_bh(&af_rule_lock);
#define af_rule_write_lock() write_lock_bh(&af_rule_lock);
#define af_rule_write_unlock() write_unlock_bh(&af_rule_lock);
extern u_int32_t g_update_jiffies;
char g_app_id_array[AF_MAX_APP_TYPE_NUM][AF_MAX_APP_NUM] = {0};
static int af_change_app_status(cJSON *data_obj, int status)
{
int i;
int id;
int type;
cJSON *appid_arr = NULL;
if (!data_obj)
{
AF_ERROR("data obj is null\n");
return -1;
}
appid_arr = cJSON_GetObjectItem(data_obj, "apps");
if (!appid_arr)
{
AF_ERROR("apps obj is null\n");
return -1;
}
for (i = 0; i < cJSON_GetArraySize(appid_arr); i++)
{
cJSON *appid_obj = cJSON_GetArrayItem(appid_arr, i);
if (!appid_obj)
return -1;
id = AF_APP_ID(appid_obj->valueint);
type = AF_APP_TYPE(appid_obj->valueint);
af_rule_write_lock();
g_app_id_array[type][id] = status;
af_rule_write_unlock();
}
return 0;
}
void af_init_app_status(void)
{
int i, j;
for (i = 0; i < AF_MAX_APP_TYPE_NUM; i++)
{
for (j = 0; j < AF_MAX_APP_NUM; j++)
{
af_rule_write_lock();
g_app_id_array[i][j] = AF_FALSE;
af_rule_write_unlock();
}
}
}
int af_get_app_status(int appid)
{
int status = 0;
int id = AF_APP_ID(appid);
int type = AF_APP_TYPE(appid);
af_rule_read_lock();
status = g_app_id_array[type][id];
af_rule_read_unlock();
return status;
}
int af_config_add_appid(cJSON *data)
{
return af_change_app_status(data, 1);
}
int af_config_del_appid(cJSON *data)
{
return af_change_app_status(data, 0);
}
int af_config_clean_appid(cJSON *data)
{
af_init_app_status();
return 0;
}
-9
View File
@@ -1,9 +0,0 @@
#ifndef __AF_RULE_CONFIG_H__
#define __AF_RULE_CONFIG_H__
#include "app_filter.h"
#include "af_utils.h"
#include "af_log.h"
void af_init_app_status(void);
int af_get_app_status(int appid);
#endif
-133
View File
@@ -1,133 +0,0 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/version.h>
#include "app_filter.h"
#include "af_utils.h"
#include "af_log.h"
#include "cJSON.h"
#include "af_config.h"
#include "af_whitelist_config.h"
#include "af_user_config.h"
DEFINE_RWLOCK(af_mac_lock);
u32 total_mac = 0;
struct list_head af_mac_htable[MAX_AF_MAC_HASH_SIZE];
void af_mac_list_init(void)
{
int i;
write_lock_bh(&af_mac_lock);
for (i = 0; i < MAX_AF_MAC_HASH_SIZE; i++)
{
INIT_LIST_HEAD(&af_mac_htable[i]);
}
write_unlock_bh(&af_mac_lock);
}
void af_mac_list_flush(void)
{
int i;
af_mac_node_t *p = NULL;
char mac_str[32] = {0};
write_lock_bh(&af_mac_lock);
for (i = 0; i < MAX_AF_MAC_HASH_SIZE; i++)
{
while (!list_empty(&af_mac_htable[i]))
{
p = list_first_entry(&af_mac_htable[i], af_mac_node_t, list);
memset(mac_str, 0x0, sizeof(mac_str));
sprintf(mac_str, MAC_FMT, MAC_ARRAY(p->mac));
list_del(&(p->list));
kfree(p);
}
}
total_mac = 0;
write_unlock_bh(&af_mac_lock);
}
af_mac_node_t *af_mac_find(unsigned char *mac)
{
af_mac_node_t *node;
unsigned int index;
index = hash_mac(mac);
read_lock_bh(&af_mac_lock);
list_for_each_entry(node, &af_mac_htable[index], list)
{
if (0 == memcmp(node->mac, mac, 6))
{
read_unlock_bh(&af_mac_lock);
return node;
}
}
read_unlock_bh(&af_mac_lock);
return NULL;
}
af_mac_node_t *af_mac_add(unsigned char *mac)
{
af_mac_node_t *node;
int index = 0;
node = (af_mac_node_t *)kmalloc(sizeof(af_mac_node_t), GFP_ATOMIC);
if (node == NULL)
{
return NULL;
}
memset(node, 0, sizeof(af_mac_node_t));
memcpy(node->mac, mac, MAC_ADDR_LEN);
index = hash_mac(mac);
printk("add user mac=" MAC_FMT "\n", MAC_ARRAY(node->mac));
total_mac++;
write_lock_bh(&af_mac_lock);
list_add(&(node->list), &af_mac_htable[index]);
write_unlock_bh(&af_mac_lock);
return node;
}
static __maybe_unused int is_user_match_enable(void)
{
return total_mac > 0;
}
int af_config_set_mac_list(cJSON *data_obj)
{
int i;
cJSON *mac_arr = NULL;
u8 mac_hex[MAC_ADDR_LEN] = {0};
if (!data_obj)
{
AF_ERROR("data obj is null\n");
return -1;
}
mac_arr = cJSON_GetObjectItem(data_obj, "mac_list");
if (!mac_arr)
{
AF_ERROR("mac_list obj is null\n");
return -1;
}
af_mac_list_flush();
for (i = 0; i < cJSON_GetArraySize(mac_arr); i++)
{
cJSON *mac_obj = cJSON_GetArrayItem(mac_arr, i);
if (!mac_obj)
{
AF_ERROR("mac obj is null\n");
return -1;
}
if (-1 == mac_to_hex(mac_obj->valuestring, mac_hex))
{
continue;
}
af_mac_add(mac_hex);
}
AF_DEBUG("## mac num = %d\n", total_mac);
return 0;
}
-16
View File
@@ -1,16 +0,0 @@
#ifndef __AF_USER_CONFIG_H__
#define __AF_USER_CONFIG_H__
#include "app_filter.h"
#include "af_utils.h"
typedef struct af_mac_node {
struct list_head list;
unsigned char mac[MAC_ADDR_LEN];
}af_mac_node_t;
void af_mac_list_init(void);
void af_mac_list_flush(void);
af_mac_node_t *af_mac_find(unsigned char *mac);
af_mac_node_t *af_mac_add(unsigned char *mac);
#endif
-122
View File
@@ -1,122 +0,0 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/version.h>
#include "app_filter.h"
#include "af_utils.h"
#include "af_log.h"
#include "cJSON.h"
#include "af_whitelist_config.h"
DEFINE_RWLOCK(af_whitelist_mac_lock);
struct list_head af_whitelist_mac_htable[MAX_AF_MAC_HASH_SIZE];
void af_whitelist_mac_init(void)
{
int i;
write_lock_bh(&af_whitelist_mac_lock);
for (i = 0; i < MAX_AF_MAC_HASH_SIZE; i++)
{
INIT_LIST_HEAD(&af_whitelist_mac_htable[i]);
}
write_unlock_bh(&af_whitelist_mac_lock);
}
void af_whitelist_mac_flush(void)
{
int i;
af_whitelist_mac_node_t *p = NULL;
char mac_str[32] = {0};
write_lock_bh(&af_whitelist_mac_lock);
for (i = 0; i < MAX_AF_MAC_HASH_SIZE; i++)
{
while (!list_empty(&af_whitelist_mac_htable[i]))
{
p = list_first_entry(&af_whitelist_mac_htable[i], af_whitelist_mac_node_t, list);
memset(mac_str, 0x0, sizeof(mac_str));
sprintf(mac_str, MAC_FMT, MAC_ARRAY(p->mac));
list_del(&(p->list));
kfree(p);
}
}
write_unlock_bh(&af_whitelist_mac_lock);
}
af_whitelist_mac_node_t *af_whitelist_mac_find(unsigned char *mac)
{
af_whitelist_mac_node_t *node = NULL;
unsigned int index = 0;
index = hash_mac(mac);
read_lock_bh(&af_whitelist_mac_lock);
list_for_each_entry(node, &af_whitelist_mac_htable[index], list)
{
if (0 == memcmp(node->mac, mac, 6))
{
read_unlock_bh(&af_whitelist_mac_lock);
return node;
}
}
read_unlock_bh(&af_whitelist_mac_lock);
return NULL;
}
af_whitelist_mac_node_t *af_whitelist_mac_add(unsigned char *mac)
{
af_whitelist_mac_node_t *node = NULL;
int index = 0;
node = (af_whitelist_mac_node_t *)kmalloc(sizeof(af_whitelist_mac_node_t), GFP_ATOMIC);
if (node == NULL)
{
return NULL;
}
memset(node, 0, sizeof(af_whitelist_mac_node_t));
memcpy(node->mac, mac, MAC_ADDR_LEN);
index = hash_mac(mac);
AF_DEBUG("add whitelist mac=" MAC_FMT "\n", MAC_ARRAY(node->mac));
write_lock_bh(&af_whitelist_mac_lock);
list_add(&(node->list), &af_whitelist_mac_htable[index]);
write_unlock_bh(&af_whitelist_mac_lock);
return node;
}
int af_config_set_whitelist_mac_list(cJSON *data_obj)
{
int i;
cJSON *mac_arr = NULL;
u8 mac_hex[MAC_ADDR_LEN] = {0};
if (!data_obj)
{
AF_ERROR("data obj is null\n");
return -1;
}
mac_arr = cJSON_GetObjectItem(data_obj, "mac_list");
if (!mac_arr)
{
AF_ERROR("mac_list obj is null\n");
return -1;
}
af_whitelist_mac_flush();
for (i = 0; i < cJSON_GetArraySize(mac_arr); i++)
{
cJSON *mac_obj = cJSON_GetArrayItem(mac_arr, i);
if (!mac_obj)
{
AF_ERROR("mac obj is null\n");
return -1;
}
if (-1 == mac_to_hex(mac_obj->valuestring, mac_hex))
{
continue;
}
af_whitelist_mac_add(mac_hex);
}
return 0;
}
-19
View File
@@ -1,19 +0,0 @@
#ifndef __AF_WHITELIST_CONFIG_H__
#define __AF_WHITELIST_CONFIG_H__
#include "app_filter.h"
#include "af_utils.h"
#define MAX_AF_WHITELIST_MAC_HASH_SIZE 64
typedef struct af_whitelist_mac_node{
struct list_head list;
unsigned char mac[MAC_ADDR_LEN];
}af_whitelist_mac_node_t;
void af_whitelist_mac_init(void);
void af_whitelist_mac_flush(void);
af_whitelist_mac_node_t *af_whitelist_mac_find(unsigned char *mac);
af_whitelist_mac_node_t *af_whitelist_mac_add(unsigned char *mac);
int af_config_set_whitelist_mac_list(cJSON *data_obj);
#endif
-1955
View File
File diff suppressed because it is too large. Load diff
-178
View File
@@ -1,178 +0,0 @@
#ifndef APP_FILTER_H
#define APP_FILTER_H
#define AF_VERSION "5.3.3"
#define AF_FEATURE_CONFIG_FILE "/tmp/feature.cfg"
#define MAX_DPI_PKT_NUM 64
#define MIN_HTTP_DATA_LEN 16
#define MAX_APP_NAME_LEN 64
#define MAX_FEATURE_NUM_PER_APP 16
#define MIN_FEATURE_STR_LEN 8
#define MAX_FEATURE_STR_LEN 128
#define MAX_HOST_URL_LEN 128
#define MAX_REQUEST_URL_LEN 128
#define MAX_FEATURE_BITS 16
#define MAX_POS_INFO_PER_FEATURE 16
#define MAX_FEATURE_LINE_LEN 600
#define MIN_FEATURE_LINE_LEN 16
#define MAX_URL_MATCH_LEN 64
#define MAX_BYPASS_DPI_PKT_LEN 600
#define MAX_AF_MAC_HASH_SIZE 64
#define HTTP_GET_METHOD_STR "GET"
#define HTTP_POST_METHOD_STR "POST"
#define HTTP_HEADER "HTTP"
#define NIPQUAD(addr) \
((unsigned char *)&addr)[0], \
((unsigned char *)&addr)[1], \
((unsigned char *)&addr)[2], \
((unsigned char *)&addr)[3]
#define NIPQUAD_FMT "%u.%u.%u.%u"
#define MAC_ARRAY(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
#define AF_TRUE 1
#define AF_FALSE 0
#define AF_APP_TYPE(a) (a) / 1000
#define AF_APP_ID(a) (a) % 1000
#define MAC_ADDR_LEN 6
#define HTTPS_URL_OFFSET 9
#define HTTPS_LEN_OFFSET 7
#define MAX_SEARCH_STR_LEN 32
enum AF_FEATURE_PARAM_INDEX{
AF_PROTO_PARAM_INDEX,
AF_SRC_PORT_PARAM_INDEX,
AF_DST_PORT_PARAM_INDEX,
AF_HOST_URL_PARAM_INDEX,
AF_REQUEST_URL_PARAM_INDEX,
AF_DICT_PARAM_INDEX,
AF_STR_PARAM_INDEX,
AF_IGNORE_PARAM_INDEX,
};
#define OAF_NETLINK_ID 29
#define MAX_OAF_NL_MSG_LEN 1024
enum E_MSG_TYPE{
AF_MSG_INIT,
AF_MSG_ADD_FEATURE,
AF_MSG_CLEAN_FEATURE,
AF_MSG_MAX
};
enum AF_WORK_MODE {
AF_MODE_GATEWAY,
AF_MODE_BYPASS,
AF_MODE_BRIDGE,
};
#define MAX_AF_MSG_DATA_LEN 800
typedef struct af_msg{
int action;
}af_msg_t;
struct af_msg_hdr{
int magic;
int len;
};
enum e_http_method{
HTTP_METHOD_GET = 1,
HTTP_METHOD_POST,
};
typedef struct http_proto{
int match;
int method;
char *url_pos;
int url_len;
char *host_pos;
int host_len;
char *data_pos;
int data_len;
}http_proto_t;
typedef struct https_proto{
int match;
char *url_pos;
int url_len;
}https_proto_t;
typedef struct af_pos_info{
int pos;
unsigned char value;
}af_pos_info_t;
#define MAX_PORT_RANGE_NUM 5
typedef struct range_value
{
int not ;
int start;
int end;
} range_value_t;
typedef struct port_info
{
u_int8_t mode; // 0: match, 1: not match
int num;
range_value_t range_list[MAX_PORT_RANGE_NUM];
} port_info_t;
typedef struct af_feature_node{
struct list_head head;
u_int32_t app_id;
char app_name[MAX_APP_NAME_LEN];
char feature[MAX_FEATURE_STR_LEN];
u_int32_t proto;
u_int32_t sport;
u_int32_t dport;
port_info_t dport_info;
char host_url[MAX_HOST_URL_LEN];
char request_url[MAX_REQUEST_URL_LEN];
int pos_num;
char search_str[MAX_SEARCH_STR_LEN];
int ignore;
af_pos_info_t pos_info[MAX_POS_INFO_PER_FEATURE];
}af_feature_node_t;
typedef struct flow_info{
struct nf_conn *ct;
u_int32_t src;
u_int32_t dst;
struct in6_addr *src6;
struct in6_addr *dst6;
int l4_protocol;
u_int16_t sport;
u_int16_t dport;
unsigned char *l4_data;
int l4_len;
http_proto_t http;
https_proto_t https;
u_int32_t app_id;
u_int8_t app_name[MAX_APP_NAME_LEN];
u_int8_t drop;
u_int8_t ignore;
u_int8_t dir;
u_int16_t total_len;
u_int8_t client_hello;
af_feature_node_t *feature;
}flow_info_t;
int regexp_match(char *reg, char *text);
int hash_mac(unsigned char *mac);
char *ipv6_to_str(const struct in6_addr *addr, char *str);
int af_send_msg_to_user(char *pbuf, uint16_t len);
#endif
+320
View File
@@ -0,0 +1,320 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_H__
#define __FWX_H__
#define FWX_VERSION "6.0.1"
#define MAX_FWX_NL_MSG_LEN 1024
#define FWX_TIMER_INTERVAL 1
#define MAX_HOST_LEN 40
#define MIN_HOST_LEN 4
#define MAX_FWX_NETLINK_MSG_LEN 1024
#define MAX_MATCH_PKT_NUM 20
#define FWX_NETLINK_ID 29
#define MAX_NETLINK_MSG_LEN 1024
#include <linux/types.h>
#include <linux/list.h>
#include <linux/in6.h>
#define AF_FEATURE_CONFIG_FILE "/tmp/feature.cfg"
#define MAX_DPI_PKT_NUM 256
#define MIN_HTTP_DATA_LEN 16
#define MAX_APP_NAME_LEN 64
#define MAX_FEATURE_NUM_PER_APP 16
#define MIN_FEATURE_STR_LEN 8
#define MAX_FEATURE_STR_LEN 128
#define MAX_HOST_URL_LEN 128
#define MAX_REQUEST_URL_LEN 128
#define MAX_FEATURE_BITS 16
#define MAX_POS_INFO_PER_FEATURE 16
#define MAX_FEATURE_LINE_LEN 800
#define MIN_FEATURE_LINE_LEN 16
#define MAX_URL_MATCH_LEN 64
#define MAX_BYPASS_DPI_PKT_LEN 600
#define FWX_QUIC_PROTO 10
#define DNS_PORT 53
#define DNS_TYPE_HTTPS 65
#define DNS_HEADER_LEN 12
#define DNS_TCP_PREFIX_LEN 2
#define MAX_DNS_DOMAIN_LEN MAX_HOST_URL_LEN
#define MAX_DNS_QUERY_NUM 16
extern u_int32_t fwx_log_level;
#define HTTP_GET_METHOD_STR "GET"
#define HTTP_POST_METHOD_STR "POST"
#define HTTP_HEADER "HTTP"
#define NIPQUAD(addr) \
((unsigned char *)&addr)[0], \
((unsigned char *)&addr)[1], \
((unsigned char *)&addr)[2], \
((unsigned char *)&addr)[3]
#define NIPQUAD_FMT "%u.%u.%u.%u"
#define MAC_ARRAY(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
#define AF_TRUE 1
#define AF_FALSE 0
#define AF_APP_TYPE(a) (a) / 1000
#define AF_APP_ID(a) (a) % 1000
#define MAC_ADDR_LEN 6
#define HTTPS_URL_OFFSET 9
#define HTTPS_LEN_OFFSET 7
#define MAX_SEARCH_STR_LEN 32
enum AF_FEATURE_PARAM_INDEX{
AF_PROTO_PARAM_INDEX,
AF_SRC_PORT_PARAM_INDEX,
AF_DST_PORT_PARAM_INDEX,
AF_HOST_URL_PARAM_INDEX,
AF_REQUEST_URL_PARAM_INDEX,
AF_DICT_PARAM_INDEX,
AF_STR_PARAM_INDEX,
AF_IGNORE_PARAM_INDEX,
};
#define OAF_NETLINK_ID 29
#define MAX_OAF_NL_MSG_LEN 1024
enum E_FWX_NL_MSG_TYPE
{
FWX_NL_MSG_INIT,
FWX_NL_MSG_ADD_FEATURE,
FWX_NL_MSG_CLEAN_FEATURE,
FWX_NL_MSG_FEATURE_LOAD_DONE,
FWX_NL_MSG_MAX
};
enum AF_WORK_MODE {
AF_MODE_GATEWAY,
AF_MODE_BYPASS,
AF_MODE_BRIDGE,
};
#define MAX_AF_MSG_DATA_LEN 800
typedef struct af_msg{
int action;
}af_msg_t;
struct af_msg_hdr{
int magic;
int len;
};
enum e_http_method{
HTTP_METHOD_GET = 1,
HTTP_METHOD_POST,
};
typedef struct http_proto{
int match;
int method;
char *url_pos;
int url_len;
char *host_pos;
int host_len;
char *data_pos;
int data_len;
}http_proto_t;
typedef struct https_proto{
int match;
char *url_pos;
int url_len;
}https_proto_t;
typedef struct af_pos_info{
int pos;
unsigned char value;
}af_pos_info_t;
#define MAX_PORT_RANGE_NUM 5
typedef struct range_value
{
int not ;
int start;
int end;
} range_value_t;
typedef struct port_info
{
u_int8_t mode; // 0: match, 1: not match
int num;
range_value_t range_list[MAX_PORT_RANGE_NUM];
} port_info_t;
typedef struct dns_proto{
int match;
int query_num;
int qdcount;
u_int16_t qtype[MAX_DNS_QUERY_NUM];
char domain[MAX_DNS_QUERY_NUM][MAX_DNS_DOMAIN_LEN];
}dns_proto_t;
typedef struct af_feature_node{
struct list_head head;
u_int32_t app_id;
char app_name[MAX_APP_NAME_LEN];
char feature[MAX_FEATURE_STR_LEN];
u_int32_t proto;
u_int32_t sport;
u_int32_t dport;
port_info_t dport_info;
char host_url[MAX_HOST_URL_LEN];
char request_url[MAX_REQUEST_URL_LEN];
int pos_num;
char search_str[MAX_SEARCH_STR_LEN];
int ignore;
af_pos_info_t pos_info[MAX_POS_INFO_PER_FEATURE];
}af_feature_node_t;
typedef struct af_mac_info {
struct list_head hlist;
unsigned char mac[MAC_ADDR_LEN];
}af_mac_info_t;
typedef struct flow_info{
struct nf_conn *ct;
u_int32_t src;
u_int32_t dst;
struct in6_addr *src6;
struct in6_addr *dst6;
int l4_protocol;
u_int16_t sport;
u_int16_t dport;
unsigned char *l4_data;
int l4_len;
http_proto_t http;
https_proto_t https;
dns_proto_t dns;
u_int32_t app_id;
u_int8_t app_name[MAX_APP_NAME_LEN];
u_int8_t drop;
u_int8_t ignore;
u_int8_t match_by_dns;
u_int8_t dir;
u_int16_t total_len;
u_int8_t client_hello;
af_feature_node_t *feature;
}flow_info_t;
#define MAX_ACTIVE_APP_LIST_SIZE 10
#define MAX_ACTIVE_HOST_LIST_SIZE 10
typedef struct active_app_node {
struct list_head list;
u_int32_t app_id;
unsigned char mac[MAC_ADDR_LEN];
u_int32_t src_ip;
u_int32_t dst_ip;
struct in6_addr src_ip6;
struct in6_addr dst_ip6;
u_int16_t src_port;
u_int16_t dst_port;
u_int8_t l4_protocol;
u_int8_t drop;
u_int8_t proto_type;
char host[32];
char uri[32];
u_int32_t update_time;
} active_app_node_t;
typedef struct active_host_node {
struct list_head list;
char host[64];
unsigned char mac[MAC_ADDR_LEN];
u_int32_t src_ip;
u_int32_t dst_ip;
struct in6_addr src_ip6;
struct in6_addr dst_ip6;
u_int16_t src_port;
u_int16_t dst_port;
u_int8_t l4_protocol;
u_int8_t drop;
u_int8_t proto_type;
u_int32_t update_time;
} active_host_node_t;
int regexp_match(char *reg, char *text);
int is_user_match_enable(void);
struct af_client_info;
typedef struct af_client_info af_client_info_t;
void af_update_active_app_list(af_client_info_t *client, flow_info_t *flow);
active_app_node_t *af_find_active_app(u_int32_t app_id);
void af_clear_active_app_list(void);
void af_update_active_host_list(af_client_info_t *client, flow_info_t *flow);
active_host_node_t *af_find_active_host(const char *host);
void af_clear_active_host_list(void);
enum FWX_PKT_DIR
{
PKT_DIR_DOWN,
PKT_DIR_UP
};
typedef struct fwx_msg
{
int action;
void *data;
} fwx_msg_t;
struct fwx_msg_hdr
{
int len;
int msg_type;
};
extern int af_log_lvl;
#define LOG(level, fmt, ...) do { \
if ((level) <= af_log_lvl) { \
printk(KERN_CONT"%s %d " fmt, __func__, __LINE__, ##__VA_ARGS__); \
} \
} while (0)
#define LLOG(level, fmt, ...) do { \
if ((level) <= af_log_lvl) { \
pr_info_ratelimited(KERN_CONT "%s %d " fmt, __func__, __LINE__, ##__VA_ARGS__); \
} \
} while (0)
#define AF_ERROR(...) LOG(0, ##__VA_ARGS__)
#define AF_WARN(...) LOG(1, ##__VA_ARGS__)
#define AF_INFO(...) LOG(2, ##__VA_ARGS__)
#define AF_DEBUG(...) LOG(3, ##__VA_ARGS__)
#define AF_LMT_ERROR(...) LLOG(0, ##__VA_ARGS__)
#define AF_LMT_WARN(...) LLOG(1, ##__VA_ARGS__)
#define AF_LMT_INFO(...) LLOG(2, ##__VA_ARGS__)
#define AF_LMT_DEBUG(...) LLOG(3, ##__VA_ARGS__)
#endif
+819
View File
@@ -0,0 +1,819 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/etherdevice.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include "k_json.h"
#include "fwx.h"
#include "fwx_app_filter.h"
#include "fwx_mac.h"
#include "fwx_log.h"
DEFINE_RWLOCK(app_filter_lock);
#define app_filter_read_lock() read_lock_bh(&app_filter_lock);
#define app_filter_read_unlock() read_unlock_bh(&app_filter_lock);
#define app_filter_write_lock() write_lock_bh(&app_filter_lock);
#define app_filter_write_unlock() write_unlock_bh(&app_filter_lock);
int g_appfilter_enable = 0;
u_int32_t g_appfilter_update_jiffies = 0;
static int g_app_rule_count = 0;
static LIST_HEAD(app_filter_rule_list);
static mac_config_t g_app_filter_whitelist;
static void app_id_config_init(app_id_config_t *config) {
int i;
for (i = 0; i < APPID_HASH_SIZE; i++) {
INIT_HLIST_HEAD(&config->hash_table[i]);
}
config->count = 0;
}
static void flush_app_id_list(app_id_config_t *config) {
int i;
app_id_node_t *node;
struct hlist_node *n;
for (i = 0; i < APPID_HASH_SIZE; i++) {
hlist_for_each_entry_safe(node, n, &config->hash_table[i], hlist) {
hlist_del(&node->hlist);
kfree(node);
}
}
config->count = 0;
}
static app_id_node_t *find_app_id_node(app_id_config_t *config, int app_id) {
int hash = app_id % APPID_HASH_SIZE;
app_id_node_t *node;
hlist_for_each_entry(node, &config->hash_table[hash], hlist) {
if (node->app_id == app_id) {
return node;
}
}
return NULL;
}
static int add_app_id_node(app_id_config_t *config, int app_id) {
app_id_node_t *node;
int hash;
if (find_app_id_node(config, app_id)) {
return 0; // 已存在,返回成功
}
if (config->count >= MAX_APP_ID_PER_RULE) {
AF_ERROR("app id count exceeds limit\n");
return -1;
}
node = kmalloc(sizeof(app_id_node_t), GFP_ATOMIC);
if (!node) {
AF_ERROR("kmalloc app_id_node failed\n");
return -1;
}
node->app_id = app_id;
hash = app_id % APPID_HASH_SIZE;
hlist_add_head(&node->hlist, &config->hash_table[hash]);
config->count++;
return 0;
}
static int add_app_id_token_to_rule(app_filter_rule_t *rule, cJSON *app_id_obj)
{
int start_id = 0, end_id = 0;
int single_id = 0;
int i = 0;
const char *token = NULL;
if (!rule || !app_id_obj) {
return -1;
}
if (app_id_obj->type == cJSON_Number) {
if (app_id_obj->valueint > 0) {
return add_app_id_node(&rule->app_id_list, app_id_obj->valueint);
}
return -1;
}
if (app_id_obj->type != cJSON_String || !app_id_obj->valuestring) {
return -1;
}
token = app_id_obj->valuestring;
if (sscanf(token, "%d-%d", &start_id, &end_id) == 2) {
if (start_id <= 0 || end_id < start_id) {
AF_ERROR("invalid app_id range: %s\n", token);
return -1;
}
for (i = start_id; i <= end_id; i++) {
AF_INFO("token parse %s, id = %d\n", token, i);
if (add_app_id_node(&rule->app_id_list, i) < 0) {
return -1;
}
}
return 0;
}
if (kstrtoint(token, 10, &single_id) == 0 && single_id > 0) {
return add_app_id_node(&rule->app_id_list, single_id);
}
AF_ERROR("invalid app_id token: %s\n", token);
return -1;
}
static int del_app_id_token_from_rule(int rule_id, cJSON *app_id_obj)
{
int start_id = 0, end_id = 0;
int single_id = 0;
int i = 0;
const char *token = NULL;
if (!app_id_obj) {
return -1;
}
if (app_id_obj->type == cJSON_Number) {
return fwx_del_app_id_from_rule(rule_id, app_id_obj->valueint);
}
if (app_id_obj->type != cJSON_String || !app_id_obj->valuestring) {
return -1;
}
token = app_id_obj->valuestring;
if (sscanf(token, "%d-%d", &start_id, &end_id) == 2) {
if (start_id <= 0 || end_id < start_id) {
AF_ERROR("invalid app_id range for delete: %s\n", token);
return -1;
}
for (i = start_id; i <= end_id; i++) {
fwx_del_app_id_from_rule(rule_id, i);
}
return 0;
}
if (kstrtoint(token, 10, &single_id) == 0 && single_id > 0) {
return fwx_del_app_id_from_rule(rule_id, single_id);
}
AF_ERROR("invalid app_id token for delete: %s\n", token);
return -1;
}
int fwx_app_filter_init(void) {
app_filter_write_lock();
INIT_LIST_HEAD(&app_filter_rule_list);
g_app_rule_count = 0;
fwx_mac_config_init(&g_app_filter_whitelist);
app_filter_write_unlock();
AF_INFO("app filter init...ok\n");
return 0;
}
void fwx_app_filter_exit(void) {
app_filter_rule_t *rule, *next;
app_filter_write_lock();
list_for_each_entry_safe(rule, next, &app_filter_rule_list, list) {
flush_app_id_list(&rule->app_id_list);
fwx_flush_mac_list(&rule->mac_list);
list_del(&rule->list);
kfree(rule);
}
g_app_rule_count = 0;
fwx_flush_mac_list(&g_app_filter_whitelist);
app_filter_write_unlock();
AF_INFO("app filter exit...ok\n");
}
app_filter_rule_t *fwx_find_app_filter_rule(int rule_id) {
app_filter_rule_t *rule;
list_for_each_entry(rule, &app_filter_rule_list, list) {
if (rule->rule_id == rule_id) {
return rule;
}
}
return NULL;
}
void fwx_update_appfilter_jiffies(void){
g_appfilter_update_jiffies = jiffies;
}
int fwx_add_app_filter_rule(int rule_id) {
app_filter_rule_t *rule;
if (g_app_rule_count >= MAX_APP_FILTER_RULE_NUM) {
AF_ERROR("app filter rule count exceeds limit\n");
return -1;
}
if (fwx_find_app_filter_rule(rule_id)) {
AF_ERROR("app filter rule %d already exists\n", rule_id);
return -1;
}
rule = kmalloc(sizeof(app_filter_rule_t), GFP_ATOMIC);
if (!rule) {
AF_ERROR("kmalloc app filter rule failed\n");
return -1;
}
rule->rule_id = rule_id;
rule->enable = 1;
rule->filter_quic = 0;
fwx_mac_config_init(&rule->mac_list);
app_id_config_init(&rule->app_id_list);
INIT_LIST_HEAD(&rule->list);
app_filter_write_lock();
list_add(&rule->list, &app_filter_rule_list);
g_app_rule_count++;
app_filter_write_unlock();
AF_INFO("add app filter rule %d ok\n", rule_id);
return 0;
}
int fwx_del_app_filter_rule(int rule_id) {
app_filter_rule_t *rule;
app_filter_write_lock();
rule = fwx_find_app_filter_rule(rule_id);
if (rule) {
flush_app_id_list(&rule->app_id_list);
fwx_flush_mac_list(&rule->mac_list);
list_del(&rule->list);
kfree(rule);
g_app_rule_count--;
app_filter_write_unlock();
AF_INFO("del app filter rule %d ok\n", rule_id);
return 0;
}
app_filter_write_unlock();
AF_ERROR("app filter rule %d not found\n", rule_id);
return -1;
}
int fwx_add_app_id_to_rule(int rule_id, int app_id) {
app_filter_rule_t *rule;
app_filter_write_lock();
rule = fwx_find_app_filter_rule(rule_id);
if (rule) {
add_app_id_node(&rule->app_id_list, app_id);
app_filter_write_unlock();
return 0;
}
app_filter_write_unlock();
AF_ERROR("app filter rule %d not found\n", rule_id);
return -1;
}
int fwx_del_app_id_from_rule(int rule_id, int app_id) {
app_filter_rule_t *rule;
app_id_node_t *node;
int hash;
app_filter_write_lock();
rule = fwx_find_app_filter_rule(rule_id);
if (rule) {
hash = app_id % APPID_HASH_SIZE;
hlist_for_each_entry(node, &rule->app_id_list.hash_table[hash], hlist) {
if (node->app_id == app_id) {
hlist_del(&node->hlist);
kfree(node);
rule->app_id_list.count--;
app_filter_write_unlock();
return 0;
}
}
}
app_filter_write_unlock();
AF_ERROR("app_id %d or rule %d not found\n", app_id, rule_id);
return -1;
}
app_filter_rule_t *fwx_match_app_filter_rule(int app_id, const unsigned char *mac) {
app_filter_rule_t *rule;
app_id_node_t *node;
struct mac_node *mac_node;
int i;
int mac_list_empty;
app_filter_read_lock();
list_for_each_entry(rule, &app_filter_rule_list, list) {
if (!rule->enable) {
continue;
}
mac_node = fwx_find_mac_node(&rule->mac_list, mac);
if (!mac_node) {
mac_list_empty = 1;
for (i = 0; i < MAC_HASH_SIZE; i++) {
if (!hlist_empty(&rule->mac_list.hash_table[i])) {
mac_list_empty = 0;
break;
}
}
if (!mac_list_empty) {
continue;
}
}
if (app_id == FWX_QUIC_PROTO) {
if (rule->filter_quic == 1) {
app_filter_read_unlock();
return rule;
}
continue;
}
node = find_app_id_node(&rule->app_id_list, app_id);
if (node) {
app_filter_read_unlock();
return rule;
}
}
app_filter_read_unlock();
return NULL;
}
int fwx_api_add_app_filter_rule(cJSON *data_obj) {
cJSON *rule_id_obj;
if (!data_obj) {
return -1;
}
rule_id_obj = cJSON_GetObjectItem(data_obj, "rule_id");
if (!rule_id_obj) {
AF_ERROR("invalid rule format\n");
return -1;
}
fwx_update_appfilter_jiffies();
if (fwx_add_app_filter_rule(rule_id_obj->valueint) < 0) {
return -1;
}
AF_INFO("add app filter rule %d ok\n", rule_id_obj->valueint);
return 0;
}
int fwx_api_mod_app_filter_rule(cJSON *data_obj) {
int i;
cJSON *rule_id_obj;
cJSON *app_id_array;
cJSON *app_id_obj;
cJSON *action_obj;
cJSON *enable_obj;
cJSON *filter_quic_obj;
app_filter_rule_t *rule = NULL;
if (!data_obj) {
return -1;
}
rule_id_obj = cJSON_GetObjectItem(data_obj, "rule_id");
action_obj = cJSON_GetObjectItem(data_obj, "app_action");
cJSON *mac_action_obj = cJSON_GetObjectItem(data_obj, "mac_action");
if (!rule_id_obj) {
AF_ERROR("rule_id not found\n");
return -1;
}
rule = fwx_find_app_filter_rule(rule_id_obj->valueint);
if (!rule) {
AF_ERROR("rule %d not found\n", rule_id_obj->valueint);
return -1;
}
if (mac_action_obj) {
if (mac_action_obj->valueint == 1 || mac_action_obj->valueint == 2) {
cJSON *mac_array = cJSON_GetObjectItem(data_obj, "mac_list");
if (mac_array) {
app_filter_write_lock();
if (mac_action_obj->valueint == 1) { // flush old
fwx_flush_mac_list(&rule->mac_list);
}
for (i = 0; i < cJSON_GetArraySize(mac_array); i++) {
cJSON *mac_obj = cJSON_GetArrayItem(mac_array, i);
u8 mac_bin[ETH_ALEN] = {0};
if (mac_obj && mac_str_to_bin(mac_obj->valuestring, mac_bin)) {
fwx_add_mac_node(&rule->mac_list, mac_bin);
}
}
app_filter_write_unlock();
}
} else if (mac_action_obj->valueint == 3) {
cJSON *mac_obj = cJSON_GetObjectItem(data_obj, "mac");
if (mac_obj) {
app_filter_write_lock();
u8 mac_bin[ETH_ALEN] = {0};
if (mac_str_to_bin(mac_obj->valuestring, mac_bin)) {
fwx_add_mac_node(&rule->mac_list, mac_bin);
}
app_filter_write_unlock();
}
} else {
app_filter_write_lock();
fwx_flush_mac_list(&rule->mac_list);
app_filter_write_unlock();
}
}
if (action_obj) {
if (action_obj->valueint == 1 || action_obj->valueint == 2) {
app_id_array = cJSON_GetObjectItem(data_obj, "app_id_list");
if (app_id_array) {
app_filter_write_lock();
if (action_obj->valueint == 1) { // flush old
flush_app_id_list(&rule->app_id_list);
}
for (i = 0; i < cJSON_GetArraySize(app_id_array); i++) {
app_id_obj = cJSON_GetArrayItem(app_id_array, i);
if (app_id_obj) {
add_app_id_token_to_rule(rule, app_id_obj);
}
}
app_filter_write_unlock();
}
} else if (action_obj->valueint == 3) {
app_id_obj = cJSON_GetObjectItem(data_obj, "app_id");
if (app_id_obj) {
app_filter_write_lock();
add_app_id_token_to_rule(rule, app_id_obj);
app_filter_write_unlock();
}
} else {
app_filter_write_lock();
flush_app_id_list(&rule->app_id_list);
app_filter_write_unlock();
}
}
enable_obj = cJSON_GetObjectItem(data_obj, "enable");
if (enable_obj) {
rule->enable = enable_obj->valueint;
}
filter_quic_obj = cJSON_GetObjectItem(data_obj, "filter_quic");
if (filter_quic_obj) {
rule->filter_quic = (filter_quic_obj->valueint == 1) ? 1 : 0;
}
fwx_update_appfilter_jiffies();
return 0;
}
int fwx_api_del_app_filter_rule(cJSON *data_obj) {
cJSON *rule_id_obj;
cJSON *app_id_obj;
if (!data_obj) {
return -1;
}
rule_id_obj = cJSON_GetObjectItem(data_obj, "rule_id");
if (!rule_id_obj) {
AF_ERROR("rule_id not found\n");
return -1;
}
fwx_update_appfilter_jiffies();
app_id_obj = cJSON_GetObjectItem(data_obj, "app_id");
if (app_id_obj) {
return del_app_id_token_from_rule(rule_id_obj->valueint, app_id_obj);
} else {
return fwx_del_app_filter_rule(rule_id_obj->valueint);
}
}
int fwx_api_dump_app_filter_rule(cJSON *data_obj) {
app_filter_rule_t *rule;
app_id_node_t *node;
struct mac_node *mac_node;
int app_count = 0;
int mac_count = 0;
int i;
if (!data_obj) {
return -1;
}
cJSON *rule_id_obj = cJSON_GetObjectItem(data_obj, "rule_id");
app_filter_read_lock();
printk("\n");
printk("+--------+-------+-----------+------------------+------------------+\n");
printk("| RuleID | Enable| FilterQUIC| MAC List | App ID List |\n");
printk("+--------+-------+-----------+------------------+------------------+\n");
if (rule_id_obj) {
rule = fwx_find_app_filter_rule(rule_id_obj->valueint);
if (rule) {
for (i = 0; i < MAC_HASH_SIZE; i++) {
hlist_for_each_entry(mac_node, &rule->mac_list.hash_table[i], hlist) {
mac_count++;
}
}
for (i = 0; i < APPID_HASH_SIZE; i++) {
hlist_for_each_entry(node, &rule->app_id_list.hash_table[i], hlist) {
app_count++;
}
}
printk(KERN_CONT "| %-6d | %-5d | %-9d | ", rule->rule_id, rule->enable, rule->filter_quic);
if (mac_count == 0) {
printk(KERN_CONT "%-16s | ", "(all MACs)");
} else {
int total_mac_count = mac_count;
mac_count = 0;
for (i = 0; i < MAC_HASH_SIZE; i++) {
hlist_for_each_entry(mac_node, &rule->mac_list.hash_table[i], hlist) {
if (mac_count > 0) {
printk(KERN_CONT ", ");
}
printk(KERN_CONT "%pM", mac_node->mac);
mac_count++;
if (mac_count >= 32) {
if (mac_count < total_mac_count) {
printk(KERN_CONT "...");
}
break;
}
}
}
printk(KERN_CONT " | ");
}
if (app_count == 0) {
printk(KERN_CONT "%-16s |\n", "(empty)");
} else {
int total_app_count = app_count;
int printed_count = 0;
int should_break = 0;
app_count = 0;
for (i = 0; i < APPID_HASH_SIZE && !should_break; i++) {
hlist_for_each_entry(node, &rule->app_id_list.hash_table[i], hlist) {
if (printed_count > 0) {
printk(KERN_CONT ", ");
}
printk(KERN_CONT "%d", node->app_id);
printed_count++;
app_count++;
if (printed_count >= 128) {
if (app_count < total_app_count) {
printk(KERN_CONT "...");
}
should_break = 1;
break;
}
}
}
printk(KERN_CONT " |\n");
}
}
} else {
list_for_each_entry(rule, &app_filter_rule_list, list) {
app_count = 0;
mac_count = 0;
for (i = 0; i < MAC_HASH_SIZE; i++) {
hlist_for_each_entry(mac_node, &rule->mac_list.hash_table[i], hlist) {
mac_count++;
}
}
for (i = 0; i < 256; i++) {
hlist_for_each_entry(node, &rule->app_id_list.hash_table[i], hlist) {
app_count++;
}
}
printk(KERN_CONT "| %-6d | %-5d | %-9d | ", rule->rule_id, rule->enable, rule->filter_quic);
if (mac_count == 0) {
printk(KERN_CONT "%-16s | ", "(all MACs)");
} else {
int total_mac_count = mac_count;
mac_count = 0;
for (i = 0; i < MAC_HASH_SIZE; i++) {
hlist_for_each_entry(mac_node, &rule->mac_list.hash_table[i], hlist) {
if (mac_count > 0) {
printk(KERN_CONT ", ");
}
printk(KERN_CONT "%pM", mac_node->mac);
mac_count++;
if (mac_count >= 32) {
if (mac_count < total_mac_count) {
printk(KERN_CONT "...");
}
break;
}
}
}
printk(KERN_CONT " | ");
}
if (app_count == 0) {
printk(KERN_CONT "%-16s |\n", "(empty)");
} else {
int total_app_count = app_count;
int printed_count = 0;
int should_break = 0;
app_count = 0;
for (i = 0; i < APPID_HASH_SIZE && !should_break; i++) {
hlist_for_each_entry(node, &rule->app_id_list.hash_table[i], hlist) {
if (printed_count > 0) {
printk(KERN_CONT ", ");
}
printk(KERN_CONT "%d", node->app_id);
printed_count++;
app_count++;
if (printed_count >= 128) {
if (app_count < total_app_count) {
printk(KERN_CONT "...");
}
should_break = 1;
break;
}
}
}
printk(KERN_CONT " |\n");
}
}
}
printk("+--------+-------+-----------+------------------+------------------+\n");
printk("\n");
printk("App Filter Whitelist:\n");
printk("+----------------------------------------+\n");
printk("| MAC Address |\n");
printk("+----------------------------------------+\n");
int total_whitelist_count = 0;
struct mac_node *whitelist_node;
for (i = 0; i < MAC_HASH_SIZE; i++) {
hlist_for_each_entry(whitelist_node, &g_app_filter_whitelist.hash_table[i], hlist) {
total_whitelist_count++;
}
}
if (total_whitelist_count == 0) {
printk(KERN_CONT "| %-38s |\n", "(empty)");
} else {
int printed_count = 0;
int should_break = 0;
for (i = 0; i < MAC_HASH_SIZE && !should_break; i++) {
hlist_for_each_entry(whitelist_node, &g_app_filter_whitelist.hash_table[i], hlist) {
printk(KERN_CONT "| %-38pM |\n", whitelist_node->mac);
printed_count++;
if (printed_count >= 10) {
if (printed_count < total_whitelist_count) {
printk(KERN_CONT "| %-38s |\n", "...");
}
should_break = 1;
break;
}
}
}
}
printk("+----------------------------------------+\n");
printk("Total whitelist entries: %d\n", total_whitelist_count);
app_filter_read_unlock();
return 0;
}
int fwx_api_flush_app_filter_rule(cJSON *data_obj) {
app_filter_rule_t *rule, *next;
app_filter_write_lock();
list_for_each_entry_safe(rule, next, &app_filter_rule_list, list) {
flush_app_id_list(&rule->app_id_list);
list_del(&rule->list);
kfree(rule);
}
g_app_rule_count = 0;
app_filter_write_unlock();
fwx_update_appfilter_jiffies();
return 0;
}
int fwx_match_app_filter_whitelist(const unsigned char *mac) {
struct mac_node *node;
int ret = 0;
app_filter_read_lock();
node = fwx_find_mac_node(&g_app_filter_whitelist, mac);
ret = (node != NULL);
app_filter_read_unlock();
return ret;
}
int fwx_api_add_app_filter_whitelist(cJSON *data_obj) {
cJSON *mac_array;
int i;
u8 mac_bin[ETH_ALEN];
if (!data_obj) {
return -1;
}
mac_array = cJSON_GetObjectItem(data_obj, "mac_list");
if (!mac_array) {
printk("mac_list not found\n");
return -1;
}
app_filter_write_lock();
for (i = 0; i < cJSON_GetArraySize(mac_array); i++) {
cJSON *mac_obj = cJSON_GetArrayItem(mac_array, i);
if (mac_obj && mac_str_to_bin(mac_obj->valuestring, mac_bin)) {
fwx_add_mac_node(&g_app_filter_whitelist, mac_bin);
}
}
app_filter_write_unlock();
fwx_update_appfilter_jiffies();
return 0;
}
int fwx_api_flush_app_filter_whitelist(cJSON *data_obj) {
app_filter_write_lock();
fwx_flush_mac_list(&g_app_filter_whitelist);
app_filter_write_unlock();
fwx_update_appfilter_jiffies();
return 0;
}
+89
View File
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_APP_FILTER_H__
#define __FWX_APP_FILTER_H__
#include "k_json.h"
#include "fwx_mac.h"
#include <linux/list.h>
#include <linux/types.h>
#define MAX_APP_FILTER_RULE_NUM 64
#define MAX_APP_ID_PER_RULE 1024
#define APPID_HASH_SIZE 256
extern u_int32_t g_appfilter_update_jiffies;
typedef struct app_id_node {
int app_id;
struct hlist_node hlist;
} app_id_node_t;
typedef struct app_id_config {
struct hlist_head hash_table[APPID_HASH_SIZE];
int count;
} app_id_config_t;
typedef struct app_filter_rule {
int rule_id;
int enable;
int filter_quic;
mac_config_t mac_list;
app_id_config_t app_id_list;
struct list_head list;
} app_filter_rule_t;
extern int g_appfilter_enable;
int fwx_app_filter_init(void);
void fwx_app_filter_exit(void);
int fwx_add_app_filter_rule(int rule_id);
int fwx_del_app_filter_rule(int rule_id);
app_filter_rule_t *fwx_find_app_filter_rule(int rule_id);
int fwx_add_app_id_to_rule(int rule_id, int app_id);
int fwx_del_app_id_from_rule(int rule_id, int app_id);
app_filter_rule_t *fwx_match_app_filter_rule(int app_id, const unsigned char *mac);
int fwx_api_add_app_filter_rule(cJSON *data_obj);
int fwx_api_del_app_filter_rule(cJSON *data_obj);
int fwx_api_dump_app_filter_rule(cJSON *data_obj);
int fwx_api_flush_app_filter_rule(cJSON *data_obj);
int fwx_api_mod_app_filter_rule(cJSON *data_obj);
int fwx_api_add_app_filter_whitelist(cJSON *data_obj);
int fwx_api_flush_app_filter_whitelist(cJSON *data_obj);
int fwx_match_app_filter_whitelist(const unsigned char *mac);
#endif
+389 -72
View File
@@ -1,7 +1,8 @@
/*
Author:Derry
Date: 2019/11/12
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h>
@@ -21,22 +22,119 @@
#include <linux/ipv6.h>
#include <linux/in6.h>
#include <linux/timer.h>
#include <linux/sort.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include "af_client.h"
#include "af_client_fs.h"
#include "af_log.h"
#include "af_utils.h"
#include "app_filter.h"
#include "cJSON.h"
#include "fwx_client.h"
#include "fwx_client_fs.h"
#include "fwx_log.h"
#include "fwx_mac.h"
#include "fwx_mac_filter.h"
#include "fwx_utils.h"
#include "fwx.h"
#include "k_json.h"
DEFINE_RWLOCK(af_client_lock);
u32 total_client = 0;
struct list_head af_client_list_table[MAX_AF_CLIENT_HASH_SIZE];
int g_max_app_report_count = 3;
int g_min_http_match_count = 3;
static DEFINE_RWLOCK(record_whitelist_lock);
#define record_whitelist_read_lock() read_lock_bh(&record_whitelist_lock);
#define record_whitelist_read_unlock() read_unlock_bh(&record_whitelist_lock);
#define record_whitelist_write_lock() write_lock_bh(&record_whitelist_lock);
#define record_whitelist_write_unlock() write_unlock_bh(&record_whitelist_lock);
static mac_config_t g_record_whitelist;
int af_send_msg_to_user(char *pbuf, uint16_t len);
extern char *ipv6_to_str(const struct in6_addr *addr, char *str);
static void init_client_timer(af_client_info_t *client);
static void stop_client_timer(af_client_info_t *client);
static int is_client_mac_filter_blocked(af_client_info_t *node)
{
if (!node || !g_mac_filter_enable) {
return 0;
}
if (fwx_match_mac_filter_whitelist(node->mac)) {
return 0;
}
return fwx_match_mac_filter_rule(node->mac) ? 1 : 0;
}
int fwx_match_record_whitelist(const unsigned char *mac)
{
struct mac_node *node;
int ret = 0;
record_whitelist_read_lock();
node = fwx_find_mac_node(&g_record_whitelist, mac);
ret = (node != NULL);
record_whitelist_read_unlock();
return ret;
}
static void fwx_sync_record_whitelist_clients(void)
{
int i;
af_client_info_t *node = NULL;
AF_CLIENT_LOCK_W();
for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
{
list_for_each_entry(node, &af_client_list_table[i], hlist)
{
node->record_whitelist = fwx_match_record_whitelist(node->mac);
}
}
AF_CLIENT_UNLOCK_W();
}
int fwx_set_record_whitelist(const char *mac_list_str)
{
char mac_buf[1024] = {0};
char *token = NULL;
char *save_ptr = NULL;
u8 mac_bin[ETH_ALEN];
struct mac_node *node = NULL;
if (!mac_list_str) {
return -1;
}
strcpy(mac_buf, mac_list_str);
record_whitelist_write_lock();
fwx_flush_mac_list(&g_record_whitelist);
save_ptr = mac_buf;
while ((token = strsep(&save_ptr, ",")) != NULL) {
token = strim(token);
if (!token || token[0] == '\0') {
continue;
}
if (!mac_str_to_bin(token, mac_bin)) {
continue;
}
node = fwx_find_mac_node(&g_record_whitelist, mac_bin);
if (!node) {
fwx_add_mac_node(&g_record_whitelist, mac_bin);
}
}
record_whitelist_write_unlock();
fwx_sync_record_whitelist_clients();
return 0;
}
static void
nf_client_list_init(void)
@@ -47,6 +145,7 @@ nf_client_list_init(void)
{
INIT_LIST_HEAD(&af_client_list_table[i]);
}
fwx_mac_config_init(&g_record_whitelist);
AF_CLIENT_UNLOCK_W();
AF_INFO("client list init......ok\n");
}
@@ -54,9 +153,12 @@ nf_client_list_init(void)
static void
nf_client_list_clear(void)
{
int i;
int i, j;
af_client_info_t *p = NULL;
char mac_str[32] = {0};
struct hlist_head *head;
struct hlist_node *n;
app_visit_info_t *info;
AF_DEBUG("clean list\n");
AF_CLIENT_LOCK_W();
@@ -69,11 +171,26 @@ nf_client_list_clear(void)
sprintf(mac_str, MAC_FMT, MAC_ARRAY(p->mac));
AF_DEBUG("clean mac:%s\n", mac_str);
stop_client_timer(p);
remove_client_proc_dir(p);
spin_lock_bh(&p->visit_info_lock);
for (j = 0; j < MAX_VISIT_INFO_HASH_SIZE; j++) {
head = &p->visit_info_hash[j];
hlist_for_each_entry_safe(info, n, head, hlist) {
hlist_del(&info->hlist);
kfree(info);
}
}
spin_unlock_bh(&p->visit_info_lock);
list_del(&(p->hlist));
kfree(p);
}
}
AF_CLIENT_UNLOCK_W();
record_whitelist_write_lock();
fwx_flush_mac_list(&g_record_whitelist);
record_whitelist_write_unlock();
}
void af_client_list_reset_report_num(void)
@@ -91,7 +208,7 @@ void af_client_list_reset_report_num(void)
AF_CLIENT_UNLOCK_W();
}
static int get_mac_hash_code(unsigned char *mac)
int get_mac_hash_code(unsigned char *mac)
{
if (!mac)
return 0;
@@ -178,15 +295,25 @@ nf_client_add(unsigned char *mac)
memset(node, 0, sizeof(af_client_info_t));
memcpy(node->mac, mac, MAC_ADDR_LEN);
node->record_whitelist = fwx_match_record_whitelist(node->mac);
node->create_jiffies = jiffies;
node->update_jiffies = jiffies;
node->timer_count = 0;
spin_lock_init(&node->visit_info_lock);
{
int i;
for (i = 0; i < MAX_VISIT_INFO_HASH_SIZE; i++) {
INIT_HLIST_HEAD(&node->visit_info_hash[i]);
}
}
index = get_mac_hash_code(mac);
AF_LMT_INFO("new client mac=" MAC_FMT "\n", MAC_ARRAY(node->mac));
total_client++;
init_client_timer(node);
list_add(&(node->hlist), &af_client_list_table[index]);
create_client_proc_dir(node);
return node;
}
@@ -208,6 +335,7 @@ void check_client_expire(void)
{
AF_INFO("del client:" MAC_FMT "\n", MAC_ARRAY(node->mac));
stop_client_timer(node);
remove_client_proc_dir(node);
list_del(&(node->hlist));
kfree(node);
AF_CLIENT_UNLOCK_W();
@@ -219,55 +347,144 @@ void check_client_expire(void)
}
#define MAX_EXPIRED_VISIT_INFO_COUNT 10
static void flush_expired_visit_info(af_client_info_t *node)
static inline int get_app_id_hash_code(unsigned int app_id)
{
return app_id & (MAX_VISIT_INFO_HASH_SIZE - 1);
}
static app_visit_info_t *find_visit_info(af_client_info_t *node, unsigned int app_id)
{
struct hlist_head *head;
app_visit_info_t *info;
head = &node->visit_info_hash[get_app_id_hash_code(app_id)];
hlist_for_each_entry(info, head, hlist) {
if (info->app_id == app_id) {
return info;
}
}
return NULL;
}
app_visit_info_t *get_or_create_visit_info(af_client_info_t *node, unsigned int app_id)
{
app_visit_info_t *info;
info = find_visit_info(node, app_id);
if (info) {
return info;
}
info = (app_visit_info_t *)kmalloc(sizeof(app_visit_info_t), GFP_ATOMIC);
if (!info) {
return NULL;
}
memset(info, 0, sizeof(app_visit_info_t));
info->app_id = app_id;
INIT_HLIST_NODE(&info->hlist);
hlist_add_head(&info->hlist, &node->visit_info_hash[get_app_id_hash_code(app_id)]);
return info;
}
void flush_expired_visit_info(af_client_info_t *node)
{
int i;
int count = 0;
u_int32_t cur_timep = 0;
int timeout = 0;
struct hlist_head *head;
struct hlist_node *n;
app_visit_info_t *info;
cur_timep = af_get_timestamp_sec();
for (i = 0; i < MAX_RECORD_APP_NUM; i++)
{
if (node->visit_info[i].app_id == 0)
{
return;
}
}
for (i = 0; i < MAX_RECORD_APP_NUM; i++)
{
if (count >= MAX_EXPIRED_VISIT_INFO_COUNT)
break;
if (node->visit_info[i].total_num > 3)
{
timeout = 180;
}
else
{
timeout = 60;
}
if (cur_timep - node->visit_info[i].latest_time > timeout)
{
// 3?��o?��??3y????
memset(&node->visit_info[i], 0x0, sizeof(app_visit_info_t));
count++;
spin_lock_bh(&node->visit_info_lock);
for (i = 0; i < MAX_VISIT_INFO_HASH_SIZE; i++) {
head = &node->visit_info_hash[i];
hlist_for_each_entry_safe(info, n, head, hlist) {
if (count >= MAX_EXPIRED_VISIT_INFO_COUNT)
break;
if (info->total_num > 3) {
timeout = 180;
} else {
timeout = 60;
}
if (cur_timep - info->latest_time > timeout) {
hlist_del(&info->hlist);
spin_unlock_bh(&node->visit_info_lock);
kfree(info);
spin_lock_bh(&node->visit_info_lock);
count++;
}
}
}
spin_unlock_bh(&node->visit_info_lock);
}
static int __af_visit_info_report(af_client_info_t *node)
#define VISIT_INFO_TIMEOUT_SEC 300
void check_expired_visit_info(af_client_info_t *node)
{
int i;
u_int32_t cur_timep = 0;
struct hlist_head *head;
struct hlist_node *n;
app_visit_info_t *info;
if (!node)
return;
cur_timep = af_get_timestamp_sec();
spin_lock_bh(&node->visit_info_lock);
for (i = 0; i < MAX_VISIT_INFO_HASH_SIZE; i++) {
head = &node->visit_info_hash[i];
hlist_for_each_entry_safe(info, n, head, hlist) {
if (cur_timep - info->latest_time > VISIT_INFO_TIMEOUT_SEC) {
hlist_del(&info->hlist);
spin_unlock_bh(&node->visit_info_lock);
kfree(info);
spin_lock_bh(&node->visit_info_lock);
}
}
}
spin_unlock_bh(&node->visit_info_lock);
}
static int compare_visit_info_count(const void *a, const void *b)
{
const app_visit_info_t *info_a = *(const app_visit_info_t **)a;
const app_visit_info_t *info_b = *(const app_visit_info_t **)b;
if (info_a->total_num > info_b->total_num)
return -1;
else if (info_a->total_num < info_b->total_num)
return 1;
return 0;
}
int __af_visit_info_report(af_client_info_t *node)
{
unsigned char mac_str[32] = {0};
unsigned char ip_str[32] = {0};
int i;
int count = 0;
int total_count = 0;
char *out = NULL;
cJSON *visit_obj = NULL;
cJSON *visit_info_array = NULL;
cJSON *root_obj = NULL;
flush_expired_visit_info(node);
struct hlist_head *head;
struct hlist_node *tmp;
app_visit_info_t *info;
app_visit_info_t *info_array[MAX_RECORD_APP_NUM];
int report_count = 0;
root_obj = cJSON_CreateObject();
if (!root_obj)
@@ -284,28 +501,44 @@ static int __af_visit_info_report(af_client_info_t *node)
cJSON_AddNumberToObject(root_obj, "down_flow", (u32)(node->period_flow.down_bytes >> 10));
cJSON_AddNumberToObject(root_obj, "active", node->active);
visit_info_array = cJSON_CreateArray();
for (i = 0; i < MAX_RECORD_APP_NUM; i++)
{
if (node->visit_info[i].app_id == 0)
continue;
count++;
visit_obj = cJSON_CreateObject();
cJSON_AddNumberToObject(visit_obj, "appid", node->visit_info[i].app_id);
cJSON_AddNumberToObject(visit_obj, "latest_action", node->visit_info[i].latest_action);
memset((char *)&node->visit_info[i], 0x0, sizeof(app_visit_info_t));
cJSON_AddItemToArray(visit_info_array, visit_obj);
spin_lock_bh(&node->visit_info_lock);
for (i = 0; i < MAX_VISIT_INFO_HASH_SIZE; i++) {
head = &node->visit_info_hash[i];
hlist_for_each_entry(info, head, hlist) {
if (info->total_num == 0)
continue;
if (info->is_http && info->conn_count <= g_min_http_match_count) {
info->total_num = 0;
continue;
}
info_array[total_count++] = info;
info->total_num = 0; //clean all
}
}
if (total_count > 0) {
sort(info_array, total_count, sizeof(app_visit_info_t *), compare_visit_info_count, NULL);
report_count = total_count > g_max_app_report_count ? g_max_app_report_count : total_count;
}
visit_info_array = cJSON_CreateArray();
for (i = 0; i < report_count; i++) {
info = info_array[i];
visit_obj = cJSON_CreateObject();
cJSON_AddNumberToObject(visit_obj, "appid", info->app_id);
cJSON_AddNumberToObject(visit_obj, "latest_action", info->latest_action);
info->total_num = 0;
cJSON_AddItemToArray(visit_info_array, visit_obj);
count++;
}
spin_unlock_bh(&node->visit_info_lock);
cJSON_AddItemToObject(root_obj, "visit_info", visit_info_array);
out = cJSON_Print(root_obj);
if (!out) {
cJSON_Delete(root_obj);
if (!out)
return 0;
}
cJSON_Minify(out);
AF_INFO("report:%s count=%d\n", out, node->report_count);
node->report_count++;
af_send_msg_to_user(out, strlen(out));
cJSON_Delete(root_obj);
@@ -330,7 +563,7 @@ static inline int get_packet_dir(struct net_device *in)
static void af_update_client_status(af_client_info_t *node)
void af_update_client_status(af_client_info_t *node)
{
if (node->last_flow.down_bytes > 0){
node->period_flow.down_bytes += (node->flow.down_bytes - node->last_flow.down_bytes);
@@ -340,7 +573,7 @@ static void af_update_client_status(af_client_info_t *node)
}
AF_LMT_DEBUG("period flow down:%llu up: %llu pkg up %d\n", node->period_flow.down_bytes,
node->period_flow.up_bytes, node->rate.pkt_up_rate);
// 2s
node->rate.up_rate = (node->flow.up_bytes - node->last_flow.up_bytes) >> 1;
node->rate.down_rate = (node->flow.down_bytes - node->last_flow.down_bytes) >> 1;
node->rate.pkt_up_rate = (node->flow.up_pkts - node->last_flow.up_pkts) >> 1;
@@ -350,7 +583,15 @@ static void af_update_client_status(af_client_info_t *node)
node->last_flow.down_bytes = node->flow.down_bytes;
node->last_flow.up_pkts = node->flow.up_pkts;
node->last_flow.down_pkts = node->flow.down_pkts;
if (node->rate.pkt_down_rate > 10 || node->rate.pkt_up_rate > 5){
if (is_client_mac_filter_blocked(node)) {
node->active = 0;
node->active_time = 0;
node->inactive_time = 0;
return;
}
if (node->rate.pkt_down_rate > 20){
node->active_time++;
node->inactive_time = 0;
node->active = 1;
@@ -448,6 +689,7 @@ static u_int32_t af_client_hook(unsigned int hook,
}
nfc->flow.up_bytes += skb->len;
nfc->flow.up_pkts++;
nfc->update_jiffies = jiffies;
}
AF_CLIENT_UNLOCK_W();
@@ -470,6 +712,8 @@ static u_int32_t af_client_hook2(unsigned int hook,
int (*okfn)(struct sk_buff *))
{
#endif
struct ethhdr *ethhdr = NULL;
unsigned char smac[ETH_ALEN];
af_client_info_t *nfc = NULL;
int pkt_dir = 0;
struct iphdr *iph = NULL;
@@ -523,6 +767,7 @@ static u_int32_t af_client_hook2(unsigned int hook,
if (nfc){
nfc->flow.down_bytes += skb->len;
nfc->flow.down_pkts++;
nfc->update_jiffies = jiffies;
}
AF_CLIENT_UNLOCK_R();
@@ -572,10 +817,10 @@ static struct nf_hook_ops af_client_ops[] = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
static void client_timer_handler(struct timer_list *t)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 16, 0)
af_client_info_t *client = from_timer(client, t, client_timer);
#else
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 16, 0)
af_client_info_t *client = timer_container_of(client, t, client_timer);
#else
af_client_info_t *client = from_timer(client, t, client_timer);
#endif
#else
static void client_timer_handler(unsigned long data)
@@ -586,13 +831,14 @@ static void client_timer_handler(unsigned long data)
AF_ERROR("client timer handler: invalid client\n");
return;
}
if (client->timer_count >= 30) {
__af_visit_info_report(client);
if (client->timer_count >= 30) {
__af_visit_info_report(client);
client->timer_count = 0;
}
}
af_update_client_status(client);
check_expired_visit_info(client);
af_update_client_status(client);
client->timer_count++;
mod_timer(&client->client_timer, jiffies + HZ * 2);
}
@@ -621,10 +867,10 @@ static void client_timer_handler(unsigned long data)
return;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 16, 0)
del_timer_sync(&client->client_timer);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 0)
timer_shutdown_sync(&client->client_timer);
#else
timer_delete_sync(&client->client_timer);
del_timer_sync(&client->client_timer);
#endif
}
@@ -641,13 +887,84 @@ int af_client_init(void)
err = nf_register_hooks(af_client_ops, ARRAY_SIZE(af_client_ops));
#endif
if (err) {
AF_ERROR("oaf register client hooks failed!\n");
AF_ERROR("register client hooks failed!\n");
}
AF_INFO("init app afclient ........ok\n");
return 0;
}
int fwx_api_add_record_whitelist(cJSON *data_obj)
{
cJSON *mac_array;
int i;
u8 mac_bin[ETH_ALEN];
if (!data_obj) {
return -1;
}
mac_array = cJSON_GetObjectItem(data_obj, "mac_list");
if (!mac_array) {
printk("mac_list not found\n");
return -1;
}
record_whitelist_write_lock();
for (i = 0; i < cJSON_GetArraySize(mac_array); i++) {
cJSON *mac_obj = cJSON_GetArrayItem(mac_array, i);
if (mac_obj && mac_str_to_bin(mac_obj->valuestring, mac_bin)) {
fwx_add_mac_node(&g_record_whitelist, mac_bin);
}
}
record_whitelist_write_unlock();
fwx_sync_record_whitelist_clients();
return 0;
}
int fwx_api_del_record_whitelist(cJSON *data_obj)
{
cJSON *mac_obj;
u8 mac_bin[ETH_ALEN];
struct mac_node *node;
if (!data_obj) {
return -1;
}
mac_obj = cJSON_GetObjectItem(data_obj, "mac");
if (!mac_obj) {
printk("mac not found\n");
return -1;
}
if (!mac_str_to_bin(mac_obj->valuestring, mac_bin)) {
printk("invalid mac format\n");
return -1;
}
record_whitelist_write_lock();
node = fwx_find_mac_node(&g_record_whitelist, mac_bin);
if (node) {
hlist_del(&node->hlist);
kfree(node);
record_whitelist_write_unlock();
fwx_sync_record_whitelist_clients();
return 0;
}
record_whitelist_write_unlock();
return -1;
}
int fwx_api_flush_record_whitelist(cJSON *data_obj)
{
record_whitelist_write_lock();
fwx_flush_mac_list(&g_record_whitelist);
record_whitelist_write_unlock();
fwx_sync_record_whitelist_clients();
return 0;
}
void af_client_exit(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
+33 -9
View File
@@ -1,14 +1,26 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __AF_CLIENT_H__
#define __AF_CLIENT_H__
#include "app_filter.h"
#include "fwx.h"
struct cJSON;
extern rwlock_t af_client_lock;
extern u32 nfc_debug_level;
extern int g_max_app_report_count;
extern int g_min_http_match_count;
#define VISIT_INFO_TIMEOUT_SEC 300
#define MAX_AF_CLIENT_HASH_SIZE 64
#define NF_CLIENT_TIMER_EXPIRE 1
#define MAX_CLIENT_ACTIVE_TIME 90
#define MAX_CLIENT_ACTIVE_TIME 180
#define AF_CLIENT_LOCK_R() read_lock_bh(&af_client_lock);
#define AF_CLIENT_UNLOCK_R() read_unlock_bh(&af_client_lock);
@@ -22,14 +34,11 @@ extern u32 nfc_debug_level;
((unsigned char *)&addr)[3]
#define NIPQUAD_FMT "%u.%u.%u.%u"
enum NFC_PKT_DIR
{
PKT_DIR_DOWN,
PKT_DIR_UP
};
#define MAX_VISIT_HISTORY_TIME 24
#define MAX_RECORD_APP_NUM 64
#define MAX_VISIT_INFO_HASH_SIZE 32
#define MIN_REPORT_URL_LEN 4
#define MAX_REPORT_URL_LEN 64
@@ -50,11 +59,14 @@ typedef struct flow_rate
}flow_rate_t;
typedef struct app_visit_info
{
struct hlist_node hlist;
unsigned int app_id;
unsigned int total_num;
unsigned int drop_num;
unsigned long latest_time;
unsigned int latest_action;
unsigned int conn_count;
unsigned int is_http;
} app_visit_info_t;
typedef struct visiting_info{
@@ -81,10 +93,13 @@ typedef struct af_client_info
int active_time;
int inactive_time;
int active;
int record_whitelist;
visiting_info_t visiting;
int timer_count;
int report_count;
app_visit_info_t visit_info[MAX_RECORD_APP_NUM];
unsigned int timer_count;
spinlock_t visit_info_lock;
struct hlist_head visit_info_hash[MAX_VISIT_INFO_HASH_SIZE];
struct proc_dir_entry *proc_dir;
} af_client_info_t;
int af_client_init(void);
@@ -102,5 +117,14 @@ void af_visit_info_report(void);
void af_client_list_reset_report_num(void);
af_client_info_t *nf_client_add(unsigned char *mac);
af_client_info_t *find_and_add_af_client(unsigned char *mac);
app_visit_info_t *get_or_create_visit_info(af_client_info_t *node, unsigned int app_id);
int af_update_client_app_info(af_client_info_t *node, int app_id, int drop, int from_conntrack, int is_http, int update_visiting);
void check_expired_visit_info(af_client_info_t *node);
int fwx_match_record_whitelist(const unsigned char *mac);
int fwx_set_record_whitelist(const char *mac_list_str);
int fwx_api_add_record_whitelist(struct cJSON *data_obj);
int fwx_api_del_record_whitelist(struct cJSON *data_obj);
int fwx_api_flush_record_whitelist(struct cJSON *data_obj);
#endif
+862
View File
@@ -0,0 +1,862 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/netlink.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/version.h>
#include <net/sock.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/netfilter_bridge.h>
#include <linux/version.h>
#include <linux/time.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <linux/etherdevice.h>
#include <linux/proc_fs.h>
#include <linux/sysctl.h>
#include <linux/mutex.h>
#include "fwx_utils.h"
#include "k_json.h"
#include "fwx_log.h"
#include "fwx_client.h"
extern char *ipv6_to_str(const struct in6_addr *addr, char *str);
extern struct list_head af_client_list_table[MAX_AF_CLIENT_HASH_SIZE];
struct af_client_iter_state
{
unsigned int bucket;
void *head;
};
static void *af_client_get_first(struct seq_file *seq)
{
struct af_client_iter_state *st = seq->private;
for (st->bucket = 0; st->bucket < MAX_AF_CLIENT_HASH_SIZE; st->bucket++)
{
if (!list_empty(&(af_client_list_table[st->bucket])))
{
st->head = &(af_client_list_table[st->bucket]);
return af_client_list_table[st->bucket].next;
}
}
return NULL;
}
static void *af_client_get_next(struct seq_file *seq,
void *head)
{
struct af_client_iter_state *st = seq->private;
struct hlist_node *node = (struct hlist_node *)head;
node = node->next;
if (node != st->head)
{
return node;
}
else
{
st->bucket++;
for (; st->bucket < MAX_AF_CLIENT_HASH_SIZE; st->bucket++)
{
if (!list_empty(&(af_client_list_table[st->bucket])))
{
st->head = &(af_client_list_table[st->bucket]);
return af_client_list_table[st->bucket].next;
}
}
return NULL;
}
}
static void *af_client_get_idx(struct seq_file *seq, loff_t pos)
{
void *head = af_client_get_first(seq);
if (head)
while (pos && (head = af_client_get_next(seq, head)))
pos--;
return pos ? NULL : head;
}
static void *af_client_seq_start(struct seq_file *s, loff_t *pos)
{
AF_CLIENT_LOCK_R();
if (*pos == 0)
{
return SEQ_START_TOKEN;
}
return af_client_get_idx(s, *pos - 1);
}
static void *af_client_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
if (v == SEQ_START_TOKEN)
return af_client_get_idx(s, 0);
return af_client_get_next(s, v);
}
static void af_client_seq_stop(struct seq_file *s, void *v)
{
AF_CLIENT_UNLOCK_R();
}
static int af_client_seq_show(struct seq_file *s, void *v)
{
unsigned char mac_str[32] = {0};
unsigned char ip_str[32] = {0};
unsigned char ipv6_str[128];
int status = 1;
static int index = 0;
af_client_info_t *node = (af_client_info_t *)v;
if (v == SEQ_START_TOKEN)
{
index = 0;
seq_printf(s, "%-4s %-20s %-20s %-32s %-12s %-8s %-16s %-16s\n", "Id", "Mac", "IP", "IPv6", "RecordWl", "Status", "UpRate", "DownRate");
return 0;
}
index++;
sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac));
sprintf(ip_str, "%pI4", &node->ip);
ipv6_to_str(&node->ipv6, ipv6_str);
status = node->active ? 2 : 1;
seq_printf(s, "%-4d %-20s %-20s %-32s %-12d %-8d %-16d %-16d\n", index, mac_str, ip_str, ipv6_str, node->record_whitelist, status, node->rate.up_rate, node->rate.down_rate);
return 0;
}
static const struct seq_operations nf_client_seq_ops = {
.start = af_client_seq_start,
.next = af_client_seq_next,
.stop = af_client_seq_stop,
.show = af_client_seq_show};
static int af_client_open(struct inode *inode, struct file *file)
{
struct seq_file *seq;
struct af_client_iter_state *iter;
int err;
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
if (!iter)
return -ENOMEM;
err = seq_open(file, &nf_client_seq_ops);
if (err)
{
kfree(iter);
return err;
}
seq = file->private_data;
seq->private = iter;
return 0;
}
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0)
static const struct file_operations af_client_fops = {
.owner = THIS_MODULE,
.open = af_client_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
#else
static const struct proc_ops af_client_fops = {
.proc_flags = PROC_ENTRY_PERMANENT,
.proc_read = seq_read,
.proc_open = af_client_open,
.proc_lseek = seq_lseek,
.proc_release = seq_release_private,
};
#endif
#define AF_CLIENT_PROC_STR "af_client"
static int af_visiting_seq_show(struct seq_file *s, void *v)
{
unsigned char mac_str[32] = {0};
unsigned char ip_str[32] = {0};
static int index = 0;
int i;
af_client_info_t *node = (af_client_info_t *)v;
if (v == SEQ_START_TOKEN)
{
index = 0;
seq_printf(s, "%-20s %-12s %-32s\n", "Mac", "Appid", "Url");
return 0;
}
index++;
sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac));
int visiting_app = 0;
char visiting_url[64] = {0};
if (af_get_timestamp_sec() - node->visiting.app_time < 120){
visiting_app = node->visiting.visiting_app;
}
if ( af_get_timestamp_sec() - node->visiting.url_time < 120 ){
strncpy(visiting_url, node->visiting.visiting_url, sizeof(visiting_url));
}
else{
strcpy(visiting_url, "none");
}
seq_printf(s, "%-20s %-12d %-32s\n", mac_str, visiting_app, visiting_url);
return 0;
}
static const struct seq_operations nf_visiting_seq_ops = {
.start = af_client_seq_start,
.next = af_client_seq_next,
.stop = af_client_seq_stop,
.show = af_visiting_seq_show
};
static int af_visiting_open(struct inode *inode, struct file *file)
{
struct seq_file *seq;
struct af_client_iter_state *iter;
int err;
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
if (!iter)
return -ENOMEM;
err = seq_open(file, &nf_visiting_seq_ops);
if (err)
{
kfree(iter);
return err;
}
seq = file->private_data;
seq->private = iter;
return 0;
}
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0)
static const struct file_operations af_visiting_fops = {
.owner = THIS_MODULE,
.open = af_visiting_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
#else
static const struct proc_ops af_visiting_fops = {
.proc_flags = PROC_ENTRY_PERMANENT,
.proc_read = seq_read,
.proc_open = af_visiting_open,
.proc_lseek = seq_lseek,
.proc_release = seq_release_private,
};
#endif
#define AF_VISIT_INFO "af_visit"
#define AF_CLIENT_VISIT_LIST "af_client_visit_list"
#define AF_CLIENT_BASE_DIR "fwx_client"
static struct proc_dir_entry *g_af_client_base_dir = NULL;
static DEFINE_MUTEX(af_client_base_dir_mutex);
void remove_client_proc_dir(af_client_info_t *client);
struct af_client_visit_iter_state
{
unsigned int client_bucket;
unsigned int visit_bucket;
af_client_info_t *current_client;
struct hlist_node *current_visit_node;
};
static void *af_client_visit_get_first_client(struct seq_file *seq)
{
struct af_client_visit_iter_state *st = seq->private;
af_client_info_t *client;
for (st->client_bucket = 0; st->client_bucket < MAX_AF_CLIENT_HASH_SIZE; st->client_bucket++) {
if (!list_empty(&af_client_list_table[st->client_bucket])) {
client = list_first_entry(&af_client_list_table[st->client_bucket], af_client_info_t, hlist);
st->current_client = client;
st->visit_bucket = 0;
return client;
}
}
return NULL;
}
static void *af_client_visit_get_next_visit(struct seq_file *seq, af_client_info_t *client)
{
struct af_client_visit_iter_state *st = seq->private;
struct hlist_head *head;
app_visit_info_t *info;
if (!client)
return NULL;
for (; st->visit_bucket < MAX_VISIT_INFO_HASH_SIZE; st->visit_bucket++) {
head = &client->visit_info_hash[st->visit_bucket];
if (!hlist_empty(head)) {
info = hlist_entry(head->first, app_visit_info_t, hlist);
st->current_visit_node = head->first;
return info;
}
}
return NULL;
}
static void *af_client_visit_get_next(struct seq_file *seq)
{
struct af_client_visit_iter_state *st = seq->private;
app_visit_info_t *info;
struct hlist_node *next;
struct hlist_head *head;
if (!st->current_client)
return NULL;
if (st->current_visit_node) {
next = st->current_visit_node->next;
if (next) {
st->current_visit_node = next;
info = hlist_entry(next, app_visit_info_t, hlist);
return info;
}
st->visit_bucket++;
st->current_visit_node = NULL;
}
for (; st->visit_bucket < MAX_VISIT_INFO_HASH_SIZE; st->visit_bucket++) {
head = &st->current_client->visit_info_hash[st->visit_bucket];
if (!hlist_empty(head)) {
st->current_visit_node = head->first;
info = hlist_entry(head->first, app_visit_info_t, hlist);
return info;
}
}
return NULL;
}
static void *af_client_visit_get_next_client(struct seq_file *seq)
{
struct af_client_visit_iter_state *st = seq->private;
af_client_info_t *client;
struct list_head *next;
if (!st->current_client)
return NULL;
next = st->current_client->hlist.next;
if (next != &af_client_list_table[st->client_bucket]) {
client = list_entry(next, af_client_info_t, hlist);
st->current_client = client;
st->visit_bucket = 0;
st->current_visit_node = NULL;
return af_client_visit_get_next_visit(seq, client);
}
st->client_bucket++;
for (; st->client_bucket < MAX_AF_CLIENT_HASH_SIZE; st->client_bucket++) {
if (!list_empty(&af_client_list_table[st->client_bucket])) {
client = list_first_entry(&af_client_list_table[st->client_bucket], af_client_info_t, hlist);
st->current_client = client;
st->visit_bucket = 0;
st->current_visit_node = NULL;
return af_client_visit_get_next_visit(seq, client);
}
}
return NULL;
}
static void *af_client_visit_seq_start(struct seq_file *s, loff_t *pos)
{
struct af_client_visit_iter_state *st = s->private;
void *v = NULL;
AF_CLIENT_LOCK_R();
if (*pos == 0) {
return SEQ_START_TOKEN;
}
v = af_client_visit_get_first_client(s);
if (!v)
return NULL;
v = af_client_visit_get_next_visit(s, (af_client_info_t *)v);
(*pos)--;
while (*pos > 0 && v) {
v = af_client_visit_get_next(s);
if (!v) {
v = af_client_visit_get_next_client(s);
}
(*pos)--;
}
return v;
}
static void *af_client_visit_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
struct af_client_visit_iter_state *st = s->private;
void *next;
(*pos)++;
if (v == SEQ_START_TOKEN) {
next = af_client_visit_get_first_client(s);
if (!next)
return NULL;
return af_client_visit_get_next_visit(s, (af_client_info_t *)next);
}
next = af_client_visit_get_next(s);
if (!next) {
next = af_client_visit_get_next_client(s);
}
return next;
}
static void af_client_visit_seq_stop(struct seq_file *s, void *v)
{
AF_CLIENT_UNLOCK_R();
}
static void print_client_visit_header(struct seq_file *s)
{
seq_printf(s, "%-20s %-8s %-8s %-8s %-8s %-8s %-12s %-12s %-10s\n",
"MAC", "AppID", "TotalNum", "DropNum", "Conn", "IsHttp", "LatestTime", "LatestAction", "OfflineTime");
}
static int af_client_visit_seq_show(struct seq_file *s, void *v)
{
unsigned char mac_str[32] = {0};
unsigned char ip_str[32] = {0};
app_visit_info_t *info = (app_visit_info_t *)v;
struct af_client_visit_iter_state *st = s->private;
if (v == SEQ_START_TOKEN) {
print_client_visit_header(s);
return 0;
}
if (!info || !st->current_client)
return 0;
sprintf(mac_str, MAC_FMT, MAC_ARRAY(st->current_client->mac));
spin_lock_bh(&st->current_client->visit_info_lock);
u_int32_t cur_time = af_get_timestamp_sec();
u_int32_t offline_time = (cur_time > info->latest_time) ? (cur_time - info->latest_time) : 0;
seq_printf(s, "%-20s %-8u %-8u %-8u %-8u %-8u %-12lu %-12u %-10u\n",
mac_str,
info->app_id,
info->total_num,
info->drop_num,
info->conn_count,
info->is_http,
info->latest_time,
info->latest_action,
offline_time);
spin_unlock_bh(&st->current_client->visit_info_lock);
return 0;
}
static const struct seq_operations af_client_visit_seq_ops = {
.start = af_client_visit_seq_start,
.next = af_client_visit_seq_next,
.stop = af_client_visit_seq_stop,
.show = af_client_visit_seq_show
};
static int af_client_visit_open(struct inode *inode, struct file *file)
{
struct seq_file *seq;
struct af_client_visit_iter_state *iter;
int err;
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
if (!iter)
return -ENOMEM;
err = seq_open(file, &af_client_visit_seq_ops);
if (err) {
kfree(iter);
return err;
}
seq = file->private_data;
seq->private = iter;
return 0;
}
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0)
static const struct file_operations af_client_visit_fops = {
.owner = THIS_MODULE,
.open = af_client_visit_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
#else
static const struct proc_ops af_client_visit_fops = {
.proc_flags = PROC_ENTRY_PERMANENT,
.proc_read = seq_read,
.proc_open = af_client_visit_open,
.proc_lseek = seq_lseek,
.proc_release = seq_release_private,
};
#endif
int init_af_client_procfs(void)
{
struct proc_dir_entry *pde;
struct net *net = &init_net;
pde = proc_create(AF_CLIENT_PROC_STR, 0440, net->proc_net, &af_client_fops);
if (!pde)
{
AF_ERROR("nf_client proc file created error\n");
return -1;
}
pde = proc_create(AF_VISIT_INFO, 0440, net->proc_net, &af_visiting_fops);
if (!pde)
{
AF_ERROR("nf_client visiting info proc file created error\n");
return -1;
}
pde = proc_create(AF_CLIENT_VISIT_LIST, 0440, net->proc_net, &af_client_visit_fops);
if (!pde)
{
AF_ERROR("nf_client visit list proc file created error\n");
return -1;
}
return 0;
}
void finit_af_client_procfs(void)
{
struct net *net = &init_net;
int i;
af_client_info_t *client;
mutex_lock(&af_client_base_dir_mutex);
if (g_af_client_base_dir) {
AF_CLIENT_LOCK_R();
for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++) {
list_for_each_entry(client, &af_client_list_table[i], hlist) {
if (client && client->proc_dir) {
remove_client_proc_dir(client);
}
}
}
AF_CLIENT_UNLOCK_R();
}
remove_proc_entry(AF_CLIENT_PROC_STR, net->proc_net);
remove_proc_entry(AF_VISIT_INFO, net->proc_net);
remove_proc_entry(AF_CLIENT_VISIT_LIST, net->proc_net);
remove_proc_entry(AF_CLIENT_BASE_DIR, net->proc_net);
g_af_client_base_dir = NULL; // 重置静态变量
mutex_unlock(&af_client_base_dir_mutex);
}
static void print_single_client_visit_header(struct seq_file *s)
{
seq_printf(s, "%-8s %-8s %-8s %-8s %-8s %-12s %-12s %-10s\n",
"AppID", "TotalNum", "DropNum", "Conn", "IsHttp", "LatestTime", "LatestAction", "OfflineTime");
}
struct single_client_visit_iter_state
{
unsigned int visit_bucket;
struct hlist_node *current_visit_node;
af_client_info_t *client;
};
static void *single_client_visit_seq_start(struct seq_file *s, loff_t *pos)
{
struct single_client_visit_iter_state *st = s->private;
struct hlist_head *head;
app_visit_info_t *info;
if (!st->client)
return NULL;
spin_lock_bh(&st->client->visit_info_lock);
if (*pos == 0) {
return SEQ_START_TOKEN;
}
st->visit_bucket = 0;
st->current_visit_node = NULL;
for (; st->visit_bucket < MAX_VISIT_INFO_HASH_SIZE; st->visit_bucket++) {
head = &st->client->visit_info_hash[st->visit_bucket];
if (!hlist_empty(head)) {
st->current_visit_node = head->first;
info = hlist_entry(head->first, app_visit_info_t, hlist);
(*pos)--;
if (*pos == 0) {
return info;
}
break;
}
}
while (*pos > 0 && st->visit_bucket < MAX_VISIT_INFO_HASH_SIZE) {
if (st->current_visit_node) {
struct hlist_node *next = st->current_visit_node->next;
if (next) {
st->current_visit_node = next;
info = hlist_entry(next, app_visit_info_t, hlist);
(*pos)--;
if (*pos == 0) {
return info;
}
continue;
}
st->visit_bucket++;
st->current_visit_node = NULL;
}
for (; st->visit_bucket < MAX_VISIT_INFO_HASH_SIZE; st->visit_bucket++) {
head = &st->client->visit_info_hash[st->visit_bucket];
if (!hlist_empty(head)) {
st->current_visit_node = head->first;
info = hlist_entry(head->first, app_visit_info_t, hlist);
(*pos)--;
if (*pos == 0) {
return info;
}
break;
}
}
}
return NULL;
}
static void *single_client_visit_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
struct single_client_visit_iter_state *st = s->private;
struct hlist_head *head;
app_visit_info_t *info;
(*pos)++;
if (v == SEQ_START_TOKEN) {
st->visit_bucket = 0;
st->current_visit_node = NULL;
for (; st->visit_bucket < MAX_VISIT_INFO_HASH_SIZE; st->visit_bucket++) {
head = &st->client->visit_info_hash[st->visit_bucket];
if (!hlist_empty(head)) {
st->current_visit_node = head->first;
return hlist_entry(head->first, app_visit_info_t, hlist);
}
}
return NULL;
}
if (st->current_visit_node) {
struct hlist_node *next = st->current_visit_node->next;
if (next) {
st->current_visit_node = next;
return hlist_entry(next, app_visit_info_t, hlist);
}
st->visit_bucket++;
st->current_visit_node = NULL;
}
for (; st->visit_bucket < MAX_VISIT_INFO_HASH_SIZE; st->visit_bucket++) {
head = &st->client->visit_info_hash[st->visit_bucket];
if (!hlist_empty(head)) {
st->current_visit_node = head->first;
return hlist_entry(head->first, app_visit_info_t, hlist);
}
}
return NULL;
}
static void single_client_visit_seq_stop(struct seq_file *s, void *v)
{
struct single_client_visit_iter_state *st = s->private;
if (st->client) {
spin_unlock_bh(&st->client->visit_info_lock);
}
}
static int single_client_visit_seq_show(struct seq_file *s, void *v)
{
app_visit_info_t *info = (app_visit_info_t *)v;
if (v == SEQ_START_TOKEN) {
print_single_client_visit_header(s);
return 0;
}
if (!info)
return 0;
u_int32_t cur_time = af_get_timestamp_sec();
u_int32_t offline_time = (cur_time > info->latest_time) ? (cur_time - info->latest_time) : 0;
seq_printf(s, "%-8u %-8u %-8u %-8u %-8u %-12lu %-12u %-10u\n",
info->app_id,
info->total_num,
info->drop_num,
info->conn_count,
info->is_http,
info->latest_time,
info->latest_action,
offline_time);
return 0;
}
static const struct seq_operations single_client_visit_seq_ops = {
.start = single_client_visit_seq_start,
.next = single_client_visit_seq_next,
.stop = single_client_visit_seq_stop,
.show = single_client_visit_seq_show
};
static int single_client_visit_open(struct inode *inode, struct file *file)
{
struct seq_file *seq;
struct single_client_visit_iter_state *iter;
af_client_info_t *client;
int err;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0)
client = PDE_DATA(inode);
#else
client = (af_client_info_t *)proc_get_parent_data(inode);
#endif
if (!client)
return -ENOENT;
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
if (!iter)
return -ENOMEM;
iter->client = client;
err = seq_open(file, &single_client_visit_seq_ops);
if (err) {
kfree(iter);
return err;
}
seq = file->private_data;
seq->private = iter;
return 0;
}
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0)
static const struct file_operations single_client_visit_fops = {
.owner = THIS_MODULE,
.open = single_client_visit_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
#else
static const struct proc_ops single_client_visit_fops = {
.proc_flags = PROC_ENTRY_PERMANENT,
.proc_read = seq_read,
.proc_open = single_client_visit_open,
.proc_lseek = seq_lseek,
.proc_release = seq_release_private,
};
#endif
int create_client_proc_dir(af_client_info_t *client)
{
struct proc_dir_entry *client_dir;
struct proc_dir_entry *visit_file;
char mac_str[32] = {0};
struct net *net = &init_net;
if (!client)
return -1;
sprintf(mac_str, MAC_FMT, MAC_ARRAY(client->mac));
mutex_lock(&af_client_base_dir_mutex);
if (!g_af_client_base_dir) {
g_af_client_base_dir = proc_mkdir(AF_CLIENT_BASE_DIR, net->proc_net);
if (!g_af_client_base_dir) {
mutex_unlock(&af_client_base_dir_mutex);
AF_ERROR("create af_client base dir failed\n");
return -1;
}
}
mutex_unlock(&af_client_base_dir_mutex);
client_dir = proc_mkdir_data(mac_str, 0555, g_af_client_base_dir, client);
if (!client_dir) {
AF_ERROR("create client dir failed: %s\n", mac_str);
return -1;
}
client->proc_dir = client_dir;
visit_file = proc_create_data("visit_list", 0444, client_dir, &single_client_visit_fops, client);
if (!visit_file) {
AF_ERROR("create visit_list file failed for client: %s\n", mac_str);
proc_remove(client_dir);
client->proc_dir = NULL;
return -1;
}
return 0;
}
void remove_client_proc_dir(af_client_info_t *client)
{
if (!client || !client->proc_dir)
return;
proc_remove(client->proc_dir);
client->proc_dir = NULL;
}
+14
View File
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __AF_CLIENT_FS_H__
#define __AF_CLIENT_FS_H__
int init_af_client_procfs(void);
void finit_af_client_procfs(void);
int create_client_proc_dir(af_client_info_t *client);
void remove_client_proc_dir(af_client_info_t *client);
#endif
+201
View File
@@ -0,0 +1,201 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <net/tcp.h>
#include <linux/netfilter.h>
#include <net/netfilter/nf_conntrack.h>
#include <linux/skbuff.h>
#include <net/ip.h>
#include <linux/types.h>
#include <net/sock.h>
#include <linux/etherdevice.h>
#include <linux/cdev.h>
#include <linux/vmalloc.h>
#include <linux/device.h>
#include <linux/version.h>
#include "fwx_config.h"
#include "k_json.h"
#include "fwx.h"
#include "fwx_mac_filter.h"
#include "fwx_app_filter.h"
#include "fwx_client.h"
static struct mutex fwx_cdev_mutex;
struct fwx_config_dev
{
dev_t id;
struct cdev char_dev;
struct class *c;
};
struct fwx_config_dev g_fwx_dev;
struct fwx_cdev_file
{
size_t size;
char buf[256 << 10];
};
k_request_item_t k_request_api_list[]={
{"add_mac_filter_rule", fwx_api_add_mac_filter_rule},
{"del_mac_filter_rule", fwx_api_del_mac_filter_rule},
{"mod_mac_filter_rule", fwx_api_mod_mac_filter_rule},
{"dump_mac_filter_rule", fwx_api_dump_mac_filter_rule},
{"flush_mac_filter_rule", fwx_api_flush_mac_filter_rule},
{"add_app_filter_rule", fwx_api_add_app_filter_rule},
{"del_app_filter_rule", fwx_api_del_app_filter_rule},
{"mod_app_filter_rule", fwx_api_mod_app_filter_rule},
{"dump_app_filter_rule", fwx_api_dump_app_filter_rule},
{"flush_app_filter_rule", fwx_api_flush_app_filter_rule},
{"add_mac_filter_whitelist", fwx_api_add_mac_filter_whitelist},
{"del_mac_filter_whitelist", fwx_api_del_mac_filter_whitelist},
{"flush_mac_filter_whitelist", fwx_api_flush_mac_filter_whitelist},
{"add_app_filter_whitelist", fwx_api_add_app_filter_whitelist},
{"flush_app_filter_whitelist", fwx_api_flush_app_filter_whitelist},
{"add_record_whitelist", fwx_api_add_record_whitelist},
{"del_record_whitelist", fwx_api_del_record_whitelist},
{"flush_record_whitelist", fwx_api_flush_record_whitelist},
};
int fwx_config_handle(char *config, unsigned int len)
{
int i;
cJSON *config_obj = NULL;
cJSON *api_obj = NULL;
cJSON *data_obj = NULL;
if (!config || len == 0)
return -1;
config_obj = cJSON_Parse(config);
if (!config_obj){
printk("parse json failed, value = %s\n", config);
return -1;
}
api_obj = cJSON_GetObjectItem(config_obj, "api");
data_obj = cJSON_GetObjectItem(config_obj, "data");
if (!api_obj){
printk("error, api obj not set\n");
cJSON_Delete(config_obj);
return -1;
}
cJSON *temp_data_obj = NULL;
if (!data_obj){
temp_data_obj = cJSON_CreateObject(); // 创建一个空的data对象
data_obj = temp_data_obj;
}
for (i = 0; i < ARRAY_SIZE(k_request_api_list); i++){
k_request_item_t *req_item = &k_request_api_list[i];
if (0 == strcmp(req_item->api, api_obj->valuestring)){
req_item->handle(data_obj);
break;
}
}
if (temp_data_obj){
cJSON_Delete(temp_data_obj);
}
cJSON_Delete(config_obj);
return 0;
}
static int fwx_cdev_open(struct inode *inode, struct file *filp)
{
struct fwx_cdev_file *file;
file = vzalloc(sizeof(*file));
if (!file)
return -EINVAL;
mutex_lock(&fwx_cdev_mutex);
filp->private_data = file;
return 0;
}
static ssize_t fwx_cdev_read(struct file *filp, char *buf, size_t count, loff_t *off)
{
return 0;
}
static int fwx_cdev_release(struct inode *inode, struct file *filp)
{
struct fwx_cdev_file *file = filp->private_data;
fwx_config_handle(file->buf, file->size);
filp->private_data = NULL;
mutex_unlock(&fwx_cdev_mutex);
vfree(file);
return 0;
}
static ssize_t fwx_cdev_write(struct file *filp, const char *buffer, size_t count, loff_t *off)
{
struct fwx_cdev_file *file = filp->private_data;
int ret;
if (file->size + count > sizeof(file->buf))
return -EINVAL;
ret = copy_from_user(file->buf + file->size, buffer, count);
if (ret != 0)
return -EINVAL;
file->size += count;
return count;
}
static struct file_operations fwx_cdev_ops = {
owner : THIS_MODULE,
release : fwx_cdev_release,
open : fwx_cdev_open,
write : fwx_cdev_write,
read : fwx_cdev_read,
};
int fwx_register_dev(void)
{
struct device *dev;
int res;
mutex_init(&fwx_cdev_mutex);
res = alloc_chrdev_region(&g_fwx_dev.id, 0, 1, FWX_CHAR_DEV);
if (res != 0)
return -EINVAL;
cdev_init(&g_fwx_dev.char_dev, &fwx_cdev_ops);
res = cdev_add(&g_fwx_dev.char_dev, g_fwx_dev.id, 1);
if (res < 0)
goto REGION_OUT;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
g_fwx_dev.c = class_create(THIS_MODULE, FWX_CHAR_DEV);
#else
g_fwx_dev.c = class_create(FWX_CHAR_DEV);
#endif
if (IS_ERR_OR_NULL(g_fwx_dev.c))
goto CDEV_OUT;
dev = device_create(g_fwx_dev.c, NULL, g_fwx_dev.id, NULL, FWX_CHAR_DEV);
if (IS_ERR_OR_NULL(dev))
goto CLASS_OUT;
return 0;
CLASS_OUT:
class_destroy(g_fwx_dev.c);
CDEV_OUT:
cdev_del(&g_fwx_dev.char_dev);
REGION_OUT:
unregister_chrdev_region(g_fwx_dev.id, 1);
printk("register char dev....fail\n");
return -EINVAL;
}
void fwx_unregister_dev(void)
{
device_destroy(g_fwx_dev.c, g_fwx_dev.id);
class_destroy(g_fwx_dev.c);
cdev_del(&g_fwx_dev.char_dev);
unregister_chrdev_region(g_fwx_dev.id, 1);
}
+19
View File
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_CONFIG_H__
#define __FWX_CONFIG_H__
#include "k_json.h"
#define FWX_CHAR_DEV "fwx"
typedef int (*k_request_handler)(cJSON *data_obj);
typedef struct k_request_item{
const char *api;
k_request_handler handle;
}k_request_item_t;
int fwx_register_dev(void);
void fwx_unregister_dev(void);
#endif
@@ -1,3 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h>
@@ -16,8 +20,9 @@
#include <net/sock.h>
#include <linux/etherdevice.h>
#include <linux/cdev.h>
#include "af_conntrack.h"
#include "af_log.h"
#include "fwx_conntrack.h"
#include "fwx_log.h"
#include "fwx.h"
struct hlist_head af_conn_table[AF_CONN_HASH_SIZE];
@@ -264,7 +269,7 @@ static const struct proc_ops af_conn_fops = {
#define AF_CONN_PROC_STR "af_conn"
static int af_conn_init_procfs(void)
int af_conn_init_procfs(void)
{
struct proc_dir_entry *pde;
struct net *net = &init_net;
@@ -278,7 +283,7 @@ static int af_conn_init_procfs(void)
return 0;
}
static void af_conn_remove_procfs(void)
void af_conn_remove_procfs(void)
{
struct net *net = &init_net;
remove_proc_entry(AF_CONN_PROC_STR, net->proc_net);
@@ -1,3 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __AF_SIMPLE_CONNTRACK_H__
#define __AF_SIMPLE_CONNTRACK_H__
+228 -183
View File
@@ -1,183 +1,228 @@
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/version.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <linux/sysctl.h>
#include "app_filter.h"
#include "af_log.h"
int af_log_lvl = 1;
int af_test_mode = 0;
// todo: rename af_log.c
int g_oaf_filter_enable __read_mostly = 0;
int g_oaf_record_enable __read_mostly = 0;
int g_by_pass_accl = 1;
int g_user_mode = 0;
int af_work_mode = AF_MODE_GATEWAY;
unsigned int af_lan_ip = 0;
unsigned int af_lan_mask = 0;
char g_lan_ifname[64] = "br-lan";
int g_tcp_rst = 1;
int g_feature_init = 0;
char g_oaf_version[64] = AF_VERSION;
int g_disable_quic = 0;
int g_app_filter_mode = 0; // 0 = specified apps, 1 = all apps
/*
cat /proc/sys/oaf/debug
*/
static struct ctl_table oaf_table[] = {
{
.procname = "debug",
.data = &af_log_lvl,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "feature_init",
.data = &g_feature_init,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "version",
.data = g_oaf_version,
.maxlen = 64,
.mode = 0444,
.proc_handler = proc_dostring,
},
{
.procname = "test_mode",
.data = &af_test_mode,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "enable",
.data = &g_oaf_filter_enable,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "by_pass_accl",
.data = &g_by_pass_accl,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "tcp_rst",
.data = &g_tcp_rst,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "lan_ifname",
.data = g_lan_ifname,
.maxlen = 64,
.mode = 0666,
.proc_handler = proc_dostring,
},
{
.procname = "record_enable",
.data = &g_oaf_record_enable,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "user_mode",
.data = &g_user_mode,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "work_mode",
.data = &af_work_mode,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "lan_ip",
.data = &af_lan_ip,
.maxlen = sizeof(unsigned int),
.mode = 0666,
.proc_handler = proc_douintvec,
},
{
.procname = "lan_mask",
.data = &af_lan_mask,
.maxlen = sizeof(unsigned int),
.mode = 0666,
.proc_handler = proc_douintvec,
},
{
.procname = "disable_quic",
.data = &g_disable_quic,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "app_filter_mode",
.data = &g_app_filter_mode,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 12, 0))
{
}
#endif
};
#define OAF_SYS_PROC_DIR "oaf"
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0))
static struct ctl_table oaf_root_table[] = {
{
.procname = OAF_SYS_PROC_DIR,
.mode = 0555,
.child = oaf_table,
},
{}
};
#endif
static struct ctl_table_header *oaf_table_header;
static int af_init_log_sysctl(void)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0))
oaf_table_header = register_sysctl_table(oaf_root_table);
#else
oaf_table_header = register_sysctl(OAF_SYS_PROC_DIR, oaf_table);
#endif
if (oaf_table_header == NULL){
printk("init log sysctl...failed\n");
return -ENOMEM;
}
return 0;
}
static int af_fini_log_sysctl(void)
{
if (oaf_table_header)
unregister_sysctl_table(oaf_table_header);
return 0;
}
int af_log_init(void){
af_init_log_sysctl();
return 0;
}
int af_log_exit(void){
af_fini_log_sysctl();
return 0;
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/version.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <linux/sysctl.h>
#include "fwx.h"
#include "fwx_log.h"
#include "fwx_mac_filter.h"
#include "fwx_app_filter.h"
#include "fwx_client.h"
int af_log_lvl = 0;
int fwx_test_mode = 0;
int g_record_enable = 0;
int g_by_pass_accl = 1;
int g_user_mode = 0;
int af_work_mode = AF_MODE_GATEWAY;
unsigned int fwx_lan_ip = 0;
unsigned int fwx_lan_mask = 0;
char g_lan_ifname[64] = "br-lan";
int g_tcp_rst = 1;
int g_feature_init = 0;
char g_fwx_version[64] = FWX_VERSION;
int g_feature_count = 0;
char g_record_whitelist[1024] = {0};
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 12, 0))
static int fwx_proc_record_whitelist_handler(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
#else
static int fwx_proc_record_whitelist_handler(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
#endif
{
int ret = 0;
ret = proc_dostring(table, write, buffer, lenp, ppos);
if (ret || !write) {
return ret;
}
fwx_set_record_whitelist(g_record_whitelist);
return ret;
}
static struct ctl_table fwx_table[] = {
{
.procname = "debug",
.data = &af_log_lvl,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "feature_init",
.data = &g_feature_init,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "version",
.data = g_fwx_version,
.maxlen = 64,
.mode = 0444,
.proc_handler = proc_dostring,
},
{
.procname = "feature_count",
.data = &g_feature_count,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "test_mode",
.data = &fwx_test_mode,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "appfilter_enable",
.data = &g_appfilter_enable,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "macfilter_enable",
.data = &g_mac_filter_enable,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "by_pass_accl",
.data = &g_by_pass_accl,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "tcp_rst",
.data = &g_tcp_rst,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "lan_ifname",
.data = g_lan_ifname,
.maxlen = 64,
.mode = 0666,
.proc_handler = proc_dostring,
},
{
.procname = "record_enable",
.data = &g_record_enable,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "record_whitelist",
.data = g_record_whitelist,
.maxlen = sizeof(g_record_whitelist),
.mode = 0666,
.proc_handler = fwx_proc_record_whitelist_handler,
},
{
.procname = "user_mode",
.data = &g_user_mode,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "work_mode",
.data = &af_work_mode,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "lan_ip",
.data = &fwx_lan_ip,
.maxlen = sizeof(unsigned int),
.mode = 0666,
.proc_handler = proc_douintvec,
},
{
.procname = "lan_mask",
.data = &fwx_lan_mask,
.maxlen = sizeof(unsigned int),
.mode = 0666,
.proc_handler = proc_douintvec,
},
{
.procname = "max_app_report_count",
.data = &g_max_app_report_count,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{
.procname = "min_http_match_count",
.data = &g_min_http_match_count,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = proc_dointvec,
},
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 12, 0))
{
}
#endif
};
#define FWX_SYS_PROC_DIR "fwx"
static struct ctl_table fwx_root_table[] = {
{
.procname = FWX_SYS_PROC_DIR,
.mode = 0555,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0))
.child = fwx_table,
#endif
},
{}
};
static struct ctl_table_header *fwx_table_header;
static int af_init_log_sysctl(void)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0))
fwx_table_header = register_sysctl_table(fwx_root_table);
#else
fwx_table_header = register_sysctl(FWX_SYS_PROC_DIR, fwx_table);
#endif
if (fwx_table_header == NULL){
printk("init log sysctl...failed\n");
return -ENOMEM;
}
return 0;
}
static int af_fini_log_sysctl(void)
{
if (fwx_table_header)
unregister_sysctl_table(fwx_table_header);
return 0;
}
int af_log_init(void){
af_init_log_sysctl();
return 0;
}
int af_log_exit(void){
af_fini_log_sysctl();
return 0;
}
+23
View File
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __AF_DEBUG_H__
#define __AF_DEBUG_H__
extern int fwx_test_mode;
extern int af_work_mode;
extern int g_app_filter_enable;
extern int g_record_enable;
extern int g_by_pass_accl;
extern unsigned int fwx_lan_ip;
extern unsigned int fwx_lan_mask;
extern int g_feature_init;
extern int g_user_mode;
extern char g_lan_ifname[64];
extern int g_tcp_rst;
extern int g_feature_count;
#define TEST_MODE() (fwx_test_mode)
int af_log_init(void);
int af_log_exit(void);
#endif
+81
View File
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/slab.h>
#include "fwx.h"
#include "fwx_mac.h"
static int mac_hash(const unsigned char *mac) {
int hash = 0;
int i;
for (i = 0; i < ETH_ALEN; i++) {
hash += mac[i];
}
return hash % MAC_HASH_SIZE;
}
void fwx_mac_config_init(mac_config_t *config){
int i;
for (i = 0; i < MAC_HASH_SIZE; i++){
INIT_HLIST_HEAD(&config->hash_table[i]);
}
}
void fwx_add_mac_node(mac_config_t *config, const unsigned char *mac) {
struct mac_node *new_node;
int hash = mac_hash(mac);
new_node = kmalloc(sizeof(struct mac_node), GFP_KERNEL);
if (!new_node) {
pr_err("Memory allocation failed\n");
return;
}
memcpy(new_node->mac, mac, ETH_ALEN);
INIT_HLIST_NODE(&new_node->hlist);
hlist_add_head(&new_node->hlist, &config->hash_table[hash]);
}
void fwx_dump_mac_node(mac_config_t *config) {
int i;
struct mac_node *node;
for(i = 0; i < MAC_HASH_SIZE; i++){
hlist_for_each_entry(node, &config->hash_table[i], hlist) {
printk("dump node: %pM\n", node->mac);
}
}
}
struct mac_node *fwx_find_mac_node(mac_config_t *config, const unsigned char *mac){
struct mac_node *node;
if (!config || !mac)
return NULL;
int hash = mac_hash(mac);
hlist_for_each_entry(node, &config->hash_table[hash], hlist) {
if (memcmp(node->mac, mac, ETH_ALEN) == 0) {
return node;
}
}
return NULL;
}
struct mac_node *fwx_flush_mac_list(mac_config_t *config){
int i;
struct mac_node *node;
struct hlist_node *n;
if (!config)
return NULL;
for (i = 0; i < MAC_HASH_SIZE; i++){
hlist_for_each_entry_safe(node, n, &config->hash_table[i], hlist) {
hlist_del(&node->hlist);
kfree(node);
}
}
return NULL;
}
+27
View File
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_MAC_H__
#define __FWX_MAC_H__
#define MAC_HASH_SIZE 128
#ifndef ETH_ALEN
#define ETH_ALEN 6
#endif
struct mac_node {
unsigned char mac[ETH_ALEN];
struct hlist_node hlist;
};
typedef struct mac_config{
struct hlist_head hash_table[MAC_HASH_SIZE];
}mac_config_t;
void fwx_mac_config_init(mac_config_t *config);
void fwx_add_mac_node(mac_config_t *config, const unsigned char *mac);
void fwx_dump_mac_node(mac_config_t *config);
struct mac_node *fwx_find_mac_node(mac_config_t *config, const unsigned char *mac);
int mac_str_to_bin(const char *mac_str, u8 *mac_bin);
struct mac_node *fwx_flush_mac_list(mac_config_t *config);
#endif
+545
View File
@@ -0,0 +1,545 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/etherdevice.h>
#include <linux/list.h>
#include "k_json.h"
#include "fwx.h"
#include "fwx_mac.h"
#include "fwx_mac_filter.h"
DEFINE_RWLOCK(mac_filter_lock);
#define mac_filter_read_lock() read_lock_bh(&mac_filter_lock);
#define mac_filter_read_unlock() read_unlock_bh(&mac_filter_lock);
#define mac_filter_write_lock() write_lock_bh(&mac_filter_lock);
#define mac_filter_write_unlock() write_unlock_bh(&mac_filter_lock);
static int g_mac_rule_count = 0;
static LIST_HEAD(mac_filter_rule_list);
int g_mac_filter_enable = 0;
static mac_config_t g_mac_filter_whitelist;
int fwx_mac_filter_init(void) {
mac_filter_write_lock();
INIT_LIST_HEAD(&mac_filter_rule_list);
g_mac_rule_count = 0;
fwx_mac_config_init(&g_mac_filter_whitelist);
mac_filter_write_unlock();
return 0;
}
void fwx_mac_filter_exit(void) {
mac_filter_rule_t *rule, *next;
mac_filter_write_lock();
list_for_each_entry_safe(rule, next, &mac_filter_rule_list, list) {
fwx_flush_mac_list(&rule->mac_list);
list_del(&rule->list);
kfree(rule);
}
g_mac_rule_count = 0;
fwx_flush_mac_list(&g_mac_filter_whitelist);
mac_filter_write_unlock();
}
mac_filter_rule_t *fwx_find_mac_filter_rule(int rule_id) {
mac_filter_rule_t *rule;
list_for_each_entry(rule, &mac_filter_rule_list, list) {
if (rule->rule_id == rule_id) {
return rule;
}
}
return NULL;
}
int fwx_add_mac_filter_rule(int rule_id, int mode) {
mac_filter_rule_t *rule;
if (g_mac_rule_count >= MAX_MAC_FILTER_RULE_NUM) {
return -1;
}
if (fwx_find_mac_filter_rule(rule_id)) {
return -1;
}
rule = kmalloc(sizeof(mac_filter_rule_t), GFP_ATOMIC);
if (!rule) {
printk("kmalloc mac filter rule failed\n");
return -1;
}
rule->rule_id = rule_id;
rule->mode = (mode == MAC_FILTER_MODE_SINGLE_USER) ? MAC_FILTER_MODE_SINGLE_USER : MAC_FILTER_MODE_ALL_USERS;
fwx_mac_config_init(&rule->mac_list);
INIT_LIST_HEAD(&rule->list);
mac_filter_write_lock();
list_add(&rule->list, &mac_filter_rule_list);
g_mac_rule_count++;
mac_filter_write_unlock();
return 0;
}
int fwx_del_mac_filter_rule(int rule_id) {
mac_filter_rule_t *rule;
mac_filter_write_lock();
rule = fwx_find_mac_filter_rule(rule_id);
if (rule) {
fwx_flush_mac_list(&rule->mac_list);
list_del(&rule->list);
kfree(rule);
g_mac_rule_count--;
mac_filter_write_unlock();
return 0;
}
mac_filter_write_unlock();
return -1;
}
int fwx_add_mac_to_rule(int rule_id, const unsigned char *mac) {
mac_filter_rule_t *rule;
mac_filter_write_lock();
rule = fwx_find_mac_filter_rule(rule_id);
if (rule) {
fwx_add_mac_node(&rule->mac_list, mac);
mac_filter_write_unlock();
return 0;
}
mac_filter_write_unlock();
return -1;
}
int fwx_del_mac_from_rule(int rule_id, const unsigned char *mac) {
mac_filter_rule_t *rule;
struct mac_node *node;
mac_filter_write_lock();
rule = fwx_find_mac_filter_rule(rule_id);
if (rule) {
node = fwx_find_mac_node(&rule->mac_list, mac);
if (node) {
hlist_del(&node->hlist);
kfree(node);
mac_filter_write_unlock();
return 0;
}
}
mac_filter_write_unlock();
return -1;
}
mac_filter_rule_t *fwx_match_mac_filter_rule(const unsigned char *mac) {
mac_filter_rule_t *rule;
struct mac_node *node;
mac_filter_read_lock();
list_for_each_entry(rule, &mac_filter_rule_list, list) {
if (rule->mode == MAC_FILTER_MODE_ALL_USERS) {
mac_filter_read_unlock();
return rule;
}
node = fwx_find_mac_node(&rule->mac_list, mac);
if (node) {
mac_filter_read_unlock();
return rule;
}
}
mac_filter_read_unlock();
return NULL;
}
int fwx_api_add_mac_filter_rule(cJSON *data_obj) {
cJSON *rule_id_obj;
cJSON *mode_obj;
int mode = MAC_FILTER_MODE_ALL_USERS;
if (!data_obj) {
return -1;
}
rule_id_obj = cJSON_GetObjectItem(data_obj, "rule_id");
mode_obj = cJSON_GetObjectItem(data_obj, "mode");
if (mode_obj) {
mode = mode_obj->valueint;
}
if (mode != MAC_FILTER_MODE_SINGLE_USER) {
mode = MAC_FILTER_MODE_ALL_USERS;
}
if (!rule_id_obj) {
printk("invalid rule format\n");
return -1;
}
if (fwx_add_mac_filter_rule(rule_id_obj->valueint, mode) < 0) {
return -1;
}
return 0;
}
int fwx_api_mod_mac_filter_rule(cJSON *data_obj) {
int i;
cJSON *rule_id_obj;
cJSON *mac_array;
cJSON *mac_obj;
cJSON *action_obj;
cJSON *mode_obj;
mac_filter_rule_t *rule = NULL;
if (!data_obj) {
return -1;
}
rule_id_obj = cJSON_GetObjectItem(data_obj, "rule_id");
action_obj = cJSON_GetObjectItem(data_obj, "mac_action");
if (!rule_id_obj) {
printk("rule_id not found\n");
return -1;
}
rule = fwx_find_mac_filter_rule(rule_id_obj->valueint);
if (!rule) {
printk("rule %d not found\n", rule_id_obj->valueint);
return -1;
}
mode_obj = cJSON_GetObjectItem(data_obj, "mode");
if (mode_obj) {
int mode = mode_obj->valueint;
if (mode != MAC_FILTER_MODE_SINGLE_USER) {
mode = MAC_FILTER_MODE_ALL_USERS;
}
mac_filter_write_lock();
rule->mode = mode;
mac_filter_write_unlock();
}
if (action_obj){
if (action_obj->valueint == 1 || action_obj->valueint == 2) {
mac_array = cJSON_GetObjectItem(data_obj, "mac_list");
if (mac_array) {
mac_filter_write_lock();
if (action_obj->valueint == 1){ // flush old
fwx_flush_mac_list(&rule->mac_list);
}
for (i = 0; i < cJSON_GetArraySize(mac_array); i++) {
mac_obj = cJSON_GetArrayItem(mac_array, i);
u8 mac_bin[ETH_ALEN] = {0};
if (mac_str_to_bin(mac_obj->valuestring, mac_bin)) {
fwx_add_mac_node(&rule->mac_list, mac_bin);
}
}
mac_filter_write_unlock();
}
}
else if (action_obj->valueint == 3) {
mac_obj = cJSON_GetObjectItem(data_obj, "mac");
if (mac_obj) {
mac_filter_write_lock();
u8 mac_bin[ETH_ALEN] = {0};
if (mac_str_to_bin(mac_obj->valuestring, mac_bin)) {
fwx_add_mac_node(&rule->mac_list, mac_bin);
}
mac_filter_write_unlock();
}
}
else{
mac_filter_write_lock();
fwx_flush_mac_list(&rule->mac_list);
mac_filter_write_unlock();
}
}
return 0;
}
int fwx_api_del_mac_filter_rule(cJSON *data_obj) {
cJSON *rule_id_obj;
cJSON *mac_obj;
if (!data_obj) {
return -1;
}
rule_id_obj = cJSON_GetObjectItem(data_obj, "rule_id");
if (!rule_id_obj) {
printk("rule_id not found\n");
return -1;
}
mac_obj = cJSON_GetObjectItem(data_obj, "mac");
if (mac_obj) {
u8 mac_bin[ETH_ALEN] = {0};
if (mac_str_to_bin(mac_obj->valuestring, mac_bin)) {
return fwx_del_mac_from_rule(rule_id_obj->valueint, mac_bin);
}
} else {
return fwx_del_mac_filter_rule(rule_id_obj->valueint);
}
return -1;
}
int fwx_api_dump_mac_filter_rule(cJSON *data_obj) {
mac_filter_rule_t *rule;
struct mac_node *node;
int mac_count = 0;
int i;
if (!data_obj) {
return -1;
}
cJSON *rule_id_obj = cJSON_GetObjectItem(data_obj, "rule_id");
mac_filter_read_lock();
printk("\n");
printk("+--------+------+----------------------------------------+\n");
printk("| RuleID | Mode | MAC List |\n");
printk("+--------+------+----------------------------------------+\n");
if (rule_id_obj) {
rule = fwx_find_mac_filter_rule(rule_id_obj->valueint);
if (rule) {
for (i = 0; i < MAC_HASH_SIZE; i++) {
hlist_for_each_entry(node, &rule->mac_list.hash_table[i], hlist) {
mac_count++;
}
}
printk(KERN_CONT "| %-6d | %-4d | ", rule->rule_id, rule->mode);
if (rule->mode == MAC_FILTER_MODE_ALL_USERS) {
printk(KERN_CONT "%-38s |\n", "(all users)");
} else if (mac_count == 0) {
printk(KERN_CONT "%-38s |\n", "(empty)");
} else {
int total_mac_count = mac_count;
mac_count = 0;
for (i = 0; i < MAC_HASH_SIZE; i++) {
hlist_for_each_entry(node, &rule->mac_list.hash_table[i], hlist) {
if (mac_count > 0) {
printk(KERN_CONT ", ");
}
printk(KERN_CONT "%pM", node->mac);
mac_count++;
if (mac_count >= 5) { // 最多显示5个MAC
if (mac_count < total_mac_count) {
printk(KERN_CONT "...");
}
break;
}
}
}
printk(KERN_CONT " |\n");
}
}
} else {
list_for_each_entry(rule, &mac_filter_rule_list, list) {
mac_count = 0;
for (i = 0; i < MAC_HASH_SIZE; i++) {
hlist_for_each_entry(node, &rule->mac_list.hash_table[i], hlist) {
mac_count++;
}
}
printk(KERN_CONT "| %-6d | %-4d | ", rule->rule_id, rule->mode);
if (rule->mode == MAC_FILTER_MODE_ALL_USERS) {
printk(KERN_CONT "%-38s |\n", "(all users)");
} else if (mac_count == 0) {
printk(KERN_CONT "%-38s |\n", "(empty)");
} else {
int total_mac_count = mac_count;
mac_count = 0;
for (i = 0; i < MAC_HASH_SIZE; i++) {
hlist_for_each_entry(node, &rule->mac_list.hash_table[i], hlist) {
if (mac_count > 0) {
printk(KERN_CONT ", ");
}
printk(KERN_CONT "%pM", node->mac);
mac_count++;
if (mac_count >= 5) { // 最多显示5个MAC
if (mac_count < total_mac_count) {
printk(KERN_CONT "...");
}
break;
}
}
}
printk(KERN_CONT " |\n");
}
}
}
printk("+--------+------+----------------------------------------+\n");
printk("\n");
printk("MAC Filter Whitelist:\n");
printk("+----------------------------------------+\n");
printk("| MAC Address |\n");
printk("+----------------------------------------+\n");
int total_whitelist_count = 0;
for (i = 0; i < MAC_HASH_SIZE; i++) {
hlist_for_each_entry(node, &g_mac_filter_whitelist.hash_table[i], hlist) {
total_whitelist_count++;
}
}
if (total_whitelist_count == 0) {
printk(KERN_CONT "| %-38s |\n", "(empty)");
} else {
int printed_count = 0;
int should_break = 0;
for (i = 0; i < MAC_HASH_SIZE && !should_break; i++) {
hlist_for_each_entry(node, &g_mac_filter_whitelist.hash_table[i], hlist) {
printk(KERN_CONT "| %-38pM |\n", node->mac);
printed_count++;
if (printed_count >= 10) { // 最多显示10个MAC
if (printed_count < total_whitelist_count) {
printk(KERN_CONT "| %-38s |\n", "...");
}
should_break = 1;
break;
}
}
}
}
printk("+----------------------------------------+\n");
printk("Total whitelist entries: %d\n", total_whitelist_count);
mac_filter_read_unlock();
return 0;
}
int fwx_api_flush_mac_filter_rule(cJSON *data_obj) {
mac_filter_rule_t *rule, *next;
mac_filter_write_lock();
list_for_each_entry_safe(rule, next, &mac_filter_rule_list, list) {
fwx_flush_mac_list(&rule->mac_list);
list_del(&rule->list);
kfree(rule);
}
g_mac_rule_count = 0;
mac_filter_write_unlock();
return 0;
}
int fwx_match_mac_filter_whitelist(const unsigned char *mac) {
struct mac_node *node;
int ret = 0;
mac_filter_read_lock();
node = fwx_find_mac_node(&g_mac_filter_whitelist, mac);
ret = (node != NULL);
mac_filter_read_unlock();
return ret;
}
int fwx_api_add_mac_filter_whitelist(cJSON *data_obj) {
cJSON *mac_array;
int i;
u8 mac_bin[ETH_ALEN];
if (!data_obj) {
return -1;
}
mac_array = cJSON_GetObjectItem(data_obj, "mac_list");
if (!mac_array) {
printk("mac_list not found\n");
return -1;
}
mac_filter_write_lock();
for (i = 0; i < cJSON_GetArraySize(mac_array); i++) {
cJSON *mac_obj = cJSON_GetArrayItem(mac_array, i);
if (mac_obj && mac_str_to_bin(mac_obj->valuestring, mac_bin)) {
fwx_add_mac_node(&g_mac_filter_whitelist, mac_bin);
}
}
mac_filter_write_unlock();
return 0;
}
int fwx_api_del_mac_filter_whitelist(cJSON *data_obj) {
cJSON *mac_obj;
u8 mac_bin[ETH_ALEN];
struct mac_node *node;
if (!data_obj) {
return -1;
}
mac_obj = cJSON_GetObjectItem(data_obj, "mac");
if (!mac_obj) {
printk("mac not found\n");
return -1;
}
if (!mac_str_to_bin(mac_obj->valuestring, mac_bin)) {
printk("invalid mac format\n");
return -1;
}
mac_filter_write_lock();
node = fwx_find_mac_node(&g_mac_filter_whitelist, mac_bin);
if (node) {
hlist_del(&node->hlist);
kfree(node);
mac_filter_write_unlock();
return 0;
}
mac_filter_write_unlock();
return -1;
}
int fwx_api_flush_mac_filter_whitelist(cJSON *data_obj) {
mac_filter_write_lock();
fwx_flush_mac_list(&g_mac_filter_whitelist);
mac_filter_write_unlock();
return 0;
}
+56
View File
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_MAC_FILTER_H__
#define __FWX_MAC_FILTER_H__
#include "k_json.h"
#include "fwx_mac.h"
#include <linux/list.h>
#define MAX_MAC_FILTER_RULE_NUM 64
#define MAC_FILTER_MODE_ALL_USERS 1
#define MAC_FILTER_MODE_SINGLE_USER 2
typedef struct mac_filter_rule {
int rule_id;
int mode;
mac_config_t mac_list;
struct list_head list;
} mac_filter_rule_t;
extern int g_mac_filter_enable;
int fwx_api_add_mac_filter_whitelist(cJSON *data_obj);
int fwx_api_del_mac_filter_whitelist(cJSON *data_obj);
int fwx_api_flush_mac_filter_whitelist(cJSON *data_obj);
int fwx_match_mac_filter_whitelist(const unsigned char *mac);
int fwx_mac_filter_init(void);
void fwx_mac_filter_exit(void);
int fwx_add_mac_filter_rule(int rule_id, int mode);
int fwx_del_mac_filter_rule(int rule_id);
mac_filter_rule_t *fwx_find_mac_filter_rule(int rule_id);
int fwx_add_mac_to_rule(int rule_id, const unsigned char *mac);
int fwx_del_mac_from_rule(int rule_id, const unsigned char *mac);
mac_filter_rule_t *fwx_match_mac_filter_rule(const unsigned char *mac);
int fwx_api_add_mac_filter_rule(cJSON *data_obj);
int fwx_api_del_mac_filter_rule(cJSON *data_obj);
int fwx_api_dump_mac_filter_rule(cJSON *data_obj);
int fwx_api_flush_mac_filter_rule(cJSON *data_obj);
int fwx_api_mod_mac_filter_rule(cJSON *data_obj);
#endif
+3501
View File
File diff suppressed because it is too large. Load diff
+39 -42
View File
@@ -1,3 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -5,7 +10,7 @@
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/version.h>
#include "af_utils.h"
#include "fwx_utils.h"
#define MAX_DUMP_STR_LEN 256
u_int32_t af_get_timestamp_sec(void)
@@ -22,15 +27,37 @@ u_int32_t af_get_timestamp_sec(void)
}
int mac_str_to_bin(const char *mac_str, u8 *mac_bin) {
if (!mac_str || !mac_bin)
return 0;
const char *p = mac_str;
int i = 0;
while (*p && i < 6) {
unsigned int byte;
char hex[3] = {0};
if (sscanf(p, "%2hhx", &byte) != 1)
return false;
mac_bin[i++] = (u8)byte;
while (*p && *p != ':')
p++;
if (*p == ':')
p++;
}
return i == 6;
}
int k_atoi(const char *str) {
int result = 0;
// Skip whitespace
while (*str == ' ' || *str == '\t') {
str++;
}
// Convert characters to integer
while (*str >= '0' && *str <= '9') {
result = result * 10 + (*str - '0');
str++;
@@ -83,7 +110,7 @@ void dump_str(char *name, unsigned char *p, int len)
strncpy(buf, p, len);
printk("[%s]\n", buf);
}
static int isprint_char(unsigned char c)
int isprint_char(unsigned char c)
{
if (c >= 0x20 && c <= 0x7e)
return 1;
@@ -187,11 +214,7 @@ static int k_vsscanf(const char *buf, const char *fmt, va_list args)
int num = 0;
u8 qualifier;
u8 base;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)
int field_width;
#else
s16 field_width;
#endif
bool is_sign;
while (*fmt && *str) {
if (isspace(*fmt)) {
@@ -251,13 +274,8 @@ static int k_vsscanf(const char *buf, const char *fmt, va_list args)
case 's':
{
char *s = (char *)va_arg(args, char *);
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)
if(field_width == -1)
field_width = INT_MAX;
#else
if (field_width == -1)
field_width = SHRT_MAX;
#endif
str = skip_spaces(str);
while (*str && (!isspace(*str) || ((unsigned char )*str == 0xA0) )&& field_width--)
@@ -279,14 +297,12 @@ static int k_vsscanf(const char *buf, const char *fmt, va_list args)
case 'X':
base = 16;
break;
case 'i':
base = 0;
fallthrough;
case 'd':
is_sign = 1;
fallthrough;
case 'u':
break;
case 'i':
base = 0;
case 'd':
is_sign = 1;
case 'u':
break;
case '%':
if (*str++ != '%')
return num;
@@ -383,22 +399,3 @@ int k_sscanf(const char *buf, const char *fmt, ...)
}
int mac_to_hex(u8 *mac, u8 *mac_hex)
{
u32 mac_tmp[6];
int ret = 0, i = 0;
ret = sscanf(mac, "%02x:%02x:%02x:%02x:%02x:%02x",
(unsigned int *)&mac_tmp[0],
(unsigned int *)&mac_tmp[1],
(unsigned int *)&mac_tmp[2],
(unsigned int *)&mac_tmp[3],
(unsigned int *)&mac_tmp[4],
(unsigned int *)&mac_tmp[5]);
if (6 != ret)
return -1;
for (i = 0; i < 6; i++)
{
mac_hex[i] = mac_tmp[i];
}
return 0;
}
+6 -2
View File
@@ -1,8 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef AF_UTILS_H
#define AF_UTILS_H
u_int32_t af_get_timestamp_sec(void);
char *k_trim(char *s);
int mac_str_to_bin(const char *mac_str, u8 *mac_bin);
int check_local_network_ip(unsigned int ip);
@@ -13,8 +19,6 @@ void dump_hex(char *name, unsigned char *p, int len);
int k_sscanf(const char *buf, const char *fmt, ...);
int k_atoi(const char *str);
void print_hex_ascii(const unsigned char *data, size_t size);
int hash_mac(unsigned char *mac);
int mac_to_hex(u8 *mac, u8 *mac_hex);
#endif
+31 -31
View File
@@ -20,8 +20,8 @@
THE SOFTWARE.
*/
// cJSON
// JSON parser in C.
#if 0
#include <string.h>
@@ -31,7 +31,7 @@
#include <float.h>
#endif
#include "cJSON.h"
#include "k_json.h"
#include <linux/module.h>
#include <linux/slab.h>
@@ -78,7 +78,7 @@ static char* cJSON_strdup(const char* str)
#if 0
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
if (!hooks) { /* Reset hooks */
if (!hooks) {
cJSON_malloc = malloc;
cJSON_realloc = realloc;
cJSON_free = free;
@@ -91,7 +91,7 @@ void cJSON_InitHooks(cJSON_Hooks* hooks)
}
#endif
// Internal constructor.
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
@@ -99,7 +99,7 @@ static cJSON *cJSON_New_Item(void)
return node;
}
// Delete a cJSON structure.
void cJSON_Delete(cJSON *c)
{
cJSON *next;
@@ -115,20 +115,20 @@ void cJSON_Delete(cJSON *c)
}
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num)
{
int n=0,sign=1;
if (*num=='-') sign=-1,num++; /* Has sign? */
if (*num=='0') num++; /* is zero */
if (*num>='1' && *num<='9') do n=(n*10)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
if (*num=='-') sign=-1,num++;
if (*num=='0') num++;
if (*num>='1' && *num<='9') do n=(n*10)+(*num++ -'0'); while (*num>='0' && *num<='9');
item->valueint=(int)n;
item->type=cJSON_Number;
return num;
}
/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item)
{
char *str;
@@ -139,7 +139,7 @@ static char *print_number(cJSON *item)
}
// Parse the input text into an unescaped cstring, and populate item.
static const char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
{
@@ -190,7 +190,7 @@ static const char *parse_string(cJSON *item,const char *str)
return ptr;
}
// Render the cstring provided to an escaped version that can be printed.
static char *print_string_ptr(const char *str)
{
const char *ptr;char *ptr2,*out;int len=0;
@@ -222,10 +222,10 @@ static char *print_string_ptr(const char *str)
*ptr2++='\"';*ptr2++=0;
return out;
}
// Invote print_string_ptr (which is useful) on an item.
static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
// Predeclare these prototypes.
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth);
static const char *parse_array(cJSON *item,const char *value);
@@ -233,23 +233,23 @@ static char *print_array(cJSON *item,int depth);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth);
// Utility to jump whitespace and cr/lf
static const char *skip(const char *in) {while (in && *in<=32) in++; return in;}
// Parse an object - create a new root, and populate.
cJSON *cJSON_Parse(const char *value)
{
cJSON *c=cJSON_New_Item();
if (!c) return 0; /* memory fail */
if (!c) return 0;
if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;}
return c;
}
// Render a cJSON item/entity/structure to text.
char *cJSON_Print(cJSON *item) {return print_value(item,0);}
// Parser core - when encountering text, process appropriately.
static const char *parse_value(cJSON *item,const char *value)
{
if (!value) return 0; // Fail on null.
@@ -264,7 +264,7 @@ static const char *parse_value(cJSON *item,const char *value)
return 0; // failure.
}
// Render a value to text.
static char *print_value(cJSON *item,int depth)
{
char *out=0;
@@ -281,7 +281,7 @@ static char *print_value(cJSON *item,int depth)
return out;
}
// Build an array from input text.
static const char *parse_array(cJSON *item,const char *value)
{
cJSON *child;
@@ -309,7 +309,7 @@ static const char *parse_array(cJSON *item,const char *value)
return 0; // malformed.
}
// Render an array to text
static char *print_array(cJSON *item,int depth)
{
char *out,*ptr,*ret;int len=5;
@@ -333,7 +333,7 @@ static char *print_array(cJSON *item,int depth)
return out;
}
// Build an object from the text.
static const char *parse_object(cJSON *item,const char *value)
{
cJSON *child;
@@ -368,7 +368,7 @@ static const char *parse_object(cJSON *item,const char *value)
return 0; // malformed.
}
// Render an object to text.
static char *print_object(cJSON *item,int depth)
{
char *out,*ptr,*ret,*str;int len=7,i;
@@ -491,23 +491,23 @@ void cJSON_Minify(char *json)
}
}
/* and null-terminate. */
*into = '\0';
}
// Get Array size/item / object item.
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && strcasecmp(c->string,string)) c=c->next; return c;}
// Utility for array list handling.
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
// Add item to array/object.
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
// Create basic types:
cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();item->type=cJSON_False;return item;}
@@ -516,6 +516,6 @@ cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();item
cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();item->type=cJSON_Object;return item;}
// Create Arrays:
cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+13 -14
View File
@@ -24,7 +24,7 @@
#define cJSON__h
#include <linux/slab.h>
// cJSON Types:
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
@@ -33,7 +33,7 @@
#define cJSON_Array 5
#define cJSON_Object 6
// The cJSON structure:
typedef struct cJSON {
struct cJSON *next,*prev; // next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem
struct cJSON *child; // An array or object item will have a child pointer pointing to a chain of the items in the array/object.
@@ -51,25 +51,25 @@ typedef struct cJSON_Hooks {
void (*free_fn)(void *ptr);
} cJSON_Hooks;
// Supply malloc, realloc and free functions to cJSON
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
// Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished.
extern cJSON *cJSON_Parse(const char *value);
// Render a cJSON entity to text for transfer/storage. Free the char* when finished.
extern char *cJSON_Print(cJSON *item);
// Delete a cJSON entity and all subentities.
extern void cJSON_Delete(cJSON *c);
// Returns the number of items in an array (or object).
extern int cJSON_GetArraySize(cJSON *array);
// Retrieve item number "item" from array "array". Returns NULL if unsuccessful.
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
// Get item "string" from object. Case insensitive.
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
// These calls create a cJSON item of the appropriate type.
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
@@ -78,11 +78,10 @@ extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
extern void cJSON_Minify(char *json);
// These utilities create an Array of count items.
extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
// Append item to the specified array/object.
extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
+1 -32
View File
@@ -3,7 +3,7 @@
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/slab.h>
//#include "regexp.h"
typedef enum{CHAR, DOT, BEGIN, END, STAR, PLUS, QUES, LIST, TYPENUM}TYPE;
@@ -18,8 +18,6 @@ typedef struct RE{
int match_longest = 0;
char *match_first = NULL;
int regexp_match(char *reg, char *text);
static void * getmem(size_t size)
{
@@ -272,32 +270,3 @@ out:
regexp_free(regexp);
return ret;
}
static __maybe_unused void TEST_reg_func(char *reg, char * str, int ret)
{
if (ret != regexp_match(reg, str)) {
if (reg)
printk("reg = %s,", reg);
else
printk("reg = null");
if (str)
printk("str = %s ", str);
else
printk("str= null");
printk("error, unit test.... failed, ret = %d\n",ret);
}
else {
if (reg && str)
printk("[unit test] %s %s......ok,ret = %d\n", reg, str, ret);
}
}
static __maybe_unused void TEST_regexp(void)
{
TEST_reg_func(".*baidu.com$", "www.baidu.com", 1);
TEST_reg_func("^sina.com", "www.sina.com.cn", 0);
TEST_reg_func("^sina.com", "sina.com.cn", 1);
TEST_reg_func(".*baidu.com$", "www.baidu.com223", 0);
}