增加上网记录统计功能,增加客户端统计模块

This commit is contained in:
destan19@126.com 2020-01-14 11:05:49 +08:00
parent 7d76a613ba
commit 0055281ca0
17 changed files with 1046 additions and 37 deletions

View File

@ -9,7 +9,7 @@ LUCI_TITLE:=Open App Filter Module
LUCI_PKGARCH:=all
LUCI_DEPENDS:=+kmod-oaf +appfilter
PKG_NAME:=luci-app-oaf
PKG_VERSION:=1.0
PKG_VERSION:=3.0
PKG_RELEASE:=1
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -9,4 +9,88 @@ function index()
page = entry({"admin", "network", "appfilter"}, cbi("appfilter/appfilter"), _("appfilter"))
page.dependent = true
page = entry({"admin", "network", "user_status"}, call("user_status"), nil)
page.leaf = true
end
function get_hostname_by_mac(dst_mac)
leasefile="/tmp/dhcp.leases"
local fd = io.open(leasefile, "r")
if not fd then return end
while true do
local ln = fd:read("*l")
if not ln then
break
end
local ts, mac, ip, name, duid = ln:match("^(%d+) (%S+) (%S+) (%S+) (%S+)")
print(ln)
if dst_mac == mac then
print("match mac", mac, "hostname=", name);
fd:close()
return name
end
end
fd:close()
return ""
end
function get_app_name_by_id(appid)
local class_fd = io.popen("find /etc/appfilter/ -type f -name *.class |xargs cat |grep "..appid.."|awk '{print $2}'")
if class_fd then
local name = class_fd:read("*l")
class_fd:close()
return name
end
return ""
end
function cmp_func(a,b)
return a.latest_time > b.latest_time
end
function user_status()
local s = require "luci.tools.status"
local json = require "luci.jsonc"
luci.http.prepare_content("application/json")
tb={}
obj={};
obj.hostname="derry"
obj.ip="192.168.10.199"
obj.mac="192.168.10.199"
tb[#tb+1]=obj
--local fs=require "nixio.fs"
--local ok, status_data = pcall(json.parse, fs.readfile("/proc/net/af_client"))
--luci.http.write_json(tb);
local fd = io.open("/proc/net/af_client","r")
status_buf=fd:read('*a')
fd:close()
user_array=json.parse(status_buf)
local history={}
for i, v in pairs(user_array) do
visit_array=user_array[i].visit_info
for j,s in pairs(visit_array) do
print(user_array[i].mac, user_array[i].ip,visit_array[j].appid, visit_array[j].latest_time)
history[#history+1]={
mac=user_array[i].mac,
ip=user_array[i].ip,
hostname=get_hostname_by_mac(user_array[i].mac),
appid=visit_array[j].appid,
appname=get_app_name_by_id(visit_array[j].appid),
total_num=visit_array[j].total_num,
drop_num=visit_array[j].drop_num,
latest_action=visit_array[j].latest_action,
latest_time=os.date("%Y/%m/%d %H:%M:%S", visit_array[j].latest_time)
}
end
end
table.sort(history, cmp_func)
--luci.http.write(history);
luci.http.write_json(history);
end

View File

@ -81,6 +81,7 @@ if class_fd then
end
class_fd:close()
end
m:section(SimpleSection).template = "admin_network/user_status"
return m

View File

@ -0,0 +1,59 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(5, '<%=url('admin/network/user_status')%>', null,
function(x, st)
{
var tb = document.getElementById('user_status_table');
if (st && st && tb)
{
var rows = [];
for (var i = 0; i < st.length; i++)
{
if(st[i].hostname == ""){
st[i].hostname="unknown"
}
var action_status=""
if(st[i].latest_action == 1)
action_status="已过滤"
else
action_status="未过滤"
<!--st[i].latest_time=st[i].latest_time+"<br>"+st[i].latest_time-->
rows.push([
st[i].hostname,
st[i].mac,
st[i].ip,
st[i].appname,
st[i].drop_num.toString(),
st[i].total_num.toString(),
st[i].latest_time,
action_status
]);
}
cbi_update_table(tb, rows, '<em><%:no data%></em>');
}
}
);
//]]></script>
<div class="cbi-section">
<h3><%:访问记录%></h3>
<div class="table" id="user_status_table">
<div class="tr table-titles">
<div class="th"><%:Hostname%></div>
<div class="th"><%:mac地址%></div>
<div class="th"><%:ip地址%></div>
<div class="th"><%:应用名称%></div>
<div class="th"><%:丢包次数%></div>
<div class="th"><%:总访问次数%></div>
<div class="th"><%:最后访问时间%></div>
<div class="th"><%:过滤状态%></div>
</div>
<div class="tr placeholder">
<div class="td"><em><%:Collecting data...%></em></div>
</div>
</div>
</div>

View File

@ -1,4 +1,7 @@
msgid "website"
msgstr "常用网站"
msgid "appfilter"
msgstr "应用过滤"
@ -39,4 +42,7 @@ msgid "App Filter Rules"
msgstr "应用过滤规则"
msgid "Enable App Filter"
msgstr "开启应用过滤"
msgstr "开启应用过滤"

View File

@ -3,11 +3,13 @@ include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=oaf
PKG_RELEASE:=3
PKG_VERSION:=3.0
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
PKG_AUTOLOAD:=oaf
RSTRIP:=:
define KernelPackage/oaf
SECTION:=Derry Apps

View File

@ -1,2 +1,2 @@
oaf-objs := app_filter.o af_utils.o regexp.o cJSON.o app_filter_config.o af_log.o
oaf-objs := app_filter.o af_utils.o regexp.o cJSON.o app_filter_config.o af_log.o af_client.o af_client_fs.o
obj-m += oaf.o

277
oaf/src/af_client.c Executable file
View File

@ -0,0 +1,277 @@
/*
Author:Derry
Date: 2019/11/12
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h>
#include <net/tcp.h>
#include <linux/netfilter.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_acct.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/device.h>
#include <linux/list.h>
#include "af_client.h"
#include "af_client_fs.h"
#include "af_log.h"
DEFINE_RWLOCK(af_client_lock);
u32 total_client = 0;
struct list_head af_client_list_table[MAX_AF_CLIENT_HASH_SIZE];
static void
nf_client_list_init(void)
{
int i;
AF_CLIENT_LOCK_W();
for(i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i ++){
INIT_LIST_HEAD(&af_client_list_table[i]);
}
AF_CLIENT_UNLOCK_W();
AF_INFO("client list init......ok\n");
}
static void
nf_client_list_clear(void)
{
int i;
af_client_info_t * p = NULL;
char mac_str[32] = {0};
AF_DEBUG("clean list\n");
AF_CLIENT_LOCK_W();
for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE;i++){
while(!list_empty(&af_client_list_table[i])){
p = list_first_entry(&af_client_list_table[i], af_client_info_t, hlist);
memset(mac_str, 0x0, sizeof(mac_str));
sprintf(mac_str, MAC_FMT, MAC_ARRAY(p->mac));
AF_DEBUG("clean mac:%s\n", mac_str);
list_del(&(p->hlist));
kfree(p);
}
}
AF_CLIENT_UNLOCK_W();
}
int get_mac_hash_code(unsigned char *mac)
{
if (!mac)
return 0;
else
return mac[5] & (MAX_AF_CLIENT_HASH_SIZE - 1);
}
af_client_info_t * find_af_client(unsigned char *mac)
{
af_client_info_t *node;
unsigned int index;
index = get_mac_hash_code(mac);
list_for_each_entry(node, &af_client_list_table[index], hlist){
if (0 == memcmp(node->mac, mac, 6)){
node->update_jiffies = jiffies;
return node;
}
}
return NULL;
}
af_client_info_t *find_af_client_by_ip(unsigned int ip)
{
af_client_info_t *node;
int i;
for(i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++){
list_for_each_entry(node, &af_client_list_table[i], hlist){
if (node->ip == ip){
AF_LMT_DEBUG("match node->ip=%pI4, ip=%pI4\n", &node->ip, &ip);
return node;
}
}
}
return NULL;
}
static af_client_info_t *
nf_client_add(unsigned char *mac)
{
af_client_info_t *node;
int index = 0;
node = (af_client_info_t *)kmalloc(sizeof(af_client_info_t), GFP_ATOMIC);
if (node == NULL) {
AF_ERROR("kmalloc failed\n");
return NULL;
}
memset(node, 0, sizeof(af_client_info_t));
memcpy(node->mac, mac, MAC_ADDR_LEN);
node->create_jiffies = jiffies;
node->update_jiffies = jiffies;
index = get_mac_hash_code(mac);
AF_LMT_INFO("new client mac="MAC_FMT"\n", MAC_ARRAY(node->mac));
total_client++;
list_add(&(node->hlist), &af_client_list_table[index]);
return node;
}
void check_client_expire(void)
{
af_client_info_t *node;
int i;
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) {
AF_DEBUG("mac:"MAC_FMT" update:%d cur:%d, interval:%d\n", MAC_ARRAY(node->mac),
node->update_jiffies, HZ, (jiffies - node->update_jiffies) / HZ);
if (jiffies > (node->update_jiffies + MAX_CLIENT_ACTIVE_TIME * HZ)) {
AF_INFO("del client:"MAC_FMT"\n", MAC_ARRAY(node->mac));
list_del(&(node->hlist));
kfree(node);
AF_CLIENT_UNLOCK_W();
return;
}
}
}
AF_CLIENT_UNLOCK_W();
}
static inline int get_packet_dir(struct net_device *in)
{
if (0 == strncmp(in->name, "br", 2)){
return PKT_DIR_UP;
}
else{
return PKT_DIR_DOWN;
}
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
static u_int32_t nfclient_hook(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state) {
#else
static u_int32_t nfclient_hook(unsigned int hook,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
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;
// 4.10-->4.11 nfct-->_nfct
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
struct nf_conn *ct = (struct nf_conn *)skb->_nfct;
#else
struct nf_conn *ct = (struct nf_conn *)skb->nfct;
#endif
if (ct == NULL) {
return NF_ACCEPT;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
if(!skb->dev)
return NF_ACCEPT;
pkt_dir = get_packet_dir(skb->dev);
#else
if (!in){
AF_ERROR("in is NULL\n");
return NF_ACCEPT;
}
pkt_dir = get_packet_dir(in);
#endif
if(PKT_DIR_UP != pkt_dir)
return NF_ACCEPT;
ethhdr = eth_hdr(skb);
if (ethhdr) {
memcpy(smac, ethhdr->h_source, ETH_ALEN);
} else {
memcpy(smac, &skb->cb[40], ETH_ALEN);
}
struct iphdr *iph = NULL;
iph = ip_hdr(skb);
if (!iph) {
return NF_ACCEPT;
}
AF_CLIENT_LOCK_W();
nfc = find_af_client(smac);
if (!nfc){
AF_DEBUG("from dev:%s [%s] %pI4--->%pI4", skb->dev, (iph->protocol == IPPROTO_TCP ? "TCP" : "UDP"),
&iph->saddr, &iph->daddr);
nfc = nf_client_add(smac);
}
if(nfc && nfc->ip != iph->saddr){
AF_DEBUG("update node "MAC_FMT" ip %pI4--->%pI4\n", MAC_ARRAY(nfc->mac), &nfc->ip, &iph->saddr);
nfc->ip = iph->saddr;
}
AF_CLIENT_UNLOCK_W();
return NF_ACCEPT;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
static struct nf_hook_ops af_client_ops[] = {
{
.hook = nfclient_hook,
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FIRST + 1,
},
};
#else
static struct nf_hook_ops af_client_ops[] = {
{
.hook = nfclient_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FIRST + 1,
},
};
#endif
int af_client_init(void)
{
nf_client_list_init();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
nf_register_net_hooks(&init_net, af_client_ops, ARRAY_SIZE(af_client_ops));
#else
nf_register_hooks(af_client_ops, ARRAY_SIZE(af_client_ops));
#endif
AF_INFO("init app afclient ........ok\n");
return 0;
}
void af_client_exit(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
nf_unregister_net_hooks(&init_net, af_client_ops, ARRAY_SIZE(af_client_ops));
#else
nf_unregister_hooks(af_client_ops, ARRAY_SIZE(af_client_ops));
#endif
nf_client_list_clear();
return ;
}

65
oaf/src/af_client.h Executable file
View File

@ -0,0 +1,65 @@
#ifndef __AF_CLIENT_H__
#define __AF_CLIENT_H__
extern rwlock_t af_client_lock;
extern u32 nfc_debug_level;
#define MAX_AF_CLIENT_HASH_SIZE 64
#define MAC_ADDR_LEN 6
#define NF_CLIENT_TIMER_EXPIRE 1
#define MAX_CLIENT_ACTIVE_TIME 90
#define AF_CLIENT_LOCK_R() read_lock_bh(&af_client_lock);
#define AF_CLIENT_UNLOCK_R() read_unlock_bh(&af_client_lock);
#define AF_CLIENT_LOCK_W() write_lock_bh(&af_client_lock);
#define AF_CLIENT_UNLOCK_W() write_unlock_bh(&af_client_lock);
#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"
enum NFC_PKT_DIR{
PKT_DIR_DOWN,
PKT_DIR_UP
};
#define MAX_VISIT_HISTORY_TIME 24
#define MAX_RECORD_APP_NUM 32
typedef struct app_visit_info{
unsigned int app_id;
unsigned int total_num;
unsigned int drop_num;
unsigned long latest_time;
unsigned int latest_action;
unsigned long history_time[MAX_VISIT_HISTORY_TIME];
unsigned int action[MAX_VISIT_HISTORY_TIME];
}app_visit_info_t;
typedef struct af_client_info {
struct list_head hlist;
unsigned char mac[MAC_ADDR_LEN];
unsigned int ip;
unsigned long create_jiffies;
unsigned long update_jiffies;
unsigned int visit_app_num;
app_visit_info_t visit_info[MAX_RECORD_APP_NUM];
}af_client_info_t;
#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"
int af_client_init(void);
void af_client_exit(void);
af_client_info_t * find_af_client_by_ip(unsigned int ip);
#endif

222
oaf/src/af_client_fs.c Executable file
View File

@ -0,0 +1,222 @@
#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 "cJSON.h"
#include "af_log.h"
#include "af_client.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)
{
seq_printf(s, "%s", "]");
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};
static int index = 0;
int i;
int j;
if (v == SEQ_START_TOKEN) {
index = 0;
seq_printf(s, "%s", "[");
return 0;
}
if(index > 0)
seq_printf(s, "%s", ",");
index++;
af_client_info_t *node = (af_client_info_t *)v;
cJSON *root_obj = cJSON_CreateObject();
if(!root_obj){
AF_ERROR("create json obj failed");
return 0;
}
sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac));
sprintf(ip_str, "%pI4", &node->ip);
cJSON_AddStringToObject(root_obj, "mac", mac_str);
cJSON_AddStringToObject(root_obj, "ip", ip_str);
cJSON_AddNumberToObject(root_obj, "app_num", node->visit_app_num);
cJSON *visit_info_array = cJSON_CreateArray();
for(i = 0; i < MAX_RECORD_APP_NUM; i++){
if(node->visit_info[i].app_id == 0)
break;
cJSON *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);
cJSON_AddNumberToObject(visit_obj, "latest_time", node->visit_info[i].latest_time);
cJSON_AddNumberToObject(visit_obj, "total_num", node->visit_info[i].total_num);
cJSON_AddNumberToObject(visit_obj, "drop_num", node->visit_info[i].drop_num);
cJSON *history_array = cJSON_CreateArray();
for(j = 0; j < MAX_VISIT_HISTORY_TIME; j++){
if(node->visit_info[i].history_time[j] <= 0)
continue;
cJSON *history_obj = cJSON_CreateObject();
cJSON_AddNumberToObject(visit_obj, "action", node->visit_info[i].history_time[j]);
cJSON_AddNumberToObject(visit_obj, "time", node->visit_info[i].action[j]);
cJSON_AddItemToArray(history_array, history_obj);
}
cJSON_AddItemToObject(visit_obj, "history_info", history_array);
cJSON_AddItemToArray(visit_info_array, visit_obj);
}
cJSON_AddItemToObject(root_obj, "visit_info", visit_info_array);
char *out = cJSON_Print(root_obj);
if(!out)
return 0;
cJSON_Minify(out);
seq_printf(s, "%s", out);
kfree(out);
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;
}
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,
};
#define AF_CLIENT_PROC_STR "af_client"
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;
}
return 0;
}
void finit_af_client_procfs(void)
{
struct net *net = &init_net;
remove_proc_entry(AF_CLIENT_PROC_STR, net->proc_net);
}

7
oaf/src/af_client_fs.h Executable file
View File

@ -0,0 +1,7 @@
#ifndef __AF_CLIENT_FS_H__
#define __AF_CLIENT_FS_H__
int init_af_client_procfs(void);
void finit_af_client_procfs(void);
#endif

View File

@ -7,7 +7,6 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h>
#include <net/tcp.h>
#include <linux/netfilter.h>
#include <net/netfilter/nf_conntrack.h>
@ -23,10 +22,13 @@
#include "app_filter.h"
#include "af_utils.h"
#include "af_log.h"
#include "af_client.h"
#include "af_client_fs.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("destan19@126.com");
MODULE_DESCRIPTION("app filter module");
MODULE_VERSION("1.0.1");
MODULE_VERSION("3.0.1");
struct list_head af_feature_head = LIST_HEAD_INIT(af_feature_head);
DEFINE_RWLOCK(af_feature_lock);
@ -148,7 +150,7 @@ int add_app_feature(int appid, char *name, char *feature)
char dict[128] = {0};
int proto = IPPROTO_TCP;
if (!name || !feature) {
printk("error, name or feature is null\n");
AF_ERROR("error, name or feature is null\n");
return -1;
}
// tcp;8000;www.sina.com;0:get_name;00:0a-01:11
@ -183,8 +185,8 @@ int add_app_feature(int appid, char *name, char *feature)
param_num ++;
begin = p + 1;
}
if (AF_DICT_PARAM_INDEX != param_num) {
printk("invalid feature:%s\n", feature);
if (AF_DICT_PARAM_INDEX != param_num && strlen(feature) > MIN_FEATURE_STR_LEN) {
AF_ERROR("22 invalid feature:%s\n", feature);
return -1;
}
strncpy(dict, begin, p - begin);
@ -276,18 +278,17 @@ void load_feature_buf_from_file(char **config_buf)
printk("open feature file failed\n");
return -1;
}
//inode = fp->f_dentry->d_inode;
//inode = fp->f_path.dentry->d_inode;
inode = fp->f_inode;
size = inode->i_size;
printk("file size: %d\n", size);
AF_INFO("feature file size: %d\n", size);
if (size == 0) {
printk("warning,file size = %d\n", size);
AF_WARN("warning, file size = %d\n", size);
return;
}
*config_buf = (char *) kzalloc( sizeof(char) * size, GFP_KERNEL);
if(NULL == *config_buf ) {
printk("alloc buf fail\n");
AF_ERROR("alloc buf fail\n");
filp_close(fp, NULL);
return -1;
}
@ -299,7 +300,6 @@ void load_feature_buf_from_file(char **config_buf)
#else
vfs_read(fp, *config_buf, size, &(fp->f_pos));
#endif
//fp->f_op->read(fp, *config_buf, size, &(fp->f_pos));
set_fs(fs);
filp_close(fp, NULL);
return size;
@ -307,11 +307,11 @@ void load_feature_buf_from_file(char **config_buf)
void load_feature_config(void)
{
printk("begin load feature config.....\n");
AF_INFO("begin load feature config.....\n");
char *feature_buf = NULL;
load_feature_buf_from_file(&feature_buf);
if (!feature_buf) {
printk("error, feature buf is null\n");
AF_ERROR("error, feature buf is null\n");
return;
}
@ -375,7 +375,7 @@ int parse_flow_base(struct sk_buff *skb, flow_info_t *flow)
return -1;
}
iph = ip_hdr(skb);
if ( !iph ) {
if (!iph) {
return -1;
}
flow->ct = ct;
@ -389,14 +389,14 @@ int parse_flow_base(struct sk_buff *skb, flow_info_t *flow)
flow->l4_len = ntohs(iph->tot_len) - iph->ihl * 4 - tcph->doff * 4;
flow->dport = htons(tcph->dest);
flow->sport = htons(tcph->source);
break;
return 0;
case IPPROTO_UDP:
udph = (struct udphdr *)(iph + 1);
flow->l4_data = skb->data + iph->ihl * 4 + 8;
flow->l4_len = ntohs(udph->len) - 8;
flow->dport = htons(udph->dest);
flow->sport = htons(udph->source);
break;
return 0;
case IPPROTO_ICMP:
break;
default:
@ -688,10 +688,12 @@ int app_filter_match(flow_info_t *flow)
{
flow->app_id = node->app_id;
if (af_get_app_status(node->app_id)){
flow->drop = AF_TRUE;
feature_list_read_unlock();
return AF_TRUE;
}
else {
flow->drop = AF_FALSE;
feature_list_read_unlock();
return AF_FALSE;
}
@ -705,6 +707,89 @@ int app_filter_match(flow_info_t *flow)
}
#define APP_FILTER_DROP_BITS 0xf0000000
u_int32_t af_get_timestamp_sec(void)
{
struct timespec ts;
ts = current_kernel_time();
return ts.tv_sec;
}
int __af_update_client_app_info(flow_info_t *flow, af_client_info_t *node)
{
int i;
int index = -1;
if(!node)
return -1;
if(!flow)
return -1;
AF_INFO("%s %d visit_app_num = %d\n", __func__, __LINE__, node->visit_app_num);
int found = 0;
for(i = 0; i < MAX_RECORD_APP_NUM; i++){
if(node->visit_info[i].app_id == flow->app_id){
index = i;
found = 1;
break;
}
if(node->visit_info[i].app_id == 0)
break;
}
if(!found){
index = 0;
//超过最大个数,查询最老的
for(i = 0; i < MAX_RECORD_APP_NUM; i++){
if(node->visit_info[i].latest_time == 0){
index = i;
break;
}
if(node->visit_info[i].latest_time < node->visit_info[index].latest_time){
// 清除之前的数据
node->visit_info[i].total_num = 0;
node->visit_info[i].drop_num = 0;
index = i;
}
}
}
if(index < 0 || index >= MAX_RECORD_APP_NUM){
AF_ERROR("invalid index:%d\n\n", index);
return 0;
}
node->visit_info[index].total_num++;
if(flow->drop)
node->visit_info[index].drop_num++;
node->visit_info[index].app_id = flow->app_id;
node->visit_info[index].latest_time = af_get_timestamp_sec();
AF_DEBUG("update time = %u\n", node->visit_info[index].latest_time);
node->visit_info[index].latest_action = flow->drop;
AF_INFO("[%d] %pI4 visit %d, time=%d action=%s, %d/%d\n", index, &node->ip, flow->app_id,
node->visit_info[index].latest_time, node->visit_info[index].latest_action ? "Drop" : "Accept",
node->visit_info[index].drop_num, node->visit_info[index].total_num);
// todo: history
return 0;
}
void af_update_client_app_info(flow_info_t *flow)
{
int i;
int index = 0;
af_client_info_t *node = NULL;
if(!flow)
return;
if(flow->app_id <= 0)
return;
AF_CLIENT_LOCK_W();
node = find_af_client_by_ip(flow->src);
if(node){
__af_update_client_app_info(flow, node);
}
AF_CLIENT_UNLOCK_W();
}
/* 在netfilter框架注册的钩子 */
@ -728,18 +813,17 @@ static u_int32_t app_filter_hook(unsigned int hook,
#else
struct nf_conn *ct = (struct nf_conn *)skb->nfct;
#endif
if (ct == NULL) {
//AF_ERROR("ct is null\n");
if(ct == NULL) {
return NF_ACCEPT;
}
if (!nf_ct_is_confirmed(ct)){
if(!nf_ct_is_confirmed(ct)){
return NF_ACCEPT;
}
#if defined(CONFIG_NF_CONNTRACK_MARK)
if (ct->mark != 0)
if (APP_FILTER_DROP_BITS == (ct->mark & APP_FILTER_DROP_BITS)){
if(ct->mark != 0)
if(APP_FILTER_DROP_BITS == (ct->mark & APP_FILTER_DROP_BITS)){
return NF_DROP;
}
#endif
@ -748,7 +832,7 @@ static u_int32_t app_filter_hook(unsigned int hook,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)
struct nf_conn_acct *acct;
acct = nf_conn_acct_find(ct);
if (!acct)
if(!acct)
return NF_ACCEPT;
total_packets = (unsigned long long)atomic64_read(&acct->counter[IP_CT_DIR_ORIGINAL].packets)
+ (unsigned long long)atomic64_read(&acct->counter[IP_CT_DIR_REPLY].packets);
@ -764,22 +848,25 @@ static u_int32_t app_filter_hook(unsigned int hook,
+ (unsigned long long)atomic64_read(&counter[IP_CT_DIR_REPLY].packets);
#endif
if (total_packets > MAX_PARSE_PKT_NUM){
if(total_packets > MAX_PARSE_PKT_NUM){
return NF_ACCEPT;
}
memset((char *)&flow, 0x0, sizeof(flow_info_t));
parse_flow_base(skb, &flow);
if(parse_flow_base(skb, &flow) < 0)
return NF_ACCEPT;
parse_http_proto(&flow);
parse_https_proto(&flow);
if (TEST_MODE())
dump_flow_info(&flow);
app_filter_match(&flow);
af_update_client_app_info(&flow);
if (app_filter_match(&flow)){
if(flow.drop){
#if defined(CONFIG_NF_CONNTRACK_MARK)
ct->mark |= APP_FILTER_DROP_BITS;
#endif
AF_INFO("##########drop appid = %d#############\n\n\n", flow.app_id);
AF_LMT_INFO("##drop appid = %d\n\n\n", flow.app_id);
return NF_DROP;
}
return NF_ACCEPT;
@ -826,26 +913,26 @@ void TEST_cJSON(void)
kfree(out);
}
/*
*/
static int __init app_filter_init(void)
{
AF_INFO("appfilter version:"AF_VERSION"\n");
AF_DEBUG("app filter module init\n");
af_log_init();
//TEST_regexp();
af_register_dev();
af_init_app_status();
load_feature_config();
init_af_client_procfs();
// show_feature_list();
// TEST_cJSON();
af_client_init();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
nf_register_net_hooks(&init_net, app_filter_ops, ARRAY_SIZE(app_filter_ops));
#else
nf_register_hooks(app_filter_ops, ARRAY_SIZE(app_filter_ops));
#endif
printk("init app filter ........ok\n");
AF_INFO("init app filter ........ok\n");
return 0;
}
@ -854,7 +941,7 @@ static int __init app_filter_init(void)
*/
static void app_filter_fini(void)
{
AF_DEBUG("app filter module exit\n");
AF_INFO("app filter module exit\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
nf_unregister_net_hooks(&init_net, app_filter_ops, ARRAY_SIZE(app_filter_ops));
#else
@ -864,9 +951,12 @@ static void app_filter_fini(void)
af_clean_feature_list();
af_unregister_dev();
af_log_exit();
af_client_exit();
finit_af_client_procfs();
return ;
}
module_init(app_filter_init);
module_exit(app_filter_fini);

View File

@ -1,13 +1,14 @@
#ifndef APP_FILTER_H
#define APP_FILTER_H
#define AF_VERSION "1.0.1"
#define AF_VERSION "3.0.1"
#define AF_FEATURE_CONFIG_FILE "/etc/appfilter/feature.cfg"
#define MAX_PARSE_PKT_NUM 16
#define MIN_HTTP_DATA_LEN 16
#define MAX_APP_NAME_LEN 64
#define MAX_FEATURE_NUM_PER_APP 16
#define MIN_FEATURE_STR_LEN 16
#define MAX_FEATURE_STR_LEN 128
#define MAX_HOST_URL_LEN 128
#define MAX_REQUEST_URL_LEN 128
@ -80,6 +81,7 @@ typedef struct flow_info{
http_proto_t http;
https_proto_t https;
u_int32_t app_id;
u_int8_t drop;
}flow_info_t;

View File

@ -396,6 +396,102 @@ static char *print_object(cJSON *item,int depth)
*ptr++='}';*ptr++=0;
return out;
}
#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
static void skip_oneline_comment(char **input)
{
*input += static_strlen("//");
for (; (*input)[0] != '\0'; ++(*input))
{
if ((*input)[0] == '\n') {
*input += static_strlen("\n");
return;
}
}
}
static void skip_multiline_comment(char **input)
{
*input += static_strlen("/*");
for (; (*input)[0] != '\0'; ++(*input))
{
if (((*input)[0] == '*') && ((*input)[1] == '/'))
{
*input += static_strlen("*/");
return;
}
}
}
static void minify_string(char **input, char **output) {
(*output)[0] = (*input)[0];
*input += static_strlen("\"");
*output += static_strlen("\"");
for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
(*output)[0] = (*input)[0];
if ((*input)[0] == '\"') {
(*output)[0] = '\"';
*input += static_strlen("\"");
*output += static_strlen("\"");
return;
} else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
(*output)[1] = (*input)[1];
*input += static_strlen("\"");
*output += static_strlen("\"");
}
}
}
void cJSON_Minify(char *json)
{
char *into = json;
if (json == NULL)
{
return;
}
while (json[0] != '\0')
{
switch (json[0])
{
case ' ':
case '\t':
case '\r':
case '\n':
json++;
break;
case '/':
if (json[1] == '/')
{
skip_oneline_comment(&json);
}
else if (json[1] == '*')
{
skip_multiline_comment(&json);
} else {
json++;
}
break;
case '\"':
minify_string(&json, (char**)&into);
break;
default:
into[0] = json[0];
json++;
into++;
}
}
/* 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;}

View File

@ -77,7 +77,7 @@ extern cJSON *cJSON_CreateNumber(int num);
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);

View File

@ -2,6 +2,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=appfilter
PKG_VERSION:=3.0
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)

View File

@ -4,6 +4,7 @@
1002 微信:[tcp;;;;;01:f1|02:03,tcp;;;;;00:ab|01:00|02:00,tcp;;80;;/mmtls;]
1003 微博:[tcp;;443;weibo;;]
1004 陌陌:[tcp;;;momo;;,tcp;;;;;04:2f|05:66|06:65|07:65,tcp;;;;;00:03|01:03|02:00]
1005 支付宝:[tcp;;443;alipay.com;;]
#class game
2001 王者荣耀:[tcp;;;;;00:33|01:66|02:00|03:09,udp;;;;;00:01|01:02|02:00|03:00]
@ -45,12 +46,23 @@
4004 拼多多:[tcp;;;pinduoduo;;,tcp;;;yangkeduo.com;;,tcp;;;s1p.cdntip.com;;]
4005 蘑菇街:[tcp;;;mogujie;;,tcp;;;mogucdn;;,tcp;;;;;00:73|01:ea|02:68|03:fb|04:3f]
4006 苏宁易购:[tcp;;;.suning.;;]
4007 当当网:[tcp;;;.dangdang.com;;]
4008 1号店:[tcp;;;.yhd.com;;]
#class music
5001 网易云音乐:[tcp;;;music.163;;,tcp;;;music.126;;]
5002 QQ音乐:[tcp;;;;^/amobile.music.tc.qq.com;,tcp;;;qqmusic;;]
5003 酷狗音乐:[tcp;;;kugou;;,tcp;;;kgimg;;,tcp;;;fanxing;;]
5004 酷我音乐:[tcp;;;.kuwo.cn;;]
5005 喜马拉雅:[tcp;;;.ximalaya.com;;]
5006 千千音乐:[tcp;;;music.taihe.com;;]
5007 虾米音乐:[tcp;;;xiami;;]
5008 音悦台:[tcp;;;yinyuetai.com;;]
5009 豆瓣FM:[tcp;;;douban.fm;;]
5010 唱吧:[tcp;;;changba.com;;]
5011 音乐随心听:[tcp;;;fm.taihe.com;;]
5012 懒人听书:[tcp;;;lrts.me;;]
#class employee
6001 前程无忧:[tcp;;;51job;;]
@ -60,6 +72,13 @@
6005 同城急聘:[tcp;;;xiaomei;;]
6006 领英:[tcp;;;linkedin;;]
6007 斗米:[tcp;;;doumi;;]
6008 看准:[tcp;;;kanzhun.com;;]
6009 应届生求职:[tcp;;;yingjiesheng.com;;]
6010 中华英才网:[tcp;;;chinahr.com;;]
6011 拉勾网:[tcp;;;lagou.com;;]
6012 大街网:[tcp;;;dajie.com;;]
6013 boss直聘:[tcp;;;zhipin.com;;]
6014 实习僧:[tcp;;;shixiseng.com;;]
#class download
7001 迅雷:[udp;12345;;;;,udp;15000;;;;,tcp;;54321;;;,tcp;;12345;;;,udp;6881;;;;,tcp;;;xunlei.com;;,tcp;;;sandai;;,udp;;8000;;;,udp;;12346;;;,udp;12346;;;;]
@ -68,3 +87,81 @@
7004 ftp文件传输:[tcp;;21;;;]
7005 Vivo应用商店:[tcp;;443;appstore.vivo;;,tcp;;443;apkappdefwsdl.vivo;;]
#class website
8001 百度:[tcp;;;baidu.com;;]
8002 新浪:[tcp;;;sina.com;;]
8003 搜狐:[tcp;;;sohu.com;;]
8004 网易:[tcp;;;163.com;;,tcp;;443;126.com;;]
8005 凤凰网:[tcp;;;ifeng.com;;]
8006 人民网:[tcp;;;people.com.cn;;]
8007 凤凰网:[tcp;;;ifeng.com;;]
8008 中华网:[tcp;;;china.com;;]
8009 hao123:[tcp;;;hao123.com;;,]
8010 2345:[tcp;;;2345.com;;,]
8011 4399游戏:[tcp;;;4399.com;;]
8012 7k7k游戏:[tcp;;;7k7k.com;;]
8013 17173游戏:[tcp;;;17173.com;;]
8014 37网游:[tcp;;;37.com;;]
8015 游民星空:[tcp;;;gamersky.com;;]
8016 游侠网:[tcp;;;ali213.net;;]
8017 世纪佳缘:[tcp;;;jiayuan.com;;]
8018 珍爱网:[tcp;;;zhenai.com;;]
8019 百合网:[tcp;;;baihe.com;;]
8020 天涯社区:[tcp;;;tianya.cn;;]
8021 携程网:[tcp;;;ctrip.com;;]
8022 飞猪:[tcp;;;fliggy.com;;]
8023 12306:[tcp;;;12306.cn;;]
8024 马蜂窝:[tcp;;;mafengwo.cn;;]
8025 途牛:[tcp;;;tuniu.com;;]
8026 穷游网:[tcp;;;qyer.com;;]
8027 驴妈妈:[tcp;;;lvmama.com;;]
8028 同程旅游:[tcp;;;ly.com;;]
8029 太平洋汽车:[tcp;;;pcauto.com.cn;;]
8030 易车网:[tcp;;;bitauto.com;;]
8031 爱卡汽车:[tcp;;;xcar.com.cn;;]
8032 雪球:[tcp;;;xueqiu.com;;]
8033 东方财富:[tcp;;;eastmoney.com;;]
8034 证券之星:[tcp;;;stockstar.com;;]
8035 和讯:[tcp;;;hexun.com;;]
8036 第一财经:[tcp;;;yicai.com;;]
8037 全景网:[tcp;;;p5w.net;;]
8038 中彩网:[tcp;;;zhcw.com;;]
8039 中国体育彩票:[tcp;;;lottery.gov.cn;;]
8040 竞彩网:[tcp;;;sporttery.cn;;]
8041 豆丁:[tcp;;;docin.com;;]
8042 豆瓣:[tcp;;;douban.com;;]
8043 知乎:[tcp;;;zhihu.com;;]
8044 缤客:[tcp;;;booking.com;;]
8045 缤客:[tcp;;;booking.com;;]
8046 猫扑:[tcp;;;mop.com;;]
8047 赶集网:[tcp;;;ganji.com;;]
8048 安居客:[tcp;;;anjuke.com;;]
8049 房天下:[tcp;;;fang.com;;]
8050 链家:[tcp;;;lianjia.com;;]
8051 百姓网:[tcp;;;baixing.com;;]
8052 下厨房:[tcp;;;xiachufang.com;;]
8053 大众点评:[tcp;;;dianping.com;;]
8054 58同城:[tcp;;;58.com;;]
8055 天眼查:[tcp;;;tianyancha.com;;]
8056 千图网:[tcp;;;58pic.com;;]
8057 csdn社区:[tcp;;;csdn.net;;]
8058 有道词典:[tcp;;;dict.youdao.com;;]
8059 动漫之家:[tcp;;;dmzj.com;;]
8060 汽车之家:[tcp;;;autohome.com.cn;;]
8061 纵横中文网:[tcp;;;zongheng.com;;]
8062 起点中文网:[tcp;;;qidian.com;;]
8063 飞卢:[tcp;;;faloo.com;;]
8064 潇湘书院:[tcp;;;xxsy.net;;]
8065 cctv5:[tcp;;;sports.cctv.com;;]
8066 虎扑体育:[tcp;;;www.hupu.com;;]
8067 建设银行:[tcp;;;ccb.com;;]
8068 农业银行:[tcp;;;abchina.com;;]
8069 中国银行:[tcp;;;boc.cn;;]
8070 交通银行:[tcp;;;bankcomm.com;;]
8071 招商银行:[tcp;;;cmbchina.com;;]
8072 邮政储蓄:[tcp;;;psbc.com;;]
8073 兴业银行:[tcp;;;cib.com.cn;;]
8074 浦发银行:[tcp;;;spdb.com.cn;;]
8075 中信银行:[tcp;;;citicbank.com;;]
8076 上海银行:[tcp;;;bosc.cn;;]