diff --git a/luci-app-oaf/Makefile b/luci-app-oaf/Makefile index 5d89424..f5ed5fb 100755 --- a/luci-app-oaf/Makefile +++ b/luci-app-oaf/Makefile @@ -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 diff --git a/luci-app-oaf/luasrc/controller/appfilter.lua b/luci-app-oaf/luasrc/controller/appfilter.lua index f9ff51e..6bb4a09 100755 --- a/luci-app-oaf/luasrc/controller/appfilter.lua +++ b/luci-app-oaf/luasrc/controller/appfilter.lua @@ -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 + diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/appfilter.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/appfilter.lua index 3a67412..ea50ac7 100755 --- a/luci-app-oaf/luasrc/model/cbi/appfilter/appfilter.lua +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/appfilter.lua @@ -81,6 +81,7 @@ if class_fd then end class_fd:close() end +m:section(SimpleSection).template = "admin_network/user_status" return m diff --git a/luci-app-oaf/luasrc/view/admin_network/user_status.htm b/luci-app-oaf/luasrc/view/admin_network/user_status.htm new file mode 100755 index 0000000..7dc7373 --- /dev/null +++ b/luci-app-oaf/luasrc/view/admin_network/user_status.htm @@ -0,0 +1,59 @@ + + +
+

<%:访问记录%>

+
+
+
<%:Hostname%>
+
<%:mac地址%>
+
<%:ip地址%>
+
<%:应用名称%>
+
<%:丢包次数%>
+
<%:总访问次数%>
+
<%:最后访问时间%>
+
<%:过滤状态%>
+ +
+
+
<%:Collecting data...%>
+
+
+
+ diff --git a/luci-app-oaf/po/zh-cn/oaf.po b/luci-app-oaf/po/zh-cn/oaf.po index deb3a4f..b133f74 100755 --- a/luci-app-oaf/po/zh-cn/oaf.po +++ b/luci-app-oaf/po/zh-cn/oaf.po @@ -1,4 +1,7 @@ +msgid "website" +msgstr "常用网站" + msgid "appfilter" msgstr "应用过滤" @@ -39,4 +42,7 @@ msgid "App Filter Rules" msgstr "应用过滤规则" msgid "Enable App Filter" -msgstr "开启应用过滤" \ No newline at end of file +msgstr "开启应用过滤" + + + diff --git a/oaf/Makefile b/oaf/Makefile index f0725ce..51aa424 100755 --- a/oaf/Makefile +++ b/oaf/Makefile @@ -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 diff --git a/oaf/src/Makefile b/oaf/src/Makefile index 7b5aad0..96c1f61 100755 --- a/oaf/src/Makefile +++ b/oaf/src/Makefile @@ -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 diff --git a/oaf/src/af_client.c b/oaf/src/af_client.c new file mode 100755 index 0000000..3bede34 --- /dev/null +++ b/oaf/src/af_client.c @@ -0,0 +1,277 @@ +/* + Author:Derry + Date: 2019/11/12 +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ; +} + + diff --git a/oaf/src/af_client.h b/oaf/src/af_client.h new file mode 100755 index 0000000..c391241 --- /dev/null +++ b/oaf/src/af_client.h @@ -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 diff --git a/oaf/src/af_client_fs.c b/oaf/src/af_client_fs.c new file mode 100755 index 0000000..8a64fff --- /dev/null +++ b/oaf/src/af_client_fs.c @@ -0,0 +1,222 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} + diff --git a/oaf/src/af_client_fs.h b/oaf/src/af_client_fs.h new file mode 100755 index 0000000..0140c27 --- /dev/null +++ b/oaf/src/af_client_fs.h @@ -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 diff --git a/oaf/src/app_filter.c b/oaf/src/app_filter.c index 3ad15a2..67e8607 100755 --- a/oaf/src/app_filter.c +++ b/oaf/src/app_filter.c @@ -7,7 +7,6 @@ #include #include #include - #include #include #include @@ -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); diff --git a/oaf/src/app_filter.h b/oaf/src/app_filter.h index b1e51db..b45c1d3 100755 --- a/oaf/src/app_filter.h +++ b/oaf/src/app_filter.h @@ -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; diff --git a/oaf/src/cJSON.c b/oaf/src/cJSON.c index 8b22453..e71f1d8 100755 --- a/oaf/src/cJSON.c +++ b/oaf/src/cJSON.c @@ -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;} diff --git a/oaf/src/cJSON.h b/oaf/src/cJSON.h index e7c794c..97b701c 100755 --- a/oaf/src/cJSON.h +++ b/oaf/src/cJSON.h @@ -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); diff --git a/open-app-filter/Makefile b/open-app-filter/Makefile index a9a495d..e8f9d31 100755 --- a/open-app-filter/Makefile +++ b/open-app-filter/Makefile @@ -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) diff --git a/open-app-filter/files/feature.cfg b/open-app-filter/files/feature.cfg index 733308e..09faf0b 100755 --- a/open-app-filter/files/feature.cfg +++ b/open-app-filter/files/feature.cfg @@ -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;;] +