diff --git a/README.md b/README.md
index 7017818575..ba7458fdc0 100755
--- a/README.md
+++ b/README.md
@@ -36,8 +36,9 @@ make -j1 V=s
### Based on: [coolsnowwolf/lede](https://github.com/coolsnowwolf/lede).
Packages jsda: [jsda/packages2](https://github.com/jsda/packages2).
-luci-theme-netgearv2 source: [tracemouse/luci-theme-netgear](https://github.com/tracemouse/luci-theme-netgear).
-luci-app-serverchan source: [zxlhhyccc/luci-app-serverchan](https://github.com/zxlhhyccc/luci-app-serverchan).
+luci-theme-netgearv2 source: [tracemouse/luci-theme-netgear](https://github.com/tracemouse/luci-theme-netgear).
+luci-app-serverchan source: [zxlhhyccc/luci-app-serverchan](https://github.com/zxlhhyccc/luci-app-serverchan).
+OpenAppFilter source: [destan19/OpenAppFilter](https://github.com/destan19/OpenAppFilter)
# License
### [GPL v3](https://www.gnu.org/licenses/gpl-3.0.html).
diff --git a/package/cnsztl/luci-app-oaf/Makefile b/package/cnsztl/luci-app-oaf/Makefile
new file mode 100755
index 0000000000..7e0e49ec0d
--- /dev/null
+++ b/package/cnsztl/luci-app-oaf/Makefile
@@ -0,0 +1,14 @@
+
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Open App Filter Module
+LUCI_PKGARCH:=all
+PKG_NAME:=luci-app-oaf
+PKG_VERSION:=1.0
+PKG_RELEASE:=1
+include $(TOPDIR)/feeds/luci/luci.mk
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/package/cnsztl/luci-app-oaf/luasrc/controller/appfilter.lua b/package/cnsztl/luci-app-oaf/luasrc/controller/appfilter.lua
new file mode 100755
index 0000000000..f9ff51ebf2
--- /dev/null
+++ b/package/cnsztl/luci-app-oaf/luasrc/controller/appfilter.lua
@@ -0,0 +1,12 @@
+module("luci.controller.appfilter", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/appfilter") then
+ return
+ end
+
+ local page
+
+ page = entry({"admin", "network", "appfilter"}, cbi("appfilter/appfilter"), _("appfilter"))
+ page.dependent = true
+end
diff --git a/package/cnsztl/luci-app-oaf/luasrc/model/cbi/appfilter/appfilter.lua b/package/cnsztl/luci-app-oaf/luasrc/model/cbi/appfilter/appfilter.lua
new file mode 100755
index 0000000000..3a6741245b
--- /dev/null
+++ b/package/cnsztl/luci-app-oaf/luasrc/model/cbi/appfilter/appfilter.lua
@@ -0,0 +1,86 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2008 Steven Barth
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+local ds = require "luci.dispatcher"
+
+local m, s
+
+m = Map("appfilter",
+ translate("appfilter"),
+ translate(""))
+
+s = m:section(TypedSection, "global", translate("Basic Settings"))
+s:option(Flag, "enable", translate("Enable App Filter"),translate(""))
+s.anonymous = true
+
+
+s = m:section(TypedSection, "appfilter", translate("App Filter Rules"))
+s.anonymous = true
+s.addremove = false
+
+
+local class_fd = io.popen("find /etc/appfilter/ -type f -name '*.class'")
+if class_fd then
+ while true do
+ local apps
+ local class
+ local path = class_fd:read("*l")
+ if not path then
+ break
+ end
+
+ class = path:match("([^/]+)%.class$")
+ -- add a tab
+ s:tab(class, translate(class))
+ -- multi value option
+ apps = s:taboption(class, MultiValue, class.."apps", translate(""))
+ apps.rmempty=true
+ --apps.delimiter=";"
+ -- select
+ apps.widget="checkbox"
+ apps.size=12
+
+ local fd = io.open(path)
+ if fd then
+ local line
+ while true do
+ local cmd
+ local cmd_fd
+ line = fd:read("*l")
+ if not line then break end
+ if string.len(line) < 5 then break end
+ if not string.find(line,"#") then
+ cmd = "echo "..line.."|awk '{print $1}'"
+ cmd_fd = io.popen(cmd)
+ id = cmd_fd:read("*l");
+ cmd_fd:close()
+
+ cmd = "echo "..line.."|awk '{print $2}'"
+ cmd_fd = io.popen(cmd)
+ name = cmd_fd:read("*l")
+
+ cmd_fd:close()
+ if not id then break end
+ if not name then break end
+ apps:value(id, name)
+ end
+ end
+ fd:close()
+ end
+ end
+ class_fd:close()
+end
+
+
+return m
diff --git a/package/cnsztl/luci-app-oaf/po/zh-cn/oaf.po b/package/cnsztl/luci-app-oaf/po/zh-cn/oaf.po
new file mode 100755
index 0000000000..deb3a4fabb
--- /dev/null
+++ b/package/cnsztl/luci-app-oaf/po/zh-cn/oaf.po
@@ -0,0 +1,42 @@
+
+msgid "appfilter"
+msgstr "应用过滤"
+
+msgid "game"
+msgstr "游戏"
+
+msgid "web"
+msgstr "网页"
+
+msgid "video"
+msgstr "视频"
+
+msgid "chat"
+msgstr "聊天"
+
+msgid "download"
+msgstr "下载"
+
+msgid "p2p"
+msgstr "p2p"
+
+msgid "music"
+msgstr "音乐"
+
+msgid "shopping"
+msgstr "购物"
+
+msgid "working"
+msgstr "办公"
+
+msgid "employee"
+msgstr "招聘"
+
+msgid "Basic Settings"
+msgstr "基本设置"
+
+msgid "App Filter Rules"
+msgstr "应用过滤规则"
+
+msgid "Enable App Filter"
+msgstr "开启应用过滤"
\ No newline at end of file
diff --git a/package/cnsztl/luci-app-oaf/root/etc/uci-defaults/91_luci-oaf b/package/cnsztl/luci-app-oaf/root/etc/uci-defaults/91_luci-oaf
new file mode 100755
index 0000000000..2041cc3dfa
--- /dev/null
+++ b/package/cnsztl/luci-app-oaf/root/etc/uci-defaults/91_luci-oaf
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# replace existing mwan ucitrack entry
+uci -q batch <<-EOF >/dev/null
+ del ucitrack.@appfilter[-1]
+ add ucitrack appfilter
+ set ucitrack.@appfilter[-1].exec="/etc/init.d/appfilter reload"
+ commit ucitrack
+EOF
+
+# remove LuCI cache
+rm -rf /tmp/luci-indexcache /tmp/luci-modulecache
+
+exit 0
diff --git a/package/cnsztl/oaf/Makefile b/package/cnsztl/oaf/Makefile
new file mode 100755
index 0000000000..f0725ce9ee
--- /dev/null
+++ b/package/cnsztl/oaf/Makefile
@@ -0,0 +1,45 @@
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_NAME:=oaf
+PKG_RELEASE:=3
+
+include $(INCLUDE_DIR)/package.mk
+
+PKG_AUTOLOAD:=oaf
+
+define KernelPackage/oaf
+ SECTION:=Derry Apps
+ CATEGORY:=Derry Apps
+ TITLE:=open app filter kernel module
+ FILES:=$(PKG_BUILD_DIR)/oaf.ko
+ KCONFIG:=
+ AUTOLOAD:=$(call AutoLoad,0,$(PKG_AUTOLOAD))
+endef
+
+define KernelPackage/oaf/description
+ open appfilter kernel module
+endef
+
+
+MAKE_OPTS:= \
+ ARCH="$(LINUX_KARCH)" \
+ CROSS_COMPILE="$(TARGET_CROSS)" \
+ SUBDIRS="$(PKG_BUILD_DIR)" \
+ EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
+ $(EXTRA_KCONFIG)
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+ $(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+define Build/Compile
+ $(MAKE) -C "$(LINUX_DIR)" \
+ $(MAKE_OPTS) \
+ modules
+endef
+
+$(eval $(call KernelPackage,oaf))
+
diff --git a/package/cnsztl/oaf/src/Makefile b/package/cnsztl/oaf/src/Makefile
new file mode 100755
index 0000000000..1ab57a0118
--- /dev/null
+++ b/package/cnsztl/oaf/src/Makefile
@@ -0,0 +1,2 @@
+oaf-objs := app_filter.o af_utils.o regexp.o cJSON.o app_filter_config.o
+obj-m += oaf.o
diff --git a/package/cnsztl/oaf/src/af_utils.c b/package/cnsztl/oaf/src/af_utils.c
new file mode 100755
index 0000000000..3ed23d4ce3
--- /dev/null
+++ b/package/cnsztl/oaf/src/af_utils.c
@@ -0,0 +1,296 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "af_utils.h"
+
+int check_local_network_ip(unsigned int ip)
+{
+ if ((ip & 0xffff0000) == 0xc0a80000)
+ return 1;
+ else if ((ip & 0xfff00000) == 0xac100000)
+ return 1;
+ else if ((ip & 0xff000000) == 0x0a000000)
+ return 1;
+ else
+ return 0;
+}
+
+void dump_str(char *name, unsigned char *p, int len)
+{
+ #define MAX_DUMP_STR_LEN 64
+ char buf[MAX_DUMP_STR_LEN] = {0};
+ int i;
+ if (len > MAX_DUMP_STR_LEN) {
+ len = MAX_DUMP_STR_LEN - 1;
+ }
+ printk("%s: ",name);
+ strncpy(buf, p, len);
+ printk("[%s]\n", buf);
+}
+
+void dump_hex(char *name, unsigned char *p, int len)
+{
+ #define MAX_DUMP_STR_LEN 64
+ int i;
+ if (len > MAX_DUMP_STR_LEN) {
+ len = MAX_DUMP_STR_LEN - 1;
+ }
+ printk("%s: ",name);
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0)
+ printk("\n");
+ printk("%02X ",*(p + i));
+ }
+ printk("\n");
+}
+
+#ifndef va_arg
+typedef signed int acpi_native_int;
+#ifndef _VALIST
+#define _VALIST
+typedef char *va_list;
+#endif
+
+#define _AUPBND (sizeof (acpi_native_int) - 1)
+#define _ADNBND (sizeof (acpi_native_int) - 1)
+
+
+#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
+#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
+#define va_end(ap) (void) 0
+#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
+
+#endif
+
+#ifndef TOLOWER
+#define TOLOWER(x) ((x) | 0x20)
+#endif
+
+
+static long long k_simple_strtoll(const char *cp, char **endp, unsigned int base)
+{
+ if (*cp == '-')
+ return -simple_strtoull(cp + 1, endp, base);
+
+ return simple_strtoull(cp, endp, base);
+}
+
+static int skip_atoi(const char **s)
+{
+ int i=0;
+ while (isdigit(**s))
+ i = i*10 + *((*s)++) - '0';
+ return i;
+}
+
+char *skip_spaces(const char *str)
+{
+ while (isspace(*str) && ((unsigned char )*str != 0xa0))
+ ++str;
+ return (char *)str;
+}
+static int k_vsscanf(const char *buf, const char *fmt, va_list args)
+{
+ const char *str = buf;
+ char *next;
+ char digit;
+ 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)) {
+ fmt = skip_spaces(++fmt);
+ str = skip_spaces(str);
+ }
+
+ if (*fmt != '%' && *fmt) {
+ if (*fmt++ != *str++)
+ break;
+ continue;
+ }
+ if (!*fmt)
+ break;
+ ++fmt;
+
+ if (*fmt == '*') {
+ while (!isspace(*fmt) && *fmt != '%' && *fmt)
+ fmt++;
+ while (!isspace(*str) && *str)
+ str++;
+ continue;
+ }
+ field_width = -1;
+ if (isdigit(*fmt))
+ field_width = skip_atoi(&fmt);
+ qualifier = -1;
+ if (*fmt == 'h' || TOLOWER(*fmt) == 'l' ||
+ TOLOWER(*fmt) == 'z') {
+ qualifier = *fmt++;
+ if (unlikely(qualifier == *fmt)) {
+ if (qualifier == 'h') {
+ qualifier = 'H';
+ fmt++;
+ } else if (qualifier == 'l') {
+ qualifier = 'L';
+ fmt++;
+ }
+ }
+ }
+ if (!*fmt || !*str)
+ break;
+ base = 10;
+ is_sign = 0;
+ switch (*fmt++) {
+ case 'c':
+ {
+ char *s = (char *)va_arg(args, char*);
+ if (field_width == -1)
+ field_width = 1;
+ do {
+ *s++ = *str++;
+ } while (--field_width > 0 && *str);
+ num++;
+ }
+ continue;
+ 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--)
+ *s++ = *str++;
+ *s = '\0';
+ num++;
+ }
+ continue;
+ case 'n':
+ {
+ int *i = (int *)va_arg(args, int*);
+ *i = str - buf;
+ }
+ continue;
+ case 'o':
+ base = 8;
+ break;
+ case 'x':
+ case 'X':
+ base = 16;
+ break;
+ case 'i':
+ base = 0;
+ case 'd':
+ is_sign = 1;
+ case 'u':
+ break;
+ case '%':
+ if (*str++ != '%')
+ return num;
+ continue;
+ default:
+ return num;
+ }
+ str = skip_spaces(str);
+ digit = *str;
+ if (is_sign && digit == '-')
+ digit = *(str + 1);
+ if (!digit
+ || (base == 16 && !isxdigit(digit))
+ || (base == 10 && !isdigit(digit))
+ || (base == 8 && (!isdigit(digit) || digit > '7'))
+ || (base == 0 && !isdigit(digit)))
+ break;
+
+ switch (qualifier) {
+ case 'H':
+ if (is_sign) {
+ signed char *s = (signed char *)va_arg(args, signed char *);
+ *s = (signed char)simple_strtol(str, &next, base);
+ } else {
+ unsigned char *s = (unsigned char *)va_arg(args, unsigned char *);
+ *s = (unsigned char)simple_strtoul(str, &next, base);
+ }
+ break;
+ case 'h':
+ if (is_sign) {
+ short *s = (short *)va_arg(args, short *);
+ *s = (short)simple_strtol(str, &next, base);
+ } else {
+ unsigned short *s = (unsigned short *)va_arg(args, unsigned short *);
+ *s = (unsigned short)simple_strtoul(str, &next, base);
+ }
+ break;
+ case 'l':
+ if (is_sign) {
+ long *l = (long *)va_arg(args, long *);
+ *l = simple_strtol(str, &next, base);
+ } else {
+ unsigned long *l = (unsigned long *)va_arg(args, unsigned long *);
+ *l = simple_strtoul(str, &next, base);
+ }
+ break;
+ case 'L':
+ if (is_sign) {
+ long long *l = (long long *)va_arg(args, long long *);
+ *l = k_simple_strtoll(str, &next, base);
+ } else {
+ unsigned long long *l = (unsigned long long *)va_arg(args, unsigned long long *);
+ *l = simple_strtoull(str, &next, base);
+ }
+ break;
+ case 'Z':
+ case 'z':
+ {
+ size_t *s = (size_t *)va_arg(args, size_t *);
+ *s = (size_t)simple_strtoul(str, &next, base);
+ }
+ break;
+ default:
+ if (is_sign) {
+ int *i = (int *)va_arg(args, int *);
+ *i = (int)simple_strtol(str, &next, base);
+ } else {
+ unsigned int *i = (unsigned int *)va_arg(args, unsigned int*);
+ *i = (unsigned int)simple_strtoul(str, &next, base);
+ }
+ break;
+ }
+ num++;
+ if (!next)
+ break;
+ str = next;
+ }
+ if (*fmt == '%' && *(fmt + 1) == 'n') {
+ int *p = (int *)va_arg(args, int *);
+ *p = str - buf;
+ }
+ return num;
+}
+
+int k_sscanf(const char *buf, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+ va_start(args, fmt);
+ i = k_vsscanf(buf, fmt, args);
+ va_end(args);
+ return i;
+}
+
+
diff --git a/package/cnsztl/oaf/src/af_utils.h b/package/cnsztl/oaf/src/af_utils.h
new file mode 100755
index 0000000000..201adfccdc
--- /dev/null
+++ b/package/cnsztl/oaf/src/af_utils.h
@@ -0,0 +1,13 @@
+#ifndef AF_UTILS_H
+#define AF_UTILS_H
+
+int check_local_network_ip(unsigned int ip);
+
+void dump_str(char *name, unsigned char *p, int len);
+
+void dump_hex(char *name, unsigned char *p, int len);
+
+int k_sscanf(const char *buf, const char *fmt, ...);
+
+#endif
+
diff --git a/package/cnsztl/oaf/src/app_filter.c b/package/cnsztl/oaf/src/app_filter.c
new file mode 100755
index 0000000000..b4587c8e54
--- /dev/null
+++ b/package/cnsztl/oaf/src/app_filter.c
@@ -0,0 +1,892 @@
+
+/*
+ author: destan19@126.com
+ Źں: OpenWrt
+ date:2019/1/10
+*/
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "app_filter.h"
+#include "af_utils.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("destan19@126.com");
+MODULE_DESCRIPTION("app filter module");
+MODULE_VERSION("1.0.1");
+struct list_head af_feature_head = LIST_HEAD_INIT(af_feature_head);
+#define AF_FEATURE_CONFIG_FILE "/etc/appfilter/feature.cfg"
+//#define AF_DEV_NAME "appfilter"
+
+DEFINE_RWLOCK(af_feature_lock);
+
+#define feature_list_read_lock() read_lock_bh(&af_feature_lock);
+#define feature_list_read_unlock() read_unlock_bh(&af_feature_lock);
+#define feature_list_write_lock() write_lock_bh(&af_feature_lock);
+#define feature_list_write_unlock() write_unlock_bh(&af_feature_lock);
+
+#define MIN_HTTP_DATA_LEN 16
+#define MAX_APP_NAME_LEN 64
+#define MAX_FEATURE_NUM_PER_APP 16
+#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 256
+#define MIN_FEATURE_LINE_LEN 16
+
+#define MAX_URL_MATCH_LEN 64
+typedef struct af_pos_info{
+ int pos;
+ unsigned char value;
+}af_pos_info_t;
+
+typedef struct af_feature_node{
+ struct list_head head;
+ int app_id;
+ char app_name[MAX_APP_NAME_LEN];
+ char feature_str[MAX_FEATURE_NUM_PER_APP][MAX_FEATURE_STR_LEN];
+ int proto;
+ int sport;
+ int dport;
+ char host_url[MAX_HOST_URL_LEN];
+ char request_url[MAX_REQUEST_URL_LEN];
+ int pos_num;
+ af_pos_info_t pos_info[MAX_POS_INFO_PER_FEATURE];
+}af_feature_node_t;
+#if 0
+static void show_feature_list(void)
+{
+ af_feature_node_t *n,*node;
+ unsigned int count = 0;
+ feature_list_read_lock();
+ if(!list_empty(&af_feature_head)) { // handle qos
+ list_for_each_entry_safe(node, n, &af_feature_head, head) {
+ count ++;
+ printk("[%d] id=%d appname:%s, dport:%d, host:%s, request:%s\n",
+ count,
+ node->app_id, node->app_name,
+ node->dport,node->host_url, node->request_url);
+ int i;
+ for (i = 0;i < node->pos_num;i++){
+ printk("(%d:%x)-->",
+ node->pos_info[i].pos,
+ node->pos_info[i].value);
+
+ }
+ printk("\n----------------------------------------\n\n\n");
+ }
+ }
+ feature_list_read_unlock();
+}
+static af_feature_node_t* af_find_feature(char *app_id)
+{
+ af_feature_node_t *node;
+ feature_list_read_lock();
+
+ if (!list_empty(&af_feature_head)) {
+ list_for_each_entry(node, &af_feature_head, head) {
+ if (node->app_id == app_id){
+ feature_list_read_unlock();
+ return node;
+ }
+ }
+ }
+ feature_list_read_unlock();
+ return NULL;
+}
+#endif
+
+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,
+};
+
+int __add_app_feature(int appid,
+ char *name,
+ int proto,
+ int src_port,
+ int dst_port,
+ char *host_url,
+ char *request_url,
+ char *dict)
+{
+ af_feature_node_t *node = NULL;
+ node = kzalloc(sizeof(af_feature_node_t), GFP_KERNEL);
+ if (node == NULL) {
+ printk("malloc feature memory error\n");
+ return -1;
+ }
+ else {
+ node->app_id = appid;
+ strcpy(node->app_name, name);
+ node->proto = proto;
+ node->dport = dst_port;
+ node->sport = src_port;
+ strcpy(node->host_url, host_url);
+ strcpy(node->request_url, request_url);
+ // 00:0a-01:11
+ char *p = dict;
+ char *begin = dict;
+ char pos[32] = {0};
+ int index = 0;
+ int value = 0;
+
+ while (*p++) {
+ if (*p == '|'){
+ memset(pos, 0x0, sizeof(pos));
+ strncpy(pos, begin, p - begin);
+ k_sscanf(pos, "%d:%x",&index, &value);
+ begin = p + 1;
+ node->pos_info[node->pos_num].pos = index;
+ node->pos_info[node->pos_num].value = value;
+ node->pos_num++;
+ }
+ }
+
+ if (begin != dict) {
+ strncpy(pos, begin, p - begin);
+ k_sscanf(pos, "%d:%x",&index, &value);
+ node->pos_info[node->pos_num].pos = index;
+ node->pos_info[node->pos_num].value = value;
+ node->pos_num++;
+ }
+ feature_list_write_lock();
+ list_add(&(node->head), &af_feature_head);
+ feature_list_write_unlock();
+ }
+ return 0;
+}
+//[tcp;;443;baidu.com;;]
+int add_app_feature(int appid, char *name, char *feature)
+{
+ char proto_str[16] = {0};
+ char src_port_str[16] = {0};
+
+ char dst_port_str[16] = {0};
+ char host_url[32] = {0};
+ char request_url[128] = {0};
+ char dict[128] = {0};
+ int proto = IPPROTO_TCP;
+ if (!name || !feature) {
+ printk("error, name or feature is null\n");
+ return -1;
+ }
+ // tcp;8000;www.sina.com;0:get_name;00:0a-01:11
+
+ char *p = feature;
+ char *begin = feature;
+ int param_num = 0;
+ while(*p++) {
+ if (*p != ';')
+ continue;
+
+ switch(param_num){
+
+ case AF_PROTO_PARAM_INDEX:
+ strncpy(proto_str, begin, p - begin);
+ break;
+ case AF_SRC_PORT_PARAM_INDEX:
+ strncpy(src_port_str, begin, p - begin);
+ break;
+ case AF_DST_PORT_PARAM_INDEX:
+ strncpy(dst_port_str, begin, p - begin);
+ break;
+
+ case AF_HOST_URL_PARAM_INDEX:
+ strncpy(host_url, begin, p - begin);
+ break;
+
+ case AF_REQUEST_URL_PARAM_INDEX:
+ strncpy(request_url, begin, p - begin);
+ break;
+ }
+ param_num ++;
+ begin = p + 1;
+ }
+ if (AF_DICT_PARAM_INDEX != param_num) {
+ printk("invalid feature:%s\n", feature);
+ return -1;
+ }
+ strncpy(dict, begin, p - begin);
+
+ //sscanf(feature, "%[^;];%d;%[^;];%[^;];%s", proto, &dst_port, host, url, dict);
+ //printk("proto = %s, port = %s, host = %s, url = %s, dict = %s\n",
+ // proto_str, port_str, host_url, request_url, dict);
+ if (0 == strcmp(proto_str, "tcp"))
+ proto = IPPROTO_TCP;
+ else if (0 == strcmp(proto_str, "udp"))
+ proto = IPPROTO_UDP;
+ else {
+ AF_DEBUG("proto %s is not support\n", proto_str);
+ return -1;
+ }
+ int dst_port = 0;
+
+ int src_port = 0;
+ sscanf(src_port_str, "%d", &src_port);
+
+ sscanf(dst_port_str, "%d", &dst_port);
+
+ __add_app_feature(appid,
+ name,
+ proto,
+ src_port,
+ dst_port,
+ host_url,
+ request_url,
+ dict);
+
+ return 0;
+}
+
+
+void af_init_feature(char *feature_str)
+{
+ int app_id;
+ char app_name[128] = {0};
+ char feature_buf[MAX_FEATURE_LINE_LEN] = {0};
+ if (strstr(feature_str,"#"))
+ return;
+ //printk("feature_str=%s\n",feature_str);
+
+ k_sscanf(feature_str, "%d%[^:]", &app_id, app_name);
+ //printk("id = %d, name = %s\n",app_id, app_name);
+
+ char *p = feature_str;
+ char *pos = NULL;
+ int len = 0;
+ while(*p++) {
+ if (*p == '['){
+ pos = p + 1;
+ continue;
+ }
+ if (*p == ']' && pos != NULL) {
+ len = p - pos;
+ }
+ }
+
+ if (pos && len)
+ strncpy(feature_buf, pos, len);
+ char feature[MAX_FEATURE_STR_LEN];;
+ int i;
+ memset(feature, 0x0, sizeof(feature));
+ p = feature_buf;
+ char *begin = feature_buf;
+
+ while(*p++){
+ if (*p == ',') {
+ memset(feature, 0x0, sizeof(feature));
+ strncpy((char *)feature, begin, p - begin);
+
+ add_app_feature(app_id, app_name, feature);
+ begin = p + 1;
+ }
+ }
+ if (p != begin){
+ memset(feature, 0x0, sizeof(feature));
+ strncpy((char *)feature, begin, p - begin);
+ add_app_feature(app_id, app_name, feature);
+ }
+}
+
+void load_feature_buf_from_file(char **config_buf)
+{
+ struct inode *inode = NULL;
+ struct file *fp = NULL;
+ mm_segment_t fs;
+ off_t size;
+ fp = filp_open(AF_FEATURE_CONFIG_FILE, O_RDONLY, 0);
+
+ if(IS_ERR(fp)) {
+ 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);
+ if (size == 0) {
+ printk("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");
+ filp_close(fp, NULL);
+ return -1;
+ }
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+// 4.14rc3 vfs_read-->kernel_read
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)
+ kernel_read(fp, *config_buf, size, &(fp->f_pos));
+#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;
+}
+
+void load_feature_config(void)
+{
+ printk("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");
+ return;
+ }
+
+// printk("feature_buf = %s\n", feature_buf);
+ char *p;
+ char *begin;
+ p = begin = feature_buf;
+ char line[MAX_FEATURE_LINE_LEN] = {0};
+ while(*p++) {
+ if (*p == '\n'){
+ if (p - begin < MIN_FEATURE_LINE_LEN || p - begin > MAX_FEATURE_LINE_LEN ) {
+ begin = p + 1;
+ continue;
+ }
+ memset(line, 0x0, sizeof(line));
+ strncpy(line, begin, p - begin);
+ af_init_feature(line);
+ begin = p + 1;
+ }
+ }
+ if (p != begin) {
+ if (p - begin < MIN_FEATURE_LINE_LEN || p - begin > MAX_FEATURE_LINE_LEN )
+ return;
+ memset(line, 0x0, sizeof(line));
+ strncpy(line, begin, p - begin);
+ af_init_feature(line);
+ begin = p + 1;
+ }
+ if (feature_buf)
+ kfree(feature_buf);
+}
+
+static void af_clean_feature_list(void)
+{
+ af_feature_node_t *n,*node;
+ feature_list_write_lock();
+ while(!list_empty(&af_feature_head)) {
+ node = list_first_entry(&af_feature_head, af_feature_node_t, head);
+ list_del(&(node->head));
+ kfree(node);
+ }
+ feature_list_write_unlock();
+}
+
+
+int parse_flow_base(struct sk_buff *skb, flow_info_t *flow)
+{
+ struct tcphdr * tcph = NULL;
+ struct udphdr * udph = NULL;
+ struct nf_conn *ct = NULL;
+ struct iphdr *iph = NULL;
+ if (!skb) {
+ return -1;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
+ ct = (struct nf_conn *)skb->_nfct;
+#else
+ ct = (struct nf_conn *)skb->nfct;
+#endif
+ if (!ct) {
+ return -1;
+ }
+ iph = ip_hdr(skb);
+ if ( !iph ) {
+ return -1;
+ }
+ flow->ct = ct;
+ flow->src = iph->saddr;
+ flow->dst = iph->daddr;
+ flow->l4_protocol = iph->protocol;
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ tcph = (struct tcphdr *)(iph + 1);
+ flow->l4_data = skb->data + iph->ihl * 4 + tcph->doff * 4;
+ flow->l4_len = ntohs(iph->tot_len) - iph->ihl * 4 - tcph->doff * 4;
+ flow->dport = htons(tcph->dest);
+ flow->sport = htons(tcph->source);
+ break;
+ 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;
+ case IPPROTO_ICMP:
+ break;
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+
+/*
+ desc: https urlϢ浽flow
+ return:
+ -1: error
+ 0: match
+ author: Derry
+ Date:2018/12/19
+*/
+int parse_https_proto(flow_info_t *flow) {
+ int i ;
+ short url_len = 0 ;
+ char * p = flow->l4_data;
+ int data_len = flow->l4_len;
+
+ if (NULL == flow) {
+ AF_ERROR("flow is NULL\n");
+ return -1;
+ }
+ if (NULL == p || data_len == 0) {
+ return -1;
+ }
+ if (!(p[0] == 0x16 && p[1] == 0x03 && p[2] == 0x01))
+ return -1;
+
+ for(i = 0; i < data_len; i++) {
+ if(i + HTTPS_URL_OFFSET >= data_len) {
+ return -1;
+ }
+
+ if(p[i] == 0x0 && p[i + 1] == 0x0 && p[i + 2] == 0x0 && p[i + 3] != 0x0) {
+ // 2 bytes
+ memcpy(&url_len , p + i + HTTPS_LEN_OFFSET, 2);
+ if(ntohs(url_len) <= 0 || ntohs(url_len) > data_len) {
+ continue ;
+ }
+
+ if(i + HTTPS_URL_OFFSET + ntohs(url_len) < data_len) {
+ //dump_hex("https hex", p, data_len);
+ flow->https.match = AF_TRUE;
+ flow->https.url_pos = p + i + HTTPS_URL_OFFSET;
+ //dump_str("https url", flow->https.url_pos, 5);
+ flow->https.url_len = ntohs(url_len);
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+
+void parse_http_proto(flow_info_t *flow)
+{
+ if (!flow) {
+ AF_ERROR("flow is null\n");
+ return;
+ }
+ if (flow->l4_protocol != IPPROTO_TCP) {
+ return;
+ }
+
+ int i = 0;
+ int start = 0;
+ char *data = flow->l4_data;
+ int data_len = flow->l4_len;
+ if (data_len < MIN_HTTP_DATA_LEN) {
+ return;
+ }
+ if (flow->sport != 80 && flow->dport != 80)
+ return;
+ for (i = 0; i < data_len - 4; i++) {
+ if (data[i] == 0x0d && data[i + 1] == 0x0a){
+ if (0 == memcmp(&data[start], "POST ", 5)) {
+ flow->http.match = AF_TRUE;
+ flow->http.method = HTTP_METHOD_POST;
+ flow->http.url_pos = data + start + 5;
+ flow->http.url_len = i - start - 5;
+ //dump_str("get request", flow->http.url_pos, flow->http.url_len);
+ }
+ else if(0 == memcmp(&data[start], "GET ", 4)) {
+ flow->http.match = AF_TRUE;
+ flow->http.method = HTTP_METHOD_GET;
+ flow->http.url_pos = data + start + 4;
+ flow->http.url_len = i - start - 4;
+ //dump_str("post request", flow->http.url_pos, flow->http.url_len);
+ }
+ else if (0 == memcmp(&data[start], "Host: ", 6) ){
+ flow->http.host_pos = data + start + 6;
+ flow->http.host_len = i - start - 6;
+ //dump_str("host ", flow->http.host_pos, flow->http.host_len);
+ }
+ // жhttpͷ
+ if (data[i + 2] == 0x0d && data[i + 3] == 0x0a){
+ flow->http.data_pos = data + i + 4;
+ flow->http.data_len = data_len - i - 4;
+ break;
+ }
+ // 0x0d 0x0a
+ start = i + 2;
+ }
+ }
+}
+#if 0
+
+static void dump_http_flow_info(http_proto_t *http) {
+ if (!http) {
+ AF_ERROR("http ptr is NULL\n");
+ return ;
+ }
+ if (!http->match)
+ return;
+ if (http->method == HTTP_METHOD_GET){
+ printk("Http method: "HTTP_GET_METHOD_STR"\n");
+ }
+ else if (http->method == HTTP_METHOD_POST) {
+ printk("Http method: "HTTP_POST_METHOD_STR"\n");
+ }
+ if (http->url_len > 0 && http->url_pos){
+ dump_str("Request url", http->url_pos, http->url_len);
+ }
+
+ if (http->host_len > 0 && http->host_pos){
+ dump_str("Host", http->host_pos, http->host_len);
+ }
+
+ printk("--------------------------------------------------------\n\n\n");
+}
+
+static void dump_https_flow_info(https_proto_t *https) {
+ if (!https) {
+ AF_ERROR("https ptr is NULL\n");
+ return ;
+ }
+ if (!https->match)
+ return;
+
+
+ if (https->url_len > 0 && https->url_pos){
+ printk("url len = %d\n",https->url_len);
+ dump_str("https server name", https->url_pos, https->url_len);
+ }
+
+ printk("--------------------------------------------------------\n\n\n");
+}
+static void dump_flow_info(flow_info_t *flow)
+{
+ if (!flow) {
+ AF_ERROR("flow is null\n");
+ return;
+ }
+ #if 0
+ if (check_local_network_ip(ntohl(flow->src))) {
+ printk("src ip(inner net):"NIPQUAD_FMT", dst ip = "NIPQUAD_FMT"\n", NIPQUAD(flow->src), NIPQUAD(flow->dst));
+ }
+ else {
+ printk("src ip(outer net):"NIPQUAD_FMT", dst ip = "NIPQUAD_FMT"\n", NIPQUAD(flow->src), NIPQUAD(flow->dst));
+ }
+ #endif
+ if (flow->l4_protocol == IPPROTO_TCP) {
+ if (AF_TRUE == flow->http.match) {
+ printk("-------------------http protocol-------------------------\n");
+ printk("protocol:TCP , sport: %-8d, dport: %-8d, data_len: %-8d\n",
+ flow->sport, flow->dport, flow->l4_len);
+ dump_http_flow_info(&flow->http);
+ }
+ if (AF_TRUE == flow->https.match) {
+ dump_https_flow_info(&flow->https);
+ }
+ }
+ else if (flow->l4_protocol == IPPROTO_UDP) {
+ // printk("protocol:UDP ,sport: %-8d, dport: %-8d, data_len: %-8d\n",
+ // flow->sport, flow->dport, flow->l4_len);
+ }
+ else {
+ return;
+ }
+}
+#endif
+int af_match_by_pos(flow_info_t *flow, af_feature_node_t *node)
+{
+ // match pos
+ int i;
+ unsigned int pos = 0;
+
+ if (!flow || !node)
+ return AF_FALSE;
+ //printk("pos_num = %d\n", node->pos_num);
+ if (node->pos_num > 0) {
+ for (i = 0;i < node->pos_num; i++){
+ // -1
+ if(node->pos_info[i].pos < 0) {
+ pos = flow->l4_len + node->pos_info[i].pos;
+ }
+ else{
+ pos = node->pos_info[i].pos;
+ }
+ if (pos >= flow->l4_len){
+ // AF_ERROR("pos is invalid, pos = %d, l4_len = %d\n", pos, flow->l4_len);
+ return AF_FALSE;
+ }
+ //printk("pos = %d, i = %d, l4_len = %d\n", pos, i, flow->l4_len);
+
+ if (flow->l4_data[pos] != node->pos_info[i].value){
+ // if (i > 0)
+ // printk("\n");
+ return AF_FALSE;
+ }
+ //if (i > 1)
+ //printk("match (%d:%02x) -->", node->pos_info[i].pos, node->pos_info[i].value);
+ }
+ AF_DEBUG("match by pos, appid=%d\n", node->app_id);
+ return AF_TRUE;
+ }
+ return AF_FALSE;
+}
+
+int af_match_by_url(flow_info_t *flow, af_feature_node_t *node)
+{
+ char reg_url_buf[MAX_URL_MATCH_LEN] = {0};
+
+ if (!flow || !node)
+ return AF_FALSE;
+ // match host or https url
+ if (flow->https.match == AF_TRUE && flow->https.url_pos) {
+ if (flow->https.url_len >= MAX_URL_MATCH_LEN)
+ strncpy(reg_url_buf, flow->https.url_pos, MAX_URL_MATCH_LEN - 1);
+ else
+ strncpy(reg_url_buf, flow->https.url_pos, flow->https.url_len);
+ }
+ else if (flow->http.match == AF_TRUE && flow->http.host_pos) {
+ if (flow->http.host_len >= MAX_URL_MATCH_LEN)
+ strncpy(reg_url_buf, flow->http.host_pos, MAX_URL_MATCH_LEN - 1);
+ else
+ strncpy(reg_url_buf, flow->http.host_pos, flow->http.host_len);
+ }
+ if (strlen(reg_url_buf) > 0 && strlen(node->host_url) > 0
+ && regexp_match(node->host_url, reg_url_buf)){
+ AF_DEBUG("match url:%s reg = %s, appid=%d\n",
+ reg_url_buf, node->host_url, node->app_id);
+ return AF_TRUE;
+ }
+
+ // match request url
+ if (flow->http.match == AF_TRUE && flow->http.url_pos) {
+ memset(reg_url_buf, 0x0, sizeof(reg_url_buf));
+ if (flow->http.url_len >= MAX_URL_MATCH_LEN)
+ strncpy(reg_url_buf, flow->http.url_pos, MAX_URL_MATCH_LEN - 1);
+ else
+ strncpy(reg_url_buf, flow->http.url_pos, flow->http.url_len);
+ if(strlen(reg_url_buf) > 0 && strlen(node->request_url)
+ && regexp_match(node->request_url, reg_url_buf)){
+ AF_DEBUG("match request:%s reg:%s appid=%d\n",
+ reg_url_buf, node->request_url, node->app_id);
+ return AF_TRUE;
+ }
+ }
+ return AF_FALSE;
+}
+
+int af_match_one(flow_info_t *flow, af_feature_node_t *node)
+{
+ int ret = AF_FALSE;
+ if (!flow || !node){
+ AF_ERROR("node or flow is NULL\n");
+ return AF_FALSE;
+ }
+
+ if (flow->l4_len == 0)
+ return AF_FALSE;
+
+ // ƥ˿
+ if (node->sport != 0 && flow->sport != node->sport ){
+ return AF_FALSE;
+ }
+
+ if (node->dport != 0 && flow->dport != node->dport) {
+ return AF_FALSE;
+ }
+
+ if (strlen(node->request_url) > 0 ||
+ strlen(node->host_url) > 0){
+ ret = af_match_by_url(flow, node);
+ }
+ else if (node->pos_num > 0){
+ ret = af_match_by_pos(flow, node);
+ }
+ else{
+ AF_DEBUG("node is empty, match sport:%d,dport:%d, appid = %d\n",
+ node->sport, node->dport, node->app_id);
+ return AF_TRUE;
+ }
+ //printk("sport = %d, dport = %d, node->sport:%d, node->dport:%d,ret = %d\n",
+ // flow->sport, flow->dport, node->sport, node->dport, ret);
+ return ret;
+}
+
+int app_filter_match(flow_info_t *flow)
+{
+ af_feature_node_t *n,*node;
+ feature_list_read_lock();
+ if(!list_empty(&af_feature_head)) {
+ list_for_each_entry_safe(node, n, &af_feature_head, head) {
+ if(af_match_one(flow, node))
+ {
+ if (af_get_app_status(node->app_id)){
+ AF_DEBUG("drop appid = %d\n", node->app_id);
+ feature_list_read_unlock();
+ return AF_TRUE;
+ }
+ else {
+ feature_list_read_unlock();
+ return AF_FALSE;
+ }
+ }
+
+
+ }
+ }
+ feature_list_read_unlock();
+ return AF_FALSE;
+}
+
+
+/* netfilterעĹ */
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
+static u_int32_t app_filter_hook(void *priv,
+ struct sk_buff *skb,
+ const struct nf_hook_state *state) {
+#else
+static u_int32_t app_filter_hook(unsigned int hook,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *)){
+#endif
+// 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) {
+ //AF_ERROR("ct is null\n");
+ return NF_ACCEPT;
+ }
+ flow_info_t flow;
+ memset((char *)&flow, 0x0, sizeof(flow_info_t));
+ parse_flow_base(skb, &flow);
+ parse_http_proto(&flow);
+ parse_https_proto(&flow);
+ //dump_flow_info(&flow);
+ if (app_filter_match(&flow))
+ return NF_DROP;
+ return NF_ACCEPT;
+}
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
+static struct nf_hook_ops app_filter_ops[] __read_mostly = {
+ {
+ .hook = app_filter_hook,
+ .pf = PF_INET,
+ .hooknum = NF_INET_FORWARD,
+ .priority = NF_IP_PRI_MANGLE + 1,
+ },
+};
+#else
+static struct nf_hook_ops app_filter_ops[] __read_mostly = {
+ {
+ .hook = app_filter_hook,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_INET_FORWARD,
+ .priority = NF_IP_PRI_MANGLE + 1,
+ },
+};
+#endif
+
+
+#include "cJSON.h"
+void TEST_cJSON(void)
+{
+ cJSON * root = NULL;
+ char *out = NULL;
+ root = cJSON_CreateObject();
+ if (!root) {
+ AF_ERROR("create obj failed\n");
+ return;
+ }
+ cJSON_AddNumberToObject(root, "id", 123);
+ cJSON_AddStringToObject(root, "name", "derry");
+ out = cJSON_Print(root);
+ printk("out = %s\n", out);
+ cJSON_Delete(root);
+ kfree(out);
+}
+
+/*
+ ģʼ
+*/
+static int __init app_filter_init(void)
+{
+ AF_INFO("appfilter version:"AF_VERSION"\n");
+ AF_DEBUG("app filter module init\n");
+ //TEST_regexp();
+ af_register_dev();
+ af_init_app_status();
+ load_feature_config();
+// show_feature_list();
+// TEST_cJSON();
+#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");
+ return 0;
+}
+
+/*
+ ģ˳
+*/
+static void app_filter_fini(void)
+{
+ AF_DEBUG("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
+ nf_unregister_hooks(app_filter_ops, ARRAY_SIZE(app_filter_ops));
+#endif
+
+ af_clean_feature_list();
+ af_unregister_dev();
+ return ;
+}
+
+module_init(app_filter_init);
+module_exit(app_filter_fini);
+
diff --git a/package/cnsztl/oaf/src/app_filter.h b/package/cnsztl/oaf/src/app_filter.h
new file mode 100755
index 0000000000..f9be684540
--- /dev/null
+++ b/package/cnsztl/oaf/src/app_filter.h
@@ -0,0 +1,68 @@
+#ifndef APP_FILTER_H
+#define APP_FILTER_H
+#define AF_DEBUG if(0) printk
+#define AF_ERROR printk
+#define AF_INFO printk
+#define AF_VERSION "1.0.1"
+//#define CONFIG_KERNEL_FUNC_TEST 1
+
+#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 AF_TRUE 1
+#define AF_FALSE 0
+
+#define AF_APP_TYPE(a) (a) / 1000
+#define AF_APP_ID(a) (a) % 1000
+
+#define HTTPS_URL_OFFSET 9
+#define HTTPS_LEN_OFFSET 7
+
+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 flow_info{
+ struct nf_conn *ct; // Ӹָ
+ u_int32_t src;
+ u_int32_t dst;
+ 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;
+}flow_info_t;
+
+int af_register_dev(void);
+void af_unregister_dev(void);
+void af_init_app_status(void);
+int af_get_app_status(int appid);
+int regexp_match(char *reg, char *text);
+
+#endif
diff --git a/package/cnsztl/oaf/src/app_filter_config.c b/package/cnsztl/oaf/src/app_filter_config.c
new file mode 100755
index 0000000000..1bd6f7d31c
--- /dev/null
+++ b/package/cnsztl/oaf/src/app_filter_config.c
@@ -0,0 +1,293 @@
+
+/*
+ author: destan19@126.com
+ Źں: OpenWrt
+ date:2019/1/10
+*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "cJSON.h"
+#include "app_filter.h"
+#include "af_utils.h"
+#define AF_MAX_APP_TYPE_NUM 16
+#define AF_MAX_APP_NUM 256
+#define AF_DEV_NAME "appfilter"
+
+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);
+
+
+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];
+};
+
+enum AF_CONFIG_CMD{
+ AF_CMD_ADD_APPID = 1,
+ AF_CMD_DEL_APPID,
+ AF_CMD_CLEAN_APPID,
+};
+
+char g_app_id_array[AF_MAX_APP_TYPE_NUM][AF_MAX_APP_NUM] = {0};
+
+
+void af_show_app_status(void)
+{
+ int i, j;
+ printk("#########show app status##########\n");
+ for (i = 0; i < AF_MAX_APP_TYPE_NUM; i++) {
+ for (j = 0; j < AF_MAX_APP_NUM; j++) {
+
+ af_rule_read_lock();
+ if (g_app_id_array[i][j] == AF_TRUE) {
+ printk("%d, %d\n", i, j);
+ }
+ af_rule_read_unlock();
+ }
+ }
+
+ printk("\n\n\n");
+}
+
+int af_change_app_status(cJSON * data_obj, int status)
+{
+ int i;
+ int id;
+ int type;
+ if (!data_obj) {
+ AF_ERROR("data obj is null\n");
+ return -1;
+ }
+ cJSON *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){
+ AF_ERROR("appid obj is null\n");
+ return -1;
+ }
+ id = AF_APP_ID(appid_obj->valueint);
+ type = AF_APP_TYPE(appid_obj->valueint);
+ AF_DEBUG("appid:%d, type = %d, id = %d\n", appid_obj->valueint, type, id);
+
+ 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;
+}
+/*
+add:
+{
+ "op":1,
+ "data"{
+ "apps":[]
+ }
+}
+clean
+{
+ "op":3,
+}
+
+*/
+int af_config_handle(char *config, unsigned int len)
+{
+ cJSON * config_obj = NULL;
+ cJSON * cmd_obj = NULL;
+ cJSON * data_obj = NULL;
+ if (!config || len == 0) {
+ AF_ERROR("config or len is invalid\n");
+ return -1;
+ }
+ 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");
+ return -1;
+ }
+ data_obj = cJSON_GetObjectItem(config_obj, "data");
+
+ switch(cmd_obj->valueint) {
+ case AF_CMD_ADD_APPID:
+ if (!data_obj)
+ break;
+ af_change_app_status(data_obj, AF_TRUE);
+ break;
+ case AF_CMD_DEL_APPID:
+ if (!data_obj)
+ break;
+ af_change_app_status(data_obj, AF_FALSE);
+ break;
+ case AF_CMD_CLEAN_APPID:
+ af_init_app_status();
+ break;
+ default:
+ AF_ERROR("invalid cmd %d\n", cmd_obj->valueint);
+ return -1;
+ }
+ af_show_app_status();
+ return 0;
+
+}
+
+
+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;
+ printk("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)) {
+ printk("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;
+ }
+
+ g_af_dev.c= class_create(THIS_MODULE, AF_DEV_NAME);
+ 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;
+ }
+ printk("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);
+
+ printk("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);
+ printk("unregister char dev....ok\n");
+}
+
diff --git a/package/cnsztl/oaf/src/app_filter_feature.c b/package/cnsztl/oaf/src/app_filter_feature.c
new file mode 100755
index 0000000000..eec046306a
--- /dev/null
+++ b/package/cnsztl/oaf/src/app_filter_feature.c
@@ -0,0 +1,849 @@
+
+/*
+ author: destan19@126.com
+ Źں: wifi
+ date:2019/1/10
+*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "app_filter.h"
+#include "af_utils.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("destan19@126.com");
+MODULE_DESCRIPTION("app filter module");
+MODULE_VERSION("1.0.2");
+struct list_head af_feature_head = LIST_HEAD_INIT(af_feature_head);
+#define AF_FEATURE_CONFIG_FILE "/etc/appfilter/feature.cfg"
+#define AF_DEV_NAME "appfilter"
+
+DEFINE_RWLOCK(af_feature_lock);
+
+#define feature_list_read_lock() read_lock_bh(&af_feature_lock);
+#define feature_list_read_unlock() read_unlock_bh(&af_feature_lock);
+#define feature_list_write_lock() write_lock_bh(&af_feature_lock);
+#define feature_list_write_unlock() write_unlock_bh(&af_feature_lock);
+
+#define MIN_HTTP_DATA_LEN 16
+#define MAX_APP_NAME_LEN 64
+#define MAX_FEATURE_NUM_PER_APP 16
+#define MAX_FEATURE_STR_LEN 128
+#define MAX_HOST_URL_LEN 128
+#define MAX_REQUEST_URL_LEN 128
+#define MAX_MATH_DATA_LEN 1600
+#define MAX_FEATURE_BITS 16
+#define MAX_POS_INFO_PER_FEATURE 16
+#define MAX_FEATURE_LINE_LEN 256
+#define MIN_FEATURE_LINE_LEN 16
+
+typedef struct af_pos_info{
+ int pos;
+ unsigned char value;
+}af_pos_info_t;
+
+typedef struct af_feature_node{
+ struct list_head head;
+ int app_id;
+ char app_name[MAX_APP_NAME_LEN];
+ char feature_str[MAX_FEATURE_NUM_PER_APP][MAX_FEATURE_STR_LEN];
+ int proto;
+ int dport;
+ char host_url[MAX_HOST_URL_LEN];
+ char request_url[MAX_REQUEST_URL_LEN];
+ int pos_num;
+ af_pos_info_t pos_info[MAX_POS_INFO_PER_FEATURE];
+}af_feature_node_t;
+
+struct af_config_dev {
+ dev_t id;
+ struct cdev char_dev;
+ struct class *c;
+};
+struct af_config_dev g_af_dev;
+
+static void show_feature_list(void)
+{
+ af_feature_node_t *n,*node;
+ unsigned int count = 0;
+ feature_list_read_lock();
+ if(!list_empty(&af_feature_head)) { // handle qos
+ list_for_each_entry_safe(node, n, &af_feature_head, head) {
+ count ++;
+ printk("[%d] id=%d appname:%s, dport:%d, host:%s, request:%s\n",
+ count,
+ node->app_id, node->app_name,
+ node->dport,node->host_url, node->request_url);
+ int i;
+ for (i = 0;i < node->pos_num;i++){
+ printk("(%d:%x)-->",
+ node->pos_info[i].pos,
+ node->pos_info[i].value);
+
+ }
+ printk("\n----------------------------------------\n\n\n");
+ }
+ }
+ feature_list_read_unlock();
+}
+
+static af_feature_node_t* af_find_feature(char *app_id)
+{
+ af_feature_node_t *node;
+ feature_list_read_lock();
+
+ if (!list_empty(&af_feature_head)) {
+ list_for_each_entry(node, &af_feature_head, head) {
+ if (node->app_id == app_id){
+ feature_list_read_unlock();
+ return node;
+ }
+ }
+ }
+ feature_list_read_unlock();
+ return NULL;
+}
+enum AF_FEATURE_PARAM_INDEX{
+ AF_PROTO_PARAM_INDEX,
+ AF_DST_PORT_PARAM_INDEX,
+ AF_HOST_URL_PARAM_INDEX,
+ AF_REQUEST_URL_PARAM_INDEX,
+ AF_DICT_PARAM_INDEX,
+};
+
+int __add_app_feature(int appid,
+ char *name,
+ int proto,
+ int dst_port,
+ char *host_url,
+ char *request_url,
+ char *dict)
+{
+ af_feature_node_t *node = NULL;
+ node = kzalloc(sizeof(af_feature_node_t), GFP_KERNEL);
+ if (node == NULL) {
+ printk("malloc feature memory error\n");
+ return -1;
+ }
+ else {
+ node->app_id = appid;
+ strcpy(node->app_name, name);
+ node->proto = proto;
+ node->dport = dst_port;
+ strcpy(node->host_url, host_url);
+ strcpy(node->request_url, request_url);
+ // 00:0a-01:11
+ char *p = dict;
+ char *begin = dict;
+ char pos[32] = {0};
+ int index = 0;
+ int value = 0;
+
+ while (*p++) {
+ if (*p == '-'){
+ memset(pos, 0x0, sizeof(pos));
+ strncpy(pos, begin, p - begin);
+ k_sscanf(pos, "%d:%x",&index, &value);
+ begin = p + 1;
+ node->pos_info[node->pos_num].pos = index;
+ node->pos_info[node->pos_num].value = value;
+ node->pos_num++;
+ }
+ }
+
+ if (begin != dict) {
+ strncpy(pos, begin, p - begin);
+ k_sscanf(pos, "%d:%x",&index, &value);
+ node->pos_info[node->pos_num].pos = index;
+ node->pos_info[node->pos_num].value = value;
+ node->pos_num++;
+ }
+ feature_list_write_lock();
+ list_add(&(node->head), &af_feature_head);
+ feature_list_write_unlock();
+ }
+}
+
+int add_app_feature(int appid, char *name, char *feature)
+{
+ char proto_str[16] = {0};
+ int port_str[16] = {0};
+ char host_url[32] = {0};
+ char request_url[128] = {0};
+ char dict[128] = {0};
+ int proto = IPPROTO_TCP;
+ if (!name || !feature) {
+ printk("error, name or feature is null\n");
+ return -1;
+ }
+ // tcp;8000;www.sina.com;0:get_name;00:0a-01:11
+
+ char *p = feature;
+ char *begin = feature;
+ int param_num = 0;
+ while(*p++) {
+ if (*p != ';')
+ continue;
+
+ switch(param_num){
+ case AF_PROTO_PARAM_INDEX:
+ strncpy(proto_str, begin, p - begin);
+ break;
+
+ case AF_DST_PORT_PARAM_INDEX:
+ strncpy(port_str, begin, p - begin);
+ break;
+
+ case AF_HOST_URL_PARAM_INDEX:
+ strncpy(host_url, begin, p - begin);
+ break;
+
+ case AF_REQUEST_URL_PARAM_INDEX:
+ strncpy(request_url, begin, p - begin);
+ break;
+ }
+ param_num ++;
+ begin = p + 1;
+ }
+ if (AF_DICT_PARAM_INDEX != param_num) {
+ printk("invalid feature:%s\n", feature);
+ return -1;
+ }
+ strncpy(dict, begin, p - begin);
+
+ //sscanf(feature, "%[^;];%d;%[^;];%[^;];%s", proto, &dst_port, host, url, dict);
+ //printk("proto = %s, port = %s, host = %s, url = %s, dict = %s\n",
+ // proto_str, port_str, host_url, request_url, dict);
+ if (0 == strcmp(proto_str, "tcp"))
+ proto = IPPROTO_TCP;
+ else if (0 == strcmp(proto_str, "udp"))
+ proto = IPPROTO_UDP;
+ else {
+ printk("proto %s is not support\n", proto_str);
+ return -1;
+ }
+ int dst_port = 0;
+ sscanf(port_str, "%d", &dst_port);
+
+ __add_app_feature(appid,
+ name,
+ proto,
+ dst_port,
+ host_url,
+ request_url,
+ dict);
+
+ return 0;
+}
+
+
+
+
+void af_init_feature(char *feature_str)
+{
+// char * qq_config_str = "1001 qq :[tcp;8000;r:www.baidu.com;4:get_status;00:0a-01:11]";
+// char * qq_config_str = "1001 qq:[tcp;443;baidu.com;get_status;00:0a-01:11,tcp;8000;www.sina.com;0:22222get;00:0a-01:11,tcp;8000;www.sina.com;hao123;00:0a-01:11-02:ff]";
+// char * qq_config_str = "1001 qq:[tcp;443;r:www.baidu.com;4:get_status;00:0a-01:11,tcp;8000;www.sina.com;0:22222get;00:0a-01:11,tcp;8000;www.sina.com;hao123;00:0a-01:11-02:ff]";
+ int app_id;
+ char app_name[128] = {0};
+ char feature_buf[MAX_FEATURE_LINE_LEN] = {0};
+
+ printk("feature_str=%s\n",feature_str);
+ k_sscanf(feature_str, "%d%[^:]", &app_id, app_name);
+ printk("id = %d, name = %s\n",app_id, app_name);
+
+ char *p = feature_str;
+ char *pos = NULL;
+ int len = 0;
+ while(*p++) {
+ if (*p == '['){
+ pos = p + 1;
+ continue;
+ }
+ if (*p == ']' && pos != NULL) {
+ len = p - pos;
+ }
+ }
+
+ if (pos && len )
+ strncpy(feature_buf, pos, len);
+ char feature[MAX_FEATURE_STR_LEN];;
+ int i;
+ memset(feature, 0x0, sizeof(feature));
+ p = feature_buf;
+ char *begin = feature_buf;
+
+ while(*p++){
+ if (*p == ',') {
+ memset(feature, 0x0, sizeof(feature));
+ strncpy((char *)feature, begin, p - begin);
+
+ add_app_feature(app_id, app_name, feature);
+ begin = p + 1;
+ }
+ }
+ if (p != begin){
+ memset(feature, 0x0, sizeof(feature));
+ strncpy((char *)feature, begin, p - begin);
+ add_app_feature(app_id, app_name, feature);
+ }
+}
+
+void load_feature_buf_from_file(char **config_buf)
+{
+ struct inode *inode;
+ struct file *fp;
+ mm_segment_t fs;
+ off_t size;
+
+ fp = filp_open(AF_FEATURE_CONFIG_FILE, O_RDONLY, 0);
+ if(IS_ERR(fp)) {
+ printk("open feature file failed\n");
+ return -1;
+ }
+
+ inode = fp->f_dentry->d_inode;
+ size = inode->i_size;
+ printk("file size: %d\n", size);
+ *config_buf = (char *) kzalloc( sizeof(char) * size, GFP_KERNEL);
+ if(NULL == *config_buf ) {
+ printk("alloc buf fail\n");
+ filp_close(fp, NULL);
+ return -1;
+ }
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ fp->f_op->read(fp, *config_buf, size, &(fp->f_pos));
+ set_fs(fs);
+ filp_close(fp, NULL);
+ return size;
+}
+
+void load_feature_config(void)
+{
+ printk("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");
+ return;
+ }
+
+ printk("feature_buf = %s\n", feature_buf);
+ char *p;
+ char *begin;
+ p = begin = feature_buf;
+ char line[MAX_FEATURE_LINE_LEN] = {0};
+ while(*p++) {
+ if (*p == '\n'){
+ if (p - begin < MIN_FEATURE_LINE_LEN || p - begin > MAX_FEATURE_LINE_LEN ) {
+ begin = p + 1;
+ continue;
+ }
+ memset(line, 0x0, sizeof(line));
+ strncpy(line, begin, p - begin);
+ af_init_feature(line);
+ begin = p + 1;
+ }
+ }
+ if (p != begin) {
+ if (p - begin < MIN_FEATURE_LINE_LEN || p - begin > MAX_FEATURE_LINE_LEN )
+ return;
+ memset(line, 0x0, sizeof(line));
+ strncpy(line, begin, p - begin);
+ af_init_feature(line);
+ begin = p + 1;
+ }
+ if (feature_buf)
+ kfree(feature_buf);
+}
+
+static void af_clean_feature_list()
+{
+ af_feature_node_t *n,*node;
+ feature_list_write_lock();
+ while(!list_empty(&af_feature_head)) {
+ node = list_first_entry(&af_feature_head, af_feature_node_t, head);
+ list_del(&(node->head));
+ kfree(node);
+ }
+ feature_list_write_unlock();
+}
+
+
+int parse_flow_base(struct sk_buff *skb, flow_info_t *flow)
+{
+ struct tcphdr * tcph = NULL;
+ struct udphdr * udph = NULL;
+ struct nf_conn *ct = NULL;
+ struct iphdr *iph = NULL;
+ if (!skb) {
+ return -1;
+ }
+ ct = (struct nf_conn *)skb->nfct;
+ if (!ct) {
+ return -1;
+ }
+ iph = ip_hdr(skb);
+ if ( !iph ) {
+ return -1;
+ }
+ flow->ct = ct;
+ flow->src = iph->saddr;
+ flow->dst = iph->daddr;
+ flow->l4_protocol = iph->protocol;
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ tcph = (struct tcphdr *)(iph + 1);
+ flow->l4_data = skb->data + iph->ihl * 4 + tcph->doff * 4;
+ flow->l4_len = ntohs(iph->tot_len) - iph->ihl * 4 - tcph->doff * 4;
+ flow->dport = htons(tcph->dest);
+ flow->sport = htons(tcph->source);
+ break;
+ 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;
+ case IPPROTO_ICMP:
+ break;
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+
+/*
+ desc: https urlϢ浽flow
+ return:
+ -1: error
+ 0: match
+ author: Derry
+ Date:2018/12/19
+*/
+int parse_https_proto(flow_info_t *flow) {
+ int i ;
+ short url_len = 0 ;
+ char * p = flow->l4_data;
+ int data_len = flow->l4_len;
+
+ if (NULL == flow) {
+ AF_ERROR("flow is NULL\n");
+ return -1;
+ }
+ if (NULL == p || data_len == 0) {
+ return -1;
+ }
+ if (!(p[0] == 0x16 && p[1] == 0x03 && p[2] == 0x01))
+ return -1;
+
+ for(i = 0; i < data_len; i++) {
+ if(i + HTTPS_URL_OFFSET >= data_len) {
+ return -1;
+ }
+
+ if(p[i] == 0x0 && p[i + 1] == 0x0 && p[i + 2] == 0x0 && p[i + 3] != 0x0) {
+ // 2 bytes
+ memcpy(&url_len , p + i + HTTPS_LEN_OFFSET, 2);
+ if(ntohs(url_len) <= 0 || ntohs(url_len) > data_len) {
+ continue ;
+ }
+
+ if(i + HTTPS_URL_OFFSET + ntohs(url_len) < data_len) {
+ //dump_hex("https hex", p, data_len);
+ flow->https.match = AF_TRUE;
+ flow->https.url_pos = p + i + HTTPS_URL_OFFSET;
+ //dump_str("https url", flow->https.url_pos, 5);
+ flow->https.url_len = ntohs(url_len);
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+
+void parse_http_proto(flow_info_t *flow)
+{
+ if (!flow) {
+ AF_ERROR("flow is null\n");
+ return;
+ }
+ if (flow->l4_protocol != IPPROTO_TCP) {
+ return;
+ }
+
+ int i = 0;
+ int start = 0;
+ char *data = flow->l4_data;
+ int data_len = flow->l4_len;
+ if (data_len < MIN_HTTP_DATA_LEN) {
+ return;
+ }
+ if (flow->sport != 80 && flow->dport != 80)
+ return;
+ for (i = 0; i < data_len - 4; i++) {
+ if (data[i] == 0x0d && data[i + 1] == 0x0a){
+ if (0 == memcmp(&data[start], "POST ", 5)) {
+ flow->http.match = AF_TRUE;
+ flow->http.method = HTTP_METHOD_POST;
+ flow->http.url_pos = data + start + 5;
+ flow->http.url_len = i - start - 5;
+ //dump_str("get request", flow->http.url_pos, flow->http.url_len);
+ }
+ else if(0 == memcmp(&data[start], "GET ", 4)) {
+ flow->http.match = AF_TRUE;
+ flow->http.method = HTTP_METHOD_GET;
+ flow->http.url_pos = data + start + 4;
+ flow->http.url_len = i - start - 4;
+ //dump_str("post request", flow->http.url_pos, flow->http.url_len);
+ }
+ else if (0 == memcmp(&data[start], "Host: ", 6) ){
+ flow->http.host_pos = data + start + 6;
+ flow->http.host_len = i - start - 6;
+ //dump_str("host ", flow->http.host_pos, flow->http.host_len);
+ }
+ // жhttpͷ
+ if (data[i + 2] == 0x0d && data[i + 3] == 0x0a){
+ flow->http.data_pos = data + i + 4;
+ flow->http.data_len = data_len - i - 4;
+ break;
+ }
+ // 0x0d 0x0a
+ start = i + 2;
+ }
+ }
+}
+
+static void dump_http_flow_info(http_proto_t *http) {
+ if (!http) {
+ AF_ERROR("http ptr is NULL\n");
+ return ;
+ }
+ if (!http->match)
+ return;
+ if (http->method == HTTP_METHOD_GET){
+ printk("Http method: "HTTP_GET_METHOD_STR"\n");
+ }
+ else if (http->method == HTTP_METHOD_POST) {
+ printk("Http method: "HTTP_POST_METHOD_STR"\n");
+ }
+ if (http->url_len > 0 && http->url_pos){
+ dump_str("Request url", http->url_pos, http->url_len);
+ }
+
+ if (http->host_len > 0 && http->host_pos){
+ dump_str("Host", http->host_pos, http->host_len);
+ }
+
+ printk("--------------------------------------------------------\n\n\n");
+}
+
+static void dump_https_flow_info(https_proto_t *https) {
+ if (!https) {
+ AF_ERROR("https ptr is NULL\n");
+ return ;
+ }
+ if (!https->match)
+ return;
+
+
+ if (https->url_len > 0 && https->url_pos){
+ printk("url len = %d\n",https->url_len);
+ dump_str("https server name", https->url_pos, https->url_len);
+ }
+
+ printk("--------------------------------------------------------\n\n\n");
+}
+
+static void dump_flow_info(flow_info_t *flow)
+{
+ if (!flow) {
+ AF_ERROR("flow is null\n");
+ return;
+ }
+ #if 0
+ if (check_local_network_ip(ntohl(flow->src))) {
+ printk("src ip(inner net):"NIPQUAD_FMT", dst ip = "NIPQUAD_FMT"\n", NIPQUAD(flow->src), NIPQUAD(flow->dst));
+ }
+ else {
+ printk("src ip(outer net):"NIPQUAD_FMT", dst ip = "NIPQUAD_FMT"\n", NIPQUAD(flow->src), NIPQUAD(flow->dst));
+ }
+ #endif
+ if (flow->l4_protocol == IPPROTO_TCP) {
+ if (AF_TRUE == flow->http.match) {
+ printk("-------------------http protocol-------------------------\n");
+ printk("protocol:TCP , sport: %-8d, dport: %-8d, data_len: %-8d\n",
+ flow->sport, flow->dport, flow->l4_len);
+ dump_http_flow_info(&flow->http);
+ }
+ if (AF_TRUE == flow->https.match) {
+ dump_https_flow_info(&flow->https);
+ }
+ }
+ else if (flow->l4_protocol == IPPROTO_UDP) {
+ // printk("protocol:UDP ,sport: %-8d, dport: %-8d, data_len: %-8d\n",
+ // flow->sport, flow->dport, flow->l4_len);
+ }
+ else {
+ return;
+ }
+}
+
+int app_filter_match(flow_info_t *flow)
+{
+ af_feature_node_t *n,*node;
+ feature_list_read_lock();
+ if(!list_empty(&af_feature_head)) {
+ list_for_each_entry_safe(node, n, &af_feature_head, head) {
+
+ if (flow->https.match == AF_TRUE) {
+ if (flow->https.url_pos &&
+ strnstr(flow->https.url_pos, node->host_url, flow->https.url_len)){
+
+ dump_str("Drop https url ",flow->https.url_pos, flow->https.url_len);
+
+ feature_list_read_unlock();
+ return 1;
+ }
+ }
+ }
+ }
+ feature_list_read_unlock();
+ return 0;
+}
+
+
+/* netfilterעĹ */
+static u_int32_t app_filter_hook(unsigned int hook,
+ struct sk_buff *pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct nf_conn *ct = (struct nf_conn *)pskb->nfct;
+
+ if (ct == NULL) {
+ AF_ERROR("ct is null\n");
+ return NF_ACCEPT;
+ }
+ flow_info_t flow;
+ memset((char *)&flow, 0x0, sizeof(flow_info_t));
+ parse_flow_base(pskb, &flow);
+ parse_http_proto(&flow);
+ parse_https_proto(&flow);
+ dump_flow_info(&flow);
+ // todo: match url rules
+ // this is example
+ #if 0
+ if (flow.http.match == AF_TRUE) {
+ if (flow.http.host_pos &&
+ strnstr(flow.http.host_pos, "sohu", flow.http.host_len)){
+
+ dump_str("Drop http url ",flow.http.host_pos, flow.http.host_len);
+ return NF_DROP;
+ }
+ }
+ if (flow.https.match == AF_TRUE) {
+ if (flow.https.url_pos &&
+ strnstr(flow.https.url_pos, "hao123", flow.https.url_len)){
+
+ dump_str("Drop https url ",flow.https.url_pos, flow.https.url_len);
+ return NF_DROP;
+ }
+ }
+ #endif
+ if (app_filter_match(&flow))
+ return NF_DROP;
+ return NF_ACCEPT;
+}
+
+
+static struct nf_hook_ops app_filter_ops[] __read_mostly = {
+ {
+ .hook = app_filter_hook,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_INET_FORWARD,
+ .priority = NF_IP_PRI_MANGLE + 10,
+ },
+};
+
+#include "cJSON.h"
+void TEST_cJSON(void)
+{
+ cJSON * root = NULL;
+ root = cJSON_CreateObject();
+ if (!root) {
+ AF_ERROR("create obj failed\n");
+ return;
+ }
+ cJSON_AddNumberToObject(root, "id", 123);
+ cJSON_AddStringToObject(root, "name", "derry");
+ char * out = cJSON_Print(root);
+ printk("out = %s\n", out);
+ cJSON_Delete(root);
+ kfree(out);
+}
+
+static struct mutex af_cdev_mutex;
+
+struct af_cdev_file {
+ size_t size;
+ char buf[256 << 10];
+};
+
+static int af_cdev_open(struct inode *inode, struct file *filp)
+{
+ struct af_cdev_file *file;
+ printk("cdev open\n");
+
+ 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;
+ int ret;
+ printk("config size: %d,data = %s\n", (int)file->size, file->buf);
+ 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;
+ printk("cdev write\n");
+ if (file->size + count > sizeof(file->buf)) {
+ printk("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,
+};
+
+void 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;
+ }
+
+ g_af_dev.c= class_create(THIS_MODULE, AF_DEV_NAME);
+ 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;
+ }
+ printk("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);
+
+ printk("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);
+ printk("unregister char dev....ok\n");
+}
+
+extern void TEST_regexp();
+/*
+ ģʼ
+*/
+static int __init app_filter_init(void)
+{
+ AF_INFO("appfilter version:"AF_VERSION"\n");
+ AF_DEBUG("app filter module init\n");
+ //TEST_regexp();
+ af_register_dev();
+ load_feature_config();
+ show_feature_list();
+ TEST_cJSON();
+ nf_register_hooks(app_filter_ops, ARRAY_SIZE(app_filter_ops));
+ printk("init app filter ........ok\n");
+ return 0;
+}
+/*
+ ģ˳
+*/
+static void app_filter_fini(void)
+{
+ AF_DEBUG("app filter module exit\n");
+ nf_unregister_hooks(app_filter_ops, ARRAY_SIZE(app_filter_ops));
+ af_clean_feature_list();
+ af_unregister_dev();
+ return ;
+}
+
+
+module_init(app_filter_init);
+module_exit(app_filter_fini);
+
diff --git a/package/cnsztl/oaf/src/cJSON.c b/package/cnsztl/oaf/src/cJSON.c
new file mode 100755
index 0000000000..8b224533cd
--- /dev/null
+++ b/package/cnsztl/oaf/src/cJSON.c
@@ -0,0 +1,423 @@
+/*
+ Copyright (c) 2009 Dave Gamble
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+// cJSON
+// JSON parser in C.
+
+#if 0
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#include "cJSON.h"
+
+#include
+#include
+
+#if 0
+#if defined(WINDOWS) || defined(__WIN32__) || defined(WIN32) || defined(_WIN32)
+#define strcasecmp stricmp
+#define strdup _strdup
+#endif
+#endif
+
+
+#if 0
+static void *(*cJSON_malloc)(size_t sz) = malloc;
+static void *(*cJSON_realloc)(void *ptr, size_t sz) = realloc;
+static void (*cJSON_free)(void *ptr) = free;
+#endif
+
+static void *cJSON_malloc(size_t sz) {
+ return kmalloc(sz, GFP_KERNEL);
+}
+
+static void *cJSON_realloc(void *ptr, size_t sz)
+{
+ return krealloc(ptr, sz, GFP_KERNEL);
+}
+
+static void cJSON_free(void *ptr)
+{
+ kfree(ptr);
+}
+
+static char* cJSON_strdup(const char* str)
+{
+ size_t len;
+ char* copy;
+
+ len = strlen(str) + 1;
+ if (!(copy = (char*)cJSON_malloc(len))) return 0;
+ memcpy(copy,str,len);
+ return copy;
+}
+
+#if 0
+void cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+ if (!hooks) { /* Reset hooks */
+ cJSON_malloc = malloc;
+ cJSON_realloc = realloc;
+ cJSON_free = free;
+ return;
+ }
+
+ cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
+ cJSON_realloc= (hooks->realloc_fn)?hooks->realloc_fn:realloc;
+ cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
+}
+#endif
+
+// Internal constructor.
+static cJSON *cJSON_New_Item(void)
+{
+ cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
+ if (node) memset(node,0,sizeof(cJSON));
+ return node;
+}
+
+// Delete a cJSON structure.
+void cJSON_Delete(cJSON *c)
+{
+ cJSON *next;
+ while (c)
+ {
+ next=c->next;
+ if (c->child) cJSON_Delete(c->child);
+ if (c->valuestring) cJSON_free(c->valuestring);
+ if (c->string) cJSON_free(c->string);
+ cJSON_free(c);
+ c=next;
+ }
+}
+
+
+/* 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,scale=0;int subscale=0,signsubscale=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? */
+ 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;
+ str=(char*)cJSON_malloc(21);
+ if (str)
+ sprintf(str,"%d",item->valueint);
+ return str;
+}
+
+
+// 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)
+{
+ const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc;
+ if (*str!='\"') return 0; // not a string!
+
+ while (*ptr!='\"' && *ptr>31 && ++len) if (*ptr++ == '\\') ptr++; // Skip escaped quotes.
+
+ out=(char*)cJSON_malloc(len+1); // This is how long we need for the string, roughly.
+ if (!out) return 0;
+
+ ptr=str+1;ptr2=out;
+ while (*ptr!='\"' && *ptr>31)
+ {
+ if (*ptr!='\\') *ptr2++=*ptr++;
+ else
+ {
+ ptr++;
+ switch (*ptr)
+ {
+ case 'b': *ptr2++='\b'; break;
+ case 'f': *ptr2++='\f'; break;
+ case 'n': *ptr2++='\n'; break;
+ case 'r': *ptr2++='\r'; break;
+ case 't': *ptr2++='\t'; break;
+ case 'u': // transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY.
+ sscanf(ptr+1,"%4x",&uc); // get the unicode char.
+ len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len;
+
+ switch (len) {
+ case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+ case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+ case 1: *--ptr2 =(uc | firstByteMark[len]);
+ }
+ ptr2+=len;ptr+=4;
+ break;
+ default: *ptr2++=*ptr; break;
+ }
+ ptr++;
+ }
+ }
+ *ptr2=0;
+ if (*ptr=='\"') ptr++;
+ item->valuestring=out;
+ item->type=cJSON_String;
+ 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;
+
+ ptr=str;while (*ptr && ++len) {if (*ptr<32 || *ptr=='\"' || *ptr=='\\') len++;ptr++;}
+
+ out=(char*)cJSON_malloc(len+3);
+ ptr2=out;ptr=str;
+ *ptr2++='\"';
+ while (*ptr)
+ {
+ if (*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
+ else
+ {
+ *ptr2++='\\';
+ switch (*ptr++)
+ {
+ case '\\': *ptr2++='\\'; break;
+ case '\"': *ptr2++='\"'; break;
+ case '\b': *ptr2++='b'; break;
+ case '\f': *ptr2++='f'; break;
+ case '\n': *ptr2++='n'; break;
+ case '\r': *ptr2++='r'; break;
+ case '\t': *ptr2++='t'; break;
+ default: ptr2--; break; // eviscerate with prejudice.
+ }
+ }
+ }
+ *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);
+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 (!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.
+ if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
+ if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
+ if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
+ if (*value=='\"') { return parse_string(item,value); }
+ if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
+ if (*value=='[') { return parse_array(item,value); }
+ if (*value=='{') { return parse_object(item,value); }
+
+ return 0; // failure.
+}
+
+// Render a value to text.
+static char *print_value(cJSON *item,int depth)
+{
+ char *out=0;
+ switch (item->type)
+ {
+ case cJSON_NULL: out=cJSON_strdup("null"); break;
+ case cJSON_False: out=cJSON_strdup("false");break;
+ case cJSON_True: out=cJSON_strdup("true"); break;
+ case cJSON_Number: out=print_number(item);break;
+ case cJSON_String: out=print_string(item);break;
+ case cJSON_Array: out=print_array(item,depth);break;
+ case cJSON_Object: out=print_object(item,depth);break;
+ }
+ return out;
+}
+
+// Build an array from input text.
+static const char *parse_array(cJSON *item,const char *value)
+{
+ cJSON *child;
+ if (*value!='[') return 0; // not an array!
+
+ item->type=cJSON_Array;
+ value=skip(value+1);
+ if (*value==']') return value+1; // empty array.
+
+ item->child=child=cJSON_New_Item();
+ if (!item->child) return 0; // memory fail
+ value=skip(parse_value(child,skip(value))); // skip any spacing, get the value.
+ if (!value) return 0;
+
+ while (*value==',')
+ {
+ cJSON *new_item;
+ if (!(new_item=cJSON_New_Item())) return 0; // memory fail
+ child->next=new_item;new_item->prev=child;child=new_item;
+ value=skip(parse_value(child,skip(value+1)));
+ if (!value) return 0; // memory fail
+ }
+
+ if (*value==']') return value+1; // end of array
+ return 0; // malformed.
+}
+
+// Render an array to text
+static char *print_array(cJSON *item,int depth)
+{
+ char *out,*ptr,*ret;int len=5;
+ cJSON *child=item->child;
+
+ out=(char*)cJSON_malloc(len);*out='[';
+ ptr=out+1;*ptr=0;
+ while (child)
+ {
+ ret=print_value(child,depth+1);
+ if (!ret) {cJSON_free(out);return 0;} // Check for failure!
+ len+=strlen(ret)+3;
+ out=(char*)cJSON_realloc(out,len);
+ ptr=out+strlen(out);
+ ptr+=sprintf(ptr,ret);
+ if (child->next) {*ptr++=',';*ptr++=' ';*ptr=0;}
+ child=child->next;
+ cJSON_free(ret);
+ }
+ *ptr++=']';*ptr++=0;
+ return out;
+}
+
+// Build an object from the text.
+static const char *parse_object(cJSON *item,const char *value)
+{
+ cJSON *child;
+ if (*value!='{') return 0; // not an object!
+
+ item->type=cJSON_Object;
+ value=skip(value+1);
+ if (*value=='}') return value+1; // empty array.
+
+ item->child=child=cJSON_New_Item();
+ value=skip(parse_string(child,skip(value)));
+ if (!value) return 0;
+ child->string=child->valuestring;child->valuestring=0;
+ if (*value!=':') return 0; // fail!
+ value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value.
+ if (!value) return 0;
+
+ while (*value==',')
+ {
+ cJSON *new_item;
+ if (!(new_item=cJSON_New_Item())) return 0; // memory fail
+ child->next=new_item;new_item->prev=child;child=new_item;
+ value=skip(parse_string(child,skip(value+1)));
+ if (!value) return 0;
+ child->string=child->valuestring;child->valuestring=0;
+ if (*value!=':') return 0; // fail!
+ value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value.
+ if (!value) return 0;
+ }
+
+ if (*value=='}') return value+1; // end of array
+ 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;
+ cJSON *child=item->child;
+
+ depth++;len+=depth;out=(char*)cJSON_malloc(len);*out='{';
+ ptr=out+1;*ptr++='\n';*ptr=0;
+ while (child)
+ {
+ str=print_string_ptr(child->string);
+ if (!str) {cJSON_free(out);return 0;}
+ ret=print_value(child,depth);
+ if (!ret) {cJSON_free(str);cJSON_free(out);return 0;} // Check for failure!
+ len+=strlen(ret)+strlen(str)+4+depth;
+ out=(char*)cJSON_realloc(out,len);
+ ptr=out+strlen(out);
+ for (i=0;inext) *ptr++=',';
+ *ptr++='\n';*ptr=0;
+ child=child->next;
+ cJSON_free(str);cJSON_free(ret);
+ }
+ for (i=0;ichild;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;}
+cJSON *cJSON_CreateNumber(int num) {cJSON *item=cJSON_New_Item();item->type=cJSON_Number;item->valueint=(int)num;return item;}
+cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();item->type=cJSON_String;item->valuestring=cJSON_strdup(string);return 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;ichild=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;ichild=n;else suffix_object(p,n);p=n;}return a;}
diff --git a/package/cnsztl/oaf/src/cJSON.h b/package/cnsztl/oaf/src/cJSON.h
new file mode 100755
index 0000000000..e7c794cbb4
--- /dev/null
+++ b/package/cnsztl/oaf/src/cJSON.h
@@ -0,0 +1,94 @@
+/*
+ Copyright (c) 2009 Dave Gamble
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+#include
+
+// cJSON Types:
+#define cJSON_False 0
+#define cJSON_True 1
+#define cJSON_NULL 2
+#define cJSON_Number 3
+#define cJSON_String 4
+#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.
+
+ int type; // The type of the item, as above.
+
+ char *valuestring; // The item's string, if type==cJSON_String
+ int valueint; // The item's number, if type==cJSON_Number
+ char *string; // The item's name string, if this item is the child of, or is in the list of subitems of an object.
+} cJSON;
+
+typedef struct cJSON_Hooks {
+ void *(*malloc_fn)(size_t sz);
+ void *(*realloc_fn)(void *ptr, size_t sz);
+ 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);
+extern cJSON *cJSON_CreateNumber(int num);
+extern cJSON *cJSON_CreateString(const char *string);
+extern cJSON *cJSON_CreateArray(void);
+extern cJSON *cJSON_CreateObject(void);
+
+// These utilities create an Array of count items.
+extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
+
+// Append item to the specified array/object.
+extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
+extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
+
+#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
+#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
+#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
+#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
+#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
+
+#endif
diff --git a/package/cnsztl/oaf/src/regexp.c b/package/cnsztl/oaf/src/regexp.c
new file mode 100755
index 0000000000..bd8ef4a3a7
--- /dev/null
+++ b/package/cnsztl/oaf/src/regexp.c
@@ -0,0 +1,302 @@
+#include
+#include
+#include
+#include
+#include
+//#include "regexp.h"
+
+typedef enum{CHAR, DOT, BEGIN, END, STAR, PLUS, QUES, LIST, TYPENUM}TYPE;
+
+typedef struct RE{
+ TYPE type;
+ int ch;
+ char *ccl;
+ int nccl;
+ struct RE *next;
+}RE;
+
+int match_longest = 0;
+char *match_first = NULL;
+
+
+static void * getmem(size_t size)
+{
+ void *tmp;
+ if((tmp = kmalloc(size, GFP_ATOMIC))==NULL)
+ {
+ printk("malloc failed");
+ return NULL;
+ }
+ return tmp;
+}
+
+static size_t creat_list(char *str, int start, int end)
+{
+ size_t cnt = end - start + 1;
+ for(; start <= end ;start++)
+ *str++ = start;
+ return (cnt > 0)?cnt:0;
+}
+
+static int in_list(char ch, RE *regexp)
+{
+ char *str = regexp->ccl;
+ if(regexp->type != LIST)
+ return 0;
+ for(; *str && ch != *str; str++)
+ ;
+ return (*str != '\0') ^ regexp->nccl;
+}
+
+static void regexp_free(RE *regexp)
+{
+ RE *tmp;
+ for(; regexp; regexp = tmp)
+ {
+ tmp = regexp->next;
+ kfree(regexp);
+ }
+}
+
+static RE* compile(char *regexp)
+{
+ RE head, *tail, *tmp;
+ char *pstr;
+ int err_flag = 0;
+
+ for(tail = &head; *regexp != '\0' && err_flag == 0; regexp++)
+ {
+ tmp = getmem(sizeof(RE));
+ switch(*regexp){
+ case '\\':
+ regexp++;
+ if(*regexp == 'd')
+ {
+ tmp->type = LIST;
+ tmp->nccl = 0;
+ tmp->ccl = getmem(11);
+ creat_list(tmp->ccl, '0','9');
+ tmp->ccl[11] = '\0';
+ }else if(*regexp == 'D')
+ {
+ tmp->type = LIST;
+ tmp->nccl = 1;
+ tmp->ccl = getmem(11);
+ creat_list(tmp->ccl, '0','9');
+ tmp->ccl[11] = '\0';
+ }else
+ {
+ tmp->type = CHAR;
+ tmp->ch = *(regexp + 1);
+ }
+ break;
+ case '.':
+ tmp->type = DOT;
+ break;
+ case '^':
+ tmp->type = BEGIN;
+ tmp->ch = '^';
+ break;
+ case '$':
+ tmp->type = END;
+ tmp->ch = '$';
+ break;
+ case '*':
+ tmp->type = STAR;
+ break;
+ case '+':
+ tmp->type = PLUS;
+ break;
+ case '?':
+ tmp->type = QUES;
+ break;
+ case '[':
+ pstr = tmp->ccl = getmem(256);
+ tmp->nccl = 0;
+ if(*++regexp == '^')
+ {
+ tmp->nccl = 1;
+ regexp++;
+ }
+ while(*regexp != '\0' && *regexp != ']')
+ {
+ if(*regexp != '-')
+ {
+ *pstr++ = *regexp++;
+ continue;
+ }
+ if(pstr == tmp->ccl || *(regexp + 1) == ']')
+ {
+ err_flag = 1;
+ break;
+ }
+ pstr += creat_list(pstr, *(regexp - 1) + 1, *(regexp + 1));
+ regexp += 2;
+ }
+ *pstr = '\0';
+ if(*regexp == '\0')
+ err_flag = 1;
+ tmp->type = LIST;
+ break;
+ default:
+ tmp->type = CHAR;
+ tmp->ch = *regexp;
+ }
+
+ tail->next = tmp;
+ tail = tmp;
+ }
+
+ tail->next = NULL;
+ if(err_flag)
+ {
+ regexp_free(head.next);
+ return NULL;
+ }
+ return head.next;
+}
+
+#define MATCH_ONE(reg, text) \
+ (reg->type == DOT || in_list(*text, reg) || *text == reg->ch)
+#define MATCH_ONE_P(reg, text) \
+ (in_list(*text++, reg) || *(text - 1) == reg->ch || reg->type == DOT)
+
+static int matchhere(RE *regexp, char *text);
+
+static int matchstar(RE *cur, RE *regexp, char *text)
+{
+ do{
+ if(matchhere(regexp, text))
+ return 1;
+ }while(*text != '\0' && MATCH_ONE_P(cur, text));
+ return 0;
+}
+
+static int matchstar_l(RE *cur, RE *regexp, char *text)
+{
+ char *t;
+ for(t = text; *t != '\0' && MATCH_ONE(cur, t); t++)
+ ;
+ do{
+ if(matchhere(regexp, t))
+ return 1;
+ }while(t-- > text);
+ return 0;
+}
+
+static int matchplus(RE *cur, RE *regexp, char *text)
+{
+ while(*text != '\0' && MATCH_ONE_P(cur, text))
+ {
+ if(matchhere(regexp, text))
+ return 1;
+ }
+ return 0;
+}
+
+static int matchplus_l(RE *cur, RE *regexp, char *text)
+{
+ char *t;
+ for(t = text; *t != '\0' && MATCH_ONE(cur, t); t++)
+ ;
+ for(; t > text; t--)
+ {
+ if(matchhere(regexp, t))
+ return 1;
+ }
+ return 0;
+}
+
+static int matchques(RE *cur, RE *regexp, char *text)
+{
+ int cnt = 1;
+ char *t = text;
+ if(*t != '\0' && cnt-- && MATCH_ONE(cur, t))
+ t++;
+ do{
+ if(matchhere(regexp, t))
+ return 1;
+ }while(t-- > text);
+ return 0;
+}
+
+static int (*matchfun[TYPENUM][2])(RE *, RE *, char *) = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ matchstar, matchstar_l,
+ matchplus, matchplus_l,
+ matchques, matchques,
+};
+
+static int matchhere(RE *regexp, char *text)
+{
+ if(regexp == NULL)
+ return 1;
+ if(regexp->type == END && regexp->next == NULL)
+ return *text == '\0';
+ if(regexp->next && matchfun[regexp->next->type][match_longest])
+ return matchfun[regexp->next->type][match_longest](regexp, regexp->next->next, text);
+
+ if(*text != '\0' && MATCH_ONE(regexp, text))
+ return matchhere(regexp->next, text + 1);
+ return 0;
+}
+
+/*
+ * return value:
+ * -1 error
+ * 0 not match
+ * 1 matched
+ */
+int regexp_match(char *reg, char *text)
+{
+ int ret;
+ RE *regexp = compile(reg);
+ if(regexp == NULL)
+ return -1;
+
+ if(regexp->type == BEGIN)
+ {
+ ret = matchhere(regexp->next, text);
+ goto out;
+ }
+
+ do{
+ if(ret = matchhere(regexp, text))
+ {
+ goto out;
+ }
+ }while(*text++ != '\0');
+
+out:
+ regexp_free(regexp);
+ return ret;
+}
+
+
+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);
+ }
+}
+
+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);
+}
diff --git a/package/cnsztl/open-app-filter/Makefile b/package/cnsztl/open-app-filter/Makefile
new file mode 100755
index 0000000000..a9a495dc86
--- /dev/null
+++ b/package/cnsztl/open-app-filter/Makefile
@@ -0,0 +1,46 @@
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=appfilter
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
+
+
+include $(INCLUDE_DIR)/package.mk
+#include $(INCLUDE_DIR)/kernel.mk
+
+define Package/appfilter
+ SECTION:=Derry Apps
+ CATEGORY:=Derry Apps
+ TITLE:=App filter userspace module
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Compile/Default
+
+endef
+
+define Package/appfilter/description
+ open appfilter app
+endef
+
+
+define Package/appfilter/install
+ echo "install"
+ $(INSTALL_DIR) $(1)/usr/bin $(1)/etc/init.d
+ $(INSTALL_DIR) $(1)/etc/appfilter
+ $(INSTALL_DIR) $(1)/etc/config
+ $(CP) ./files/feature.cfg $(1)/etc/appfilter/
+ $(INSTALL_BIN) ./files/appfilter.init $(1)/etc/init.d/appfilter
+ $(INSTALL_BIN) ./files/appfilter.sh $(1)/usr/bin
+ $(INSTALL_BIN) ./files/gen_class.sh $(1)/usr/bin
+ $(INSTALL_BIN) ./files/appfilter.config $(1)/etc/config/appfilter
+endef
+
+
+$(eval $(call BuildPackage,appfilter))
+
diff --git a/package/cnsztl/open-app-filter/files/appfilter.config b/package/cnsztl/open-app-filter/files/appfilter.config
new file mode 100755
index 0000000000..a63a65c78c
--- /dev/null
+++ b/package/cnsztl/open-app-filter/files/appfilter.config
@@ -0,0 +1,3 @@
+config global global
+
+config appfilter appfilter
diff --git a/package/cnsztl/open-app-filter/files/appfilter.init b/package/cnsztl/open-app-filter/files/appfilter.init
new file mode 100755
index 0000000000..a1aac6ebf9
--- /dev/null
+++ b/package/cnsztl/open-app-filter/files/appfilter.init
@@ -0,0 +1,11 @@
+#!/bin/sh /etc/rc.common
+
+START=96
+start() {
+ gen_class.sh /etc/appfilter/feature.cfg
+ appfilter.sh
+}
+
+stop() {
+ echo "stop appfilter"
+}
diff --git a/package/cnsztl/open-app-filter/files/appfilter.sh b/package/cnsztl/open-app-filter/files/appfilter.sh
new file mode 100755
index 0000000000..db42f06d2f
--- /dev/null
+++ b/package/cnsztl/open-app-filter/files/appfilter.sh
@@ -0,0 +1,65 @@
+. /usr/share/libubox/jshn.sh
+. /lib/functions.sh
+
+config_apply()
+{
+ test -z "$1" && return 1
+
+ if [ -e "/dev/appfilter" ];then
+ echo "config json str=$1"
+ echo "$1" >/dev/appfilter
+ fi
+}
+
+clean_rule()
+{
+ json_init
+ echo "clean appfilter rule..."
+
+ json_add_int "op" 3
+ json_add_object "data"
+ json_str=`json_dump`
+
+ config_apply "$json_str"
+
+ json_cleanup
+}
+
+load_rule()
+{
+ json_init
+
+ config_load appfilter
+ config_get enable "global" enable
+ echo "enable = $enable"
+ if [ x"$enable" != x"1" ];then
+ echo "appfilter is disabled"
+ return 0
+ fi
+ echo "appfilter is enabled"
+ json_add_int "op" 1
+
+ json_add_object "data"
+ json_add_array "apps"
+
+ for file in `ls /etc/appfilter/*.class`
+ do
+ class_name=`echo "$file" | awk -F/ '{print $4}'| awk -F. '{print $1}'`
+ config_get appid_list "appfilter" "${class_name}apps"
+ echo "appid_list=$appid_list"
+
+ if ! test -z "$appid_list";then
+ for appid in $appid_list:
+ do
+ json_add_int "" $appid
+ done
+ fi
+ done
+
+ json_str=`json_dump`
+ config_apply "$json_str"
+ json_cleanup
+}
+
+clean_rule
+load_rule
diff --git a/package/cnsztl/open-app-filter/files/feature.cfg b/package/cnsztl/open-app-filter/files/feature.cfg
new file mode 100755
index 0000000000..5b4bb762ec
--- /dev/null
+++ b/package/cnsztl/open-app-filter/files/feature.cfg
@@ -0,0 +1,56 @@
+#id name:[proto;sport;dport;host url;request;dict]
+#class chat
+1001 QQ:[tcp;;;;;00:02|-1:03,tcp;;;;;02:02|-1:03,tcp;;14000;;;,tcp;;8080;;;00:ca|01:3c,tcp;;;;;00:00|01:00|02:00|03:15]
+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]
+
+#class game
+2001 王者荣耀:[tcp;;;;;00:33|01:66|02:00|03:09,udp;;;;;00:01|01:02|02:00|03:00]
+2002 绝地求生:[tcp;;;;;00:43|1:66|02:aa,tcp;;;;;00:33|1:66|03:0a|05:0a|,tcp;;;;;00:01|1:00|02:00,udp;;;;;00:59|01:ad|03:45|05:e4,udp;;;;;00:b9|01:5d|03:39|05:77]
+2003 英雄联盟:[udp;;;;;44:00|45:00|46:00|47:02]
+2004 荒野行动:[udp;;;;;00:05|01:09,tcp;;;;;00:02|01:00|02:00|03:00]
+2005 欢乐斗地主:[tcp;;8000;;;00:74|01:67|02:77|03:5f]
+2006 梦幻西游:[tcp;;;;;00:0e|01:00|02:fe|03:ff]
+2007 明日之后:[udp;;;;;00:05|01:09|02:00,tcp;;;;;00:02|01:00|02:00|03:00|04:00|05:00]
+2008 QQ飞车:[udp;;;;;00:28|01:28,tcp;;10000;;;00:33|01:66|02:00|03:08]
+
+#class video
+3001 抖音短视频:[tcp;;80;-dy-;;,tcp;;;-dy.;;]
+3002 火山小视频:[tcp;;;.huoshan.com;;,tcp;;;hs.pstatp.com;;,tcp;;;hs.ixigua.com;;]
+3003 腾讯视频:[tcp;;443;v.qq.com;;,tcp;;443;tc.qq.com;;,tcp;;443;video.qq.com;;,tcp;;443;btrace.qq.com;;]
+3004 爱奇艺:[tcp;;443;iqiyi.com;;,tcp;;443;irs01.com;;]
+3005 微视:[tcp;;80;;;00:34|01:16|02:75,tcp;;80;weishi.qq.com;;]
+3006 斗鱼直播:[tcp;;;douyu;;,tcp;;;douyu;;-2:2f|-1:00]
+3007 熊猫直播:[tcp;;;panda;;,tcp;;;;;00:00|01:06|02:00]
+3008 虎牙直播:[tcp;;;huya;;,udp;;;;;01:00|02:00|03:00|04:23,udp;;;;;01:00|02:00|03:00|04:24]
+3009 快手:[tcp;;;kuaishou;;,tcp;;;ksyuncdn.com;;,tcp;;;.gifshow.com;;,tcp;;;yximgs.com;;]
+3010 小红书:[tcp;;;xiaohongshu;;]
+3011 花椒直播:[tcp;;;huajiao;;]
+3012 映客直播:[tcp;;;;.inke.cn;]
+3013 YY:[udp;;;;;02:00|03:00|04:08,udp;;;;;00:4f|01:00|02:00]
+3014 妩媚直播:[tcp;;;guojiang.tv;;]
+3015 菠萝直播:[tcp;;;;;00:03|01:00|02:00|03:00]
+
+#class shopping
+4001 淘宝:[tcp;;;taobao;;,tcp;;;alicdn.com;;,tcp;;;tmall.com;;,tcp;;;;;00:d3|01:00,,tcp;;;;;00:d4|01:00,,tcp;;;;;00:d3|01:00]
+4002 京东:[tcp;;;360buyimg;;,tcp;;;jd.com;;,tcp;;;jdcdn.com;;,tcp;;;;;00:d5|01:00]
+4003 唯品会:[tcp;;;vips-mobile;;,tcp;;;vipshop;;,tcp;;;vip.com;;,tcp;;;vipstatic.com;;,tcp;;;appsimg.com;;]
+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.;;]
+
+#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;;]
+
+#class employee
+6001 前程无忧:[tcp;;;51job;;]
+6002 智联招聘:[tcp;;;zhaopin;;]
+6003 猎聘:[tcp;;;liepin;;]
+6004 赶集网:[tcp;;;58.com;;,tcp;;;58cdn;;]
+6005 同城急聘:[tcp;;;xiaomei;;]
+6006 领英:[tcp;;;linkedin;;]
+6007 斗米:[tcp;;;doumi;;]
diff --git a/package/cnsztl/open-app-filter/files/gen_class.sh b/package/cnsztl/open-app-filter/files/gen_class.sh
new file mode 100755
index 0000000000..ab09a1c2d8
--- /dev/null
+++ b/package/cnsztl/open-app-filter/files/gen_class.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+f_file=$1
+test -z "$f_file" && return
+
+cur_class=""
+cur_class_file=""
+while read line
+do
+ echo "$line"| grep "^#class"
+ if [ $? -eq 0 ];then
+# echo "match $line"
+# echo "cur_class=$cur_class"
+ class=`echo $line| grep '#class' | awk '{print $2}'`
+ if ! test -z "$class";then
+# echo "class=$class"
+ cur_class=$class
+ cur_class_file="/etc/appfilter/${cur_class}.class"
+ if [ -e "$cur_class_file" ];then
+ rm $cur_class_file
+ fi
+ touch $cur_class_file
+ fi
+ fi
+ test -z "$cur_class" && continue
+ appid=`echo "$line" |awk '{print $1}'`
+ appname=`echo "$line" | awk '{print $2}' | awk -F: '{print $1}'`
+
+ echo "$appid $appname" >> $cur_class_file
+done < $f_file
+echo "ok"
+