openwrt app过滤模块所有源码,包含内核、luci和应用层脚本

This commit is contained in:
Derry 2019-06-28 22:46:14 +08:00
parent 14c3ef1cce
commit 6e7c737046
27 changed files with 3589 additions and 368 deletions

View File

@ -1,2 +0,0 @@
appfilter-objs := app_filter.o af_utils.o flow_detection.o
obj-m += appfilter.o

View File

@ -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");
}

View File

@ -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
View 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

View 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

View 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
View 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 "开启应用过滤"

View 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
View 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
View 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
View 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;
}

View File

@ -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
View 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);

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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))

View File

@ -0,0 +1,3 @@
config global global
config appfilter appfilter

View 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"
}

View 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

View 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;;]

View 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"