openwrt app过滤模块所有源码,包含内核、luci和应用层脚本
This commit is contained in:
parent
14c3ef1cce
commit
6e7c737046
@ -1,2 +0,0 @@
|
||||
appfilter-objs := app_filter.o af_utils.o flow_detection.o
|
||||
obj-m += appfilter.o
|
@ -1,48 +0,0 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#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");
|
||||
}
|
||||
|
@ -1,317 +0,0 @@
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/tcp.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/types.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#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);
|
14
luci-app-oaf/Makefile
Executable file
14
luci-app-oaf/Makefile
Executable file
@ -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
|
12
luci-app-oaf/luasrc/controller/appfilter.lua
Executable file
12
luci-app-oaf/luasrc/controller/appfilter.lua
Executable file
@ -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
|
86
luci-app-oaf/luasrc/model/cbi/appfilter/appfilter.lua
Executable file
86
luci-app-oaf/luasrc/model/cbi/appfilter/appfilter.lua
Executable file
@ -0,0 +1,86 @@
|
||||
--[[
|
||||
LuCI - Lua Configuration Interface
|
||||
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
|
||||
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
|
42
luci-app-oaf/po/zh-cn/oaf.po
Executable file
42
luci-app-oaf/po/zh-cn/oaf.po
Executable file
@ -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 "开启应用过滤"
|
14
luci-app-oaf/root/etc/uci-defaults/91_luci-oaf
Executable file
14
luci-app-oaf/root/etc/uci-defaults/91_luci-oaf
Executable file
@ -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
|
45
oaf/Makefile
Executable file
45
oaf/Makefile
Executable file
@ -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))
|
||||
|
2
oaf/src/Makefile
Executable file
2
oaf/src/Makefile
Executable file
@ -0,0 +1,2 @@
|
||||
oaf-objs := app_filter.o af_utils.o regexp.o cJSON.o app_filter_config.o
|
||||
obj-m += oaf.o
|
296
oaf/src/af_utils.c
Executable file
296
oaf/src/af_utils.c
Executable file
@ -0,0 +1,296 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/version.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
892
oaf/src/app_filter.c
Executable file
892
oaf/src/app_filter.c
Executable file
@ -0,0 +1,892 @@
|
||||
|
||||
/*
|
||||
author: destan19@126.com
|
||||
微信公众号: OpenWrt
|
||||
date:2019/1/10
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <net/tcp.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/types.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "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);
|
||||
|
@ -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
|
293
oaf/src/app_filter_config.c
Executable file
293
oaf/src/app_filter_config.c
Executable file
@ -0,0 +1,293 @@
|
||||
|
||||
/*
|
||||
author: destan19@126.com
|
||||
΢ÐŹ«ÖÚºÅ: OpenWrt
|
||||
date:2019/1/10
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/tcp.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/types.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/device.h>
|
||||
#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");
|
||||
}
|
||||
|
849
oaf/src/app_filter_feature.c
Executable file
849
oaf/src/app_filter_feature.c
Executable file
@ -0,0 +1,849 @@
|
||||
|
||||
/*
|
||||
author: destan19@126.com
|
||||
微信公众号: wifi开发者
|
||||
date:2019/1/10
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/tcp.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/types.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "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);
|
||||
|
423
oaf/src/cJSON.c
Executable file
423
oaf/src/cJSON.c
Executable file
@ -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 <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <float.h>
|
||||
#endif
|
||||
|
||||
#include "cJSON.h"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#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;i<depth;i++) *ptr++='\t';
|
||||
ptr+=sprintf(ptr,str);
|
||||
*ptr++=':';*ptr++='\t';
|
||||
ptr+=sprintf(ptr,ret);
|
||||
if (child->next) *ptr++=',';
|
||||
*ptr++='\n';*ptr=0;
|
||||
child=child->next;
|
||||
cJSON_free(str);cJSON_free(ret);
|
||||
}
|
||||
for (i=0;i<depth-1;i++) *ptr++='\t';
|
||||
*ptr++='}';*ptr++=0;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Get Array size/item / object item.
|
||||
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
|
||||
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;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=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;i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
|
94
oaf/src/cJSON.h
Executable file
94
oaf/src/cJSON.h
Executable file
@ -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 <linux/slab.h>
|
||||
|
||||
// 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
|
302
oaf/src/regexp.c
Executable file
302
oaf/src/regexp.c
Executable file
@ -0,0 +1,302 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
//#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);
|
||||
}
|
46
open-app-filter/Makefile
Executable file
46
open-app-filter/Makefile
Executable file
@ -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))
|
||||
|
3
open-app-filter/files/appfilter.config
Executable file
3
open-app-filter/files/appfilter.config
Executable file
@ -0,0 +1,3 @@
|
||||
config global global
|
||||
|
||||
config appfilter appfilter
|
11
open-app-filter/files/appfilter.init
Executable file
11
open-app-filter/files/appfilter.init
Executable file
@ -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"
|
||||
}
|
65
open-app-filter/files/appfilter.sh
Executable file
65
open-app-filter/files/appfilter.sh
Executable file
@ -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
|
56
open-app-filter/files/feature.cfg
Executable file
56
open-app-filter/files/feature.cfg
Executable file
@ -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;;]
|
32
open-app-filter/files/gen_class.sh
Executable file
32
open-app-filter/files/gen_class.sh
Executable file
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user