mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-10-30 15:05:13 +08:00
update 2026-08-26 20:40:17
This commit is contained in:
1 parent
d64e131165
commit
1bfa02b7c5
397 files changed
+52332
-16577
No files matched your search
+3
-1
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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__
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large.
Load diff
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user