diff --git a/kernel/Makefile b/kernel/Makefile deleted file mode 100755 index fcd5b18..0000000 --- a/kernel/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -appfilter-objs := app_filter.o af_utils.o flow_detection.o -obj-m += appfilter.o diff --git a/kernel/af_utils.c b/kernel/af_utils.c deleted file mode 100755 index bfb3aab..0000000 --- a/kernel/af_utils.c +++ /dev/null @@ -1,48 +0,0 @@ -#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 - int i; - if (len > MAX_DUMP_STR_LEN) { - len = MAX_DUMP_STR_LEN - 1; - } - printk("%s: ",name); - for (i = 0; i < len; i++) { - printk("%c",*(p + i)); - } - printk("\n"); -} - -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"); -} - diff --git a/kernel/app_filter.c b/kernel/app_filter.c deleted file mode 100755 index bf3a9d4..0000000 --- a/kernel/app_filter.c +++ /dev/null @@ -1,317 +0,0 @@ - -#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"); - -#define MIN_HTTP_DATA_LEN 16 - - -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; - } -} - -/* 在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 (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; - } - } - 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, - }, -}; -/* - 模块退出 -*/ -static void app_filter_fini(void) -{ - AF_DEBUG("app filter module exit\n"); - nf_unregister_hooks(app_filter_ops, ARRAY_SIZE(app_filter_ops)); - return ; -} - -/* - 模块初始化 -*/ -static int __init app_filter_init(void) -{ - AF_INFO("appfilter version:"AF_VERSION"\n"); - AF_DEBUG("app filter module init\n"); - nf_register_hooks(app_filter_ops, ARRAY_SIZE(app_filter_ops)); - printk("init app filter ........ok\n"); - return 0; -} - -module_init(app_filter_init); -module_exit(app_filter_fini); diff --git a/kernel/flow_detection.c b/kernel/flow_detection.c deleted file mode 100755 index e69de29..0000000 diff --git a/kernel/flow_detection.h b/kernel/flow_detection.h deleted file mode 100755 index e69de29..0000000 diff --git a/luci-app-oaf/Makefile b/luci-app-oaf/Makefile new file mode 100755 index 0000000..7e0e49e --- /dev/null +++ b/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/luci-app-oaf/luasrc/controller/appfilter.lua b/luci-app-oaf/luasrc/controller/appfilter.lua new file mode 100755 index 0000000..f9ff51e --- /dev/null +++ b/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/luci-app-oaf/luasrc/model/cbi/appfilter/appfilter.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/appfilter.lua new file mode 100755 index 0000000..3a67412 --- /dev/null +++ b/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/luci-app-oaf/po/zh-cn/oaf.po b/luci-app-oaf/po/zh-cn/oaf.po new file mode 100755 index 0000000..deb3a4f --- /dev/null +++ b/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/luci-app-oaf/root/etc/uci-defaults/91_luci-oaf b/luci-app-oaf/root/etc/uci-defaults/91_luci-oaf new file mode 100755 index 0000000..2041cc3 --- /dev/null +++ b/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/oaf/Makefile b/oaf/Makefile new file mode 100755 index 0000000..f0725ce --- /dev/null +++ b/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/oaf/src/Makefile b/oaf/src/Makefile new file mode 100755 index 0000000..1ab57a0 --- /dev/null +++ b/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/oaf/src/af_utils.c b/oaf/src/af_utils.c new file mode 100755 index 0000000..3ed23d4 --- /dev/null +++ b/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/kernel/af_utils.h b/oaf/src/af_utils.h similarity index 75% rename from kernel/af_utils.h rename to oaf/src/af_utils.h index 0ab37df..201adfc 100755 --- a/kernel/af_utils.h +++ b/oaf/src/af_utils.h @@ -7,5 +7,7 @@ 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/oaf/src/app_filter.c b/oaf/src/app_filter.c new file mode 100755 index 0000000..b4587c8 --- /dev/null +++ b/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/kernel/app_filter.h b/oaf/src/app_filter.h similarity index 80% rename from kernel/app_filter.h rename to oaf/src/app_filter.h index 7680349..f9be684 100755 --- a/kernel/app_filter.h +++ b/oaf/src/app_filter.h @@ -1,6 +1,6 @@ #ifndef APP_FILTER_H #define APP_FILTER_H -#define AF_DEBUG printk +#define AF_DEBUG if(0) printk #define AF_ERROR printk #define AF_INFO printk #define AF_VERSION "1.0.1" @@ -19,6 +19,9 @@ #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 @@ -56,4 +59,10 @@ typedef struct flow_info{ 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/oaf/src/app_filter_config.c b/oaf/src/app_filter_config.c new file mode 100755 index 0000000..1bd6f7d --- /dev/null +++ b/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/oaf/src/app_filter_feature.c b/oaf/src/app_filter_feature.c new file mode 100755 index 0000000..eec0463 --- /dev/null +++ b/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/oaf/src/cJSON.c b/oaf/src/cJSON.c new file mode 100755 index 0000000..8b22453 --- /dev/null +++ b/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/oaf/src/cJSON.h b/oaf/src/cJSON.h new file mode 100755 index 0000000..e7c794c --- /dev/null +++ b/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/oaf/src/regexp.c b/oaf/src/regexp.c new file mode 100755 index 0000000..bd8ef4a --- /dev/null +++ b/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/open-app-filter/Makefile b/open-app-filter/Makefile new file mode 100755 index 0000000..a9a495d --- /dev/null +++ b/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/open-app-filter/files/appfilter.config b/open-app-filter/files/appfilter.config new file mode 100755 index 0000000..a63a65c --- /dev/null +++ b/open-app-filter/files/appfilter.config @@ -0,0 +1,3 @@ +config global global + +config appfilter appfilter diff --git a/open-app-filter/files/appfilter.init b/open-app-filter/files/appfilter.init new file mode 100755 index 0000000..a1aac6e --- /dev/null +++ b/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/open-app-filter/files/appfilter.sh b/open-app-filter/files/appfilter.sh new file mode 100755 index 0000000..db42f06 --- /dev/null +++ b/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/open-app-filter/files/feature.cfg b/open-app-filter/files/feature.cfg new file mode 100755 index 0000000..5b4bb76 --- /dev/null +++ b/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/open-app-filter/files/gen_class.sh b/open-app-filter/files/gen_class.sh new file mode 100755 index 0000000..ab09a1c --- /dev/null +++ b/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" +