small-packages/oaf/src/af_conntrack.c
2026-05-10 09:48:30 +08:00

303 lines
7.6 KiB
C

#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/jhash.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/sysctl.h>
#include <net/ip.h>
#include <linux/types.h>
#include <net/sock.h>
#include <linux/etherdevice.h>
#include <linux/cdev.h>
#include "af_conntrack.h"
#include "af_log.h"
struct hlist_head af_conn_table[AF_CONN_HASH_SIZE];
DEFINE_SPINLOCK(af_conn_lock);
static u32 af_conn_hash(u32 src_ip, u32 dst_ip,
u16 src_port, u16 dst_port,
u8 protocol)
{
return jhash_3words(src_ip, dst_ip,
((u32)protocol << 16) | src_port,
dst_port) % AF_CONN_HASH_SIZE;
}
void af_conn_cleanup(void)
{
int i;
spin_lock(&af_conn_lock);
af_conn_t *p = NULL;
struct hlist_node *n;
for (i = 0; i < AF_CONN_HASH_SIZE; i++)
{
hlist_for_each_entry_safe(p, n, &af_conn_table[i], node)
{
hlist_del(&p->node);
kfree(p);
}
}
spin_unlock(&af_conn_lock);
}
af_conn_t *af_conn_add(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 protocol)
{
u32 hash;
af_conn_t *conn;
hash = af_conn_hash(src_ip, dst_ip, src_port, dst_port, protocol);
conn = kmalloc(sizeof(af_conn_t), GFP_ATOMIC);
if (!conn) {
return NULL;
}
conn->src_ip = src_ip;
conn->dst_ip = dst_ip;
conn->src_port = src_port;
conn->dst_port = dst_port;
conn->protocol = protocol;
conn->total_pkts = 0;
conn->app_id = 0;
conn->client_hello = 0;
conn->drop = 0;
conn->state = AF_CONN_NEW;
conn->last_jiffies = jiffies;
hlist_add_head(&conn->node, &af_conn_table[hash]);
AF_LMT_INFO("add new conn ok...%pI4:%d->%pI4:%d %d\n",
&conn->src_ip, conn->src_port, &conn->dst_ip, conn->dst_port, conn->protocol);
return conn;
}
af_conn_t* af_conn_find(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 protocol)
{
u32 hash;
af_conn_t *conn;
hash = af_conn_hash(src_ip, dst_ip, src_port, dst_port, protocol);
hlist_for_each_entry(conn, &af_conn_table[hash], node)
{
if (conn->src_ip == src_ip && conn->dst_ip == dst_ip &&
conn->src_port == src_port && conn->dst_port == dst_port &&
conn->protocol == protocol) {
return conn;
}
}
return NULL;
}
af_conn_t* af_conn_find_and_add(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 protocol)
{
af_conn_t *conn;
conn = af_conn_find(src_ip, dst_ip, src_port, dst_port, protocol);
if (!conn)
{
conn = af_conn_add(src_ip, dst_ip, src_port, dst_port, protocol);
}
return conn;
}
void af_conn_update(af_conn_t *conn, u32 app_id, u8 drop)
{
spin_lock(&af_conn_lock);
conn->app_id = app_id;
conn->drop = drop;
conn->last_jiffies = jiffies;
spin_unlock(&af_conn_lock);
}
#define MAX_AF_CONN_CHECK_COUNT 5
void af_conn_clean_timeout(void)
{
int i;
af_conn_t *conn;
struct hlist_node *n;
unsigned long timeout = AF_CONN_TIMEOUT * HZ;
static int last_bucket = 0;
int count = 0;
spin_lock(&af_conn_lock);
for (i = last_bucket; i < AF_CONN_HASH_SIZE; i++)
{
hlist_for_each_entry_safe(conn, n, &af_conn_table[i], node)
{
if (time_after(jiffies, conn->last_jiffies + timeout)) {
AF_LMT_INFO("clean timeout conn ok...%pI4:%d->%pI4:%d %d\n",
&conn->src_ip, conn->src_port, &conn->dst_ip, conn->dst_port, conn->protocol);
hlist_del(&(conn->node));
kfree(conn);
}
}
last_bucket = i;
count++;
if (count > MAX_AF_CONN_CHECK_COUNT)
break;
}
if (last_bucket == AF_CONN_HASH_SIZE - 1)
{
last_bucket = 0;
}
spin_unlock(&af_conn_lock);
}
struct af_conn_iter_state
{
unsigned int bucket;
};
static void *af_conn_seq_start(struct seq_file *s, loff_t *pos)
{
if (*pos == 0)
{
return SEQ_START_TOKEN;
}
struct af_conn_iter_state *st = s->private;
while (st->bucket < AF_CONN_HASH_SIZE){
if (!hlist_empty(&(af_conn_table[st->bucket])))
{
return &af_conn_table[st->bucket++].first->next;
}
st->bucket++;
}
return NULL;
}
static void *af_conn_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
if (v == SEQ_START_TOKEN){
return NULL;
}
struct hlist_node *node = (struct hlist_node *)v;
node = node->next;
if (node != NULL)
return node;
return NULL;
}
static void af_conn_seq_stop(struct seq_file *s, void *v)
{
}
static int af_conn_seq_show(struct seq_file *s, void *v)
{
unsigned char src_ip_str[32] = {0};
unsigned char dst_ip_str[32] = {0};
static int index = 0;
af_conn_t *node = (af_conn_t *)v;
if (v == SEQ_START_TOKEN)
{
index = 0;
seq_printf(s, "%-4s %-20s %-20s %-12s %-12s %-12s %-12s %-12s %-12s %-12s\n",
"Id", "src_ip", "dst_ip", "src_port", "dst_port", "protocol", "app_id", "drop", "inactive", "total_pkts");
return 0;
}
index++;
sprintf(src_ip_str, "%pI4", &node->src_ip);
sprintf(dst_ip_str, "%pI4", &node->dst_ip);
u_int32_t inactive_time = jiffies - node->last_jiffies;
seq_printf(s, "%-4d %-20s %-20s %-12d %-12d %-12d %-12d %-12d %-12d %-12d\n", index, src_ip_str, dst_ip_str,
node->src_port, node->dst_port, node->protocol, node->app_id, node->drop, inactive_time, node->total_pkts);
return 0;
}
static const struct seq_operations af_conn_seq_ops = {
.start = af_conn_seq_start,
.next = af_conn_seq_next,
.stop = af_conn_seq_stop,
.show = af_conn_seq_show
};
static int af_conn_open(struct inode *inode, struct file *file)
{
struct seq_file *seq;
struct af_conn_iter_state *iter;
int err;
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
if (!iter)
return -ENOMEM;
err = seq_open(file, &af_conn_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_conn_fops = {
.owner = THIS_MODULE,
.open = af_conn_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
#else
static const struct proc_ops af_conn_fops = {
.proc_flags = PROC_ENTRY_PERMANENT,
.proc_read = seq_read,
.proc_open = af_conn_open,
.proc_lseek = seq_lseek,
.proc_release = seq_release_private,
};
#endif
#define AF_CONN_PROC_STR "af_conn"
int af_conn_init_procfs(void)
{
struct proc_dir_entry *pde;
struct net *net = &init_net;
pde = proc_create(AF_CONN_PROC_STR, 0644, net->proc_net, &af_conn_fops);
if (!pde)
{
printk("af_conn seq file created error\n");
return -1;
}
return 0;
}
void af_conn_remove_procfs(void)
{
struct net *net = &init_net;
remove_proc_entry(AF_CONN_PROC_STR, net->proc_net);
}
int af_conn_init(void)
{
int i;
for (i = 0; i < AF_CONN_HASH_SIZE; i++)
{
INIT_HLIST_HEAD(&af_conn_table[i]);
}
af_conn_init_procfs();
return 0;
}
void af_conn_exit(void){
af_conn_remove_procfs();
af_conn_cleanup();
}