mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-07-28 17:32:00 +08:00
303 lines
7.6 KiB
C
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();
|
|
}
|