v5.0.1-Support app access time statistics

This commit is contained in:
Derry 2021-03-02 04:17:30 -08:00
parent cd2387c88a
commit 2489a517e5
24 changed files with 2095 additions and 94 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,6 @@
module("luci.controller.appfilter", package.seeall)
local utl = require "luci.util"
function index()
if not nixio.fs.access("/etc/config/appfilter") then
@ -6,12 +8,27 @@ function index()
end
local page
--hide save button
page = entry({"admin", "network", "appfilter"}, arcombine(cbi("appfilter/appfilter"), cbi("appfilter/dev_status", {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true})), _("appfilter"), 100)
page = entry({"admin", "network", "appfilter"}, cbi("appfilter/appfilter"), _("appfilter"))
page.dependent = true
page.leaf = true
page.subindex = true
--page.dependent = true
page = entry({"admin", "network", "user_status"}, call("user_status"), nil)
page.leaf = true
page = entry({"admin", "network", "dev_app_status"}, call("dev_app_status"), nil)
page.leaf = true
page = entry({"admin", "network", "dev_visit_list"}, call("get_dev_visit_list"), nil)
page.leaf = true
page = entry({"admin", "network", "dev_visit_time"}, call("get_dev_visit_time"), nil)
page.leaf = true
page = entry({"admin", "network", "app_class_visit_time"}, call("get_app_class_visit_time"), nil)
page.leaf = true
end
function get_hostname_by_mac(dst_mac)
@ -48,40 +65,106 @@ function cmp_func(a,b)
end
function user_status()
local json = require "luci.jsonc"
luci.http.prepare_content("application/json")
--local fs=require "nixio.fs"
--local ok, status_data = pcall(json.parse, fs.readfile("/proc/net/af_client"))
--luci.http.write_json(tb);
local fd = io.open("/proc/net/af_client","r")
status_buf=fd:read('*a')
fd:close()
user_array=json.parse(status_buf)
local visit_obj=utl.ubus("appfilter", "visit_list", {});
local user_array=visit_obj.dev_list
local history={}
for i, v in pairs(user_array) do
visit_array=user_array[i].visit_info
for j,s in pairs(visit_array) do
print(user_array[i].mac, user_array[i].ip,visit_array[j].appid, visit_array[j].latest_time)
total_time=visit_array[j].latest_time - visit_array[j].first_time;
history[#history+1]={
mac=user_array[i].mac,
ip=user_array[i].ip,
hostname=get_hostname_by_mac(user_array[i].mac),
appid=visit_array[j].appid,
appname=get_app_name_by_id(visit_array[j].appid),
total_num=visit_array[j].total_num,
drop_num=visit_array[j].drop_num,
--total_num=visit_array[j].total_num,
--drop_num=visit_array[j].drop_num,
total_num=0,
drop_num=0,
latest_action=visit_array[j].latest_action,
latest_time=os.date("%Y/%m/%d %H:%M:%S", visit_array[j].latest_time)
latest_time=os.date("%Y/%m/%d %H:%M:%S", visit_array[j].latest_time),
first_time=os.date("%Y/%m/%d %H:%M:%S", visit_array[j].first_time),
total_time=total_time
}
end
end
table.sort(history, cmp_func)
--luci.http.write(history);
luci.http.write_json(history);
end
function dev_app_status()
local json = require "luci.jsonc"
luci.http.prepare_content("application/json")
local visit_obj=utl.ubus("appfilter", "dev_list", {});
luci.http.write_json(visit_obj);
end
function get_dev_visit_time(mac)
local json = require "luci.jsonc"
luci.http.prepare_content("application/json")
local fd = io.open("/proc/net/af_client","r")
status_buf=fd:read('*a')
fd:close()
user_array=json.parse(status_buf)
local req_obj = {}
req_obj.mac = mac;
local visit_obj=utl.ubus("appfilter", "dev_visit_time", req_obj);
local user_array=visit_obj.app_list
luci.http.write_json(user_array);
end
function get_app_class_visit_time(mac)
local json = require "luci.jsonc"
luci.http.prepare_content("application/json")
local req_obj = {}
req_obj.mac = mac;
local visit_obj=utl.ubus("appfilter", "app_class_visit_time", req_obj);
local class_array=visit_obj.class_list
luci.http.write_json(class_array);
end
function get_dev_visit_list(mac)
local json = require "luci.jsonc"
luci.http.prepare_content("application/json")
local req_obj = {}
req_obj.mac = mac;
local visit_obj=utl.ubus("appfilter", "visit_list", req_obj);
local user_array=visit_obj.dev_list
local history={}
for i, v in pairs(user_array) do
visit_array=user_array[i].visit_info
for j,s in pairs(visit_array) do
print(user_array[i].mac, user_array[i].ip,visit_array[j].appid, visit_array[j].latest_time)
total_time=visit_array[j].latest_time - visit_array[j].first_time;
history[#history+1]={
mac=user_array[i].mac,
ip=user_array[i].ip,
hostname=get_hostname_by_mac(user_array[i].mac),
appid=visit_array[j].appid,
appname=get_app_name_by_id(visit_array[j].appid),
total_num=0,
drop_num=0,
latest_action=visit_array[j].latest_action,
latest_time=os.date("%Y/%m/%d %H:%M:%S", visit_array[j].latest_time),
first_time=os.date("%Y/%m/%d %H:%M:%S", visit_array[j].first_time),
total_time=total_time
}
end
end
table.sort(history, cmp_func)
luci.http.write_json(history);
end

View File

@ -1,6 +1,14 @@
local ds = require "luci.dispatcher"
local nxo = require "nixio"
local nfs = require "nixio.fs"
local ipc = require "luci.ip"
local sys = require "luci.sys"
local utl = require "luci.util"
local dsp = require "luci.dispatcher"
local uci = require "luci.model.uci"
local lng = require "luci.i18n"
local jsc = require "luci.jsonc"
local m, s
@ -108,18 +116,17 @@ users.widget="checkbox"
--users.widget="select"
users.size=1
local fd = io.open("/proc/net/arp", "r")
local fd = io.open("/tmp/dev_list", "r")
if not fd then return m end
while true do
local line = fd:read("*l")
if not line then
break
end
if not line:match("Ip*") then
local ip=get_cmd_result(string.format("echo '%s' | awk '{print $1}'", line))
local mac=get_cmd_result(string.format("echo '%s' | awk '{print $4}'", line))
local device=get_cmd_result(string.format("echo '%s' | awk '{print $6}'", line))
if device ~= nil and mac ~= nil and device:match("lan") then
if not line:match("Id*") then
local ip=get_cmd_result(string.format("echo '%s' | awk '{print $3}'", line))
local mac=get_cmd_result(string.format("echo '%s' | awk '{print $2}'", line))
if mac ~= nil then
local hostname=get_hostname_by_mac(mac)
if not hostname or hostname == "*" then
users:value(mac, mac);

View File

@ -0,0 +1,24 @@
local ds = require "luci.dispatcher"
local nxo = require "nixio"
local nfs = require "nixio.fs"
local ipc = require "luci.ip"
local sys = require "luci.sys"
local utl = require "luci.util"
local dsp = require "luci.dispatcher"
local uci = require "luci.model.uci"
local lng = require "luci.i18n"
local jsc = require "luci.jsonc"
local m, s
arg[1] = arg[1] or ""
m = Map("appfilter",
translate("上网统计("..arg[1]..")"),
translate(""))
local v
v=m:section(SimpleSection)
v.template="admin_network/dev_status"
v.mac=arg[1]
m.redirect = luci.dispatcher.build_url("admin", "network", "appfilter")
return m

View File

@ -0,0 +1,353 @@
<style type="text/css">
<%
local dsp = require "luci.dispatcher"
-%>
#display{
display: flex;
}
table.imagetable {
font-family: verdana,arial,sans-serif;
font-size:11px;
color:#333333;
border-width: 1px;
border-color: #999999;
border-collapse: collapse;
}
<!--
table.imagetable th {
background:#f5f5f5
border-width: 0px;
padding: 5px;
border-style: solid;
border-color: #999999;
}
table.imagetable td {
background:#ffffffff
border-width: 0px;
padding: 5px;
border-style: solid;
border-color: #999999;
}-->
</style>
<script type="text/javascript" src="<%=resource%>/echarts.min.js"></script>
<script type="text/javascript">//<![CDATA[
window.onload =function(){
}
var app_class_data;
var app_time_data;
var mac='<%=self.mac%>';
function get_display_time(total_time){
var hour=parseInt(total_time / 3600);
var seconds=total_time % 3600;
var min=parseInt(seconds / 60)
var seconds2=seconds % 60;
var total_time_str;
if (hour > 0)
total_time_str=hour + "小时" + min + "分"
else{
if (min == 0 && seconds2 != 0)
min = 1;
total_time_str=min + "分"
}
return total_time_str;
}
function display_app_visit_view(data){
var myChart = echarts.init(document.getElementById('main2'));
var dev_array=new Array();
var m2R2Data=new Array()
var total_time=0
for(var i = 0; i < data.length; i++){
var dev_obj = data[i];
var m2_obj={};
m2_obj.value=dev_obj.visit_time;
m2_obj.legendname=dev_obj.app_id;
var tmp_time = get_display_time(dev_obj.visit_time);
m2_obj.name=dev_obj.app_id + " " + tmp_time;
total_time+=dev_obj.visit_time
m2R2Data.push(m2_obj);
}
var total_time_str = get_display_time(total_time);
option = {
title: [
{
text: 'APP时间统计',
textStyle: {
fontSize: 16,
color: "black"
},
left: "2%"
},
{
text: '',
subtext: total_time_str,
textStyle:{
fontSize:15,
color:"black"
},
subtextStyle: {
fontSize: 15,
color: 'black'
},
textAlign:"center",
x: '34.5%',
y: '44%',
}],
tooltip: {
trigger: 'item',
formatter:function (parms){
var total_time = get_display_time(parms.data.value);
var str= parms.seriesName+"</br>"+
parms.marker+""+parms.data.legendname+"</br>"+
"时间:"+ total_time+"</br>"+
"占比:"+ parms.percent+"%";
return str ;
}
},
legend: {
type:"scroll",
orient: 'vertical',
left:'70%',
align:'left',
top:'middle',
textStyle: {
color:'#8C8C8C'
},
height:250
},
series: [
{
name: "访问时间",
type:'pie',
center: ['35%', '50%'],
radius: ['40%', '65%'],
clockwise: false, //饼图的扇区是否是顺时针排布
avoidLabelOverlap: false,
label: {
normal: {
show: true,
position: 'outter',
formatter:function (parms){
return parms.data.legendname
}
}
},
labelLine: {
normal: {
length:8,
length2:7,
smooth:true,
}
},
data:m2R2Data
}
]
};
myChart.setOption(option);
}
function display_app_class_view(data){
console.log("begin display.");
var myChart = echarts.init(document.getElementById('main'));
var dev_array=new Array();
var m2R2Data=new Array()
var total_time=0
for(var i = 0; i < data.length; i++){
console.log("begin display222.");
var dev_obj = data[i];
if (dev_obj.visit_time == 0)
continue;
var m2_obj={};
m2_obj.value=dev_obj.visit_time;
m2_obj.legendname=dev_obj.name;
var tmp_time = get_display_time(dev_obj.visit_time);
m2_obj.name=dev_obj.name + " " + tmp_time;
total_time+=dev_obj.visit_time
m2R2Data.push(m2_obj);
}
var total_time_str = get_display_time(total_time);
option = {
title: [
{
text: 'APP分类时间统计',
textStyle: {
fontSize: 16,
color: "black"
},
left: "2%"
},
{
text: '',
subtext: total_time_str,
textStyle:{
fontSize:15,
color:"black"
},
subtextStyle: {
fontSize: 15,
color: 'black'
},
textAlign:"center",
x: '34.5%',
y: '44%',
}],
tooltip: {
trigger: 'item',
formatter:function (parms){
var total_time = get_display_time(parms.data.value);
var str= parms.seriesName+"</br>"+
parms.marker+""+parms.data.legendname+"</br>"+
"时间:"+ total_time +"</br>"+
"占比:"+ parms.percent+"%";
return str ;
}
},
legend: {
type:"scroll",
orient: 'vertical',
left:'70%',
align:'left',
top:'middle',
textStyle: {
color:'#8C8C8C'
},
height:250
},
series: [
{
name:'访问时间',
type:'pie',
center: ['35%', '50%'],
radius: ['40%', '65%'],
clockwise: false, //饼图的扇区是否是顺时针排布
avoidLabelOverlap: false,
label: {
normal: {
show: true,
position: 'outter',
formatter:function (parms){
return parms.data.legendname
}
}
},
labelLine: {
normal: {
length:8,
length2:7,
smooth:true,
}
},
data:m2R2Data
}
]
};
myChart.setOption(option);
}
new XHR().get('<%=url('admin/network/app_class_visit_time')%>/' + mac, null,
function(x, st)
{
display_app_class_view(st);
}
);
new XHR().get('<%=url('admin/network/dev_visit_time')%>/' + mac, null,
function(x, st)
{
display_app_visit_view(st);
}
);
new XHR().get('<%=url('admin/network/dev_visit_list')%>/' + mac, null,
function(x, st)
{
var tb = document.getElementById('user_status_table');
var str=JSON.stringify(st);
if (st && tb)
{
/* clear all rows */
while(tb.rows.length > 1)
tb.deleteRow(1);
for(var i = 0; i < st.length; i++ )
{
var action_status=""
if(st[i].latest_action == 1)
action_status="已过滤"
else
action_status="未过滤"
var hostname=""
if(st[i].hostname == "" || st[i].hostname == "*"){
hostname="?";
}
else{
hostname=st[i].hostname;
}
var tr = tb.insertRow(-1);
//tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
tr.insertCell(-1).innerHTML = st[i].appname;
tr.insertCell(-1).innerHTML = hostname;
tr.insertCell(-1).innerHTML = st[i].mac;
tr.insertCell(-1).innerHTML = st[i].first_time;
var hour=parseInt(st[i].total_time / 3600);
var seconds=st[i].total_time % 3600;
var min=parseInt(seconds / 60)
var total_time_str;
if (st[i].latest_action == 1)
total_time_str="-"
else {
if (hour > 0)
total_time_str=hour + "小时" + min + "分"
else{
if (min == 0)
min = 1;
total_time_str=min + "分"
}
}
tr.insertCell(-1).innerHTML = total_time_str;
tr.insertCell(-1).innerHTML = action_status;
}
}
}
);
//]]></script>
<fieldset class="cbi-section">
<div id="display">
<div id="main" style="width: 500px;height:300px;"></div>
<div id="main2" style="width: 400px;height:300px;"></div>
</div>
<table class="imagetable" id="user_status_table">
<tr>
<th ><%:App%></th>
<th ><%:主机名%></th>
<th ><%:mac地址%></th>
<th><%:开始时间%></th>
<th><%:访问时长%></th>
<th><%:过滤状态%></th>
</tr>
<tr>
<td colspan="8"><em><br /><%:Collecting data...%></em></td>
</tr>
</table>
</fieldset>

View File

@ -1,4 +1,8 @@
<style type="text/css">
<%
local dsp = require "luci.dispatcher"
-%>
table.imagetable {
font-family: verdana,arial,sans-serif;
font-size:11px;
@ -25,6 +29,7 @@ table.imagetable td {
</style>
<script type="text/javascript">//<![CDATA[
/*
XHR.poll(10, '<%=url('admin/network/user_status')%>', null,
function(x, st)
{
@ -32,7 +37,6 @@ table.imagetable td {
var str=JSON.stringify(st);
if (st && tb)
{
/* clear all rows */
while(tb.rows.length > 1)
tb.deleteRow(1);
for(var i = 0; i < st.length; i++ )
@ -51,33 +55,93 @@ table.imagetable td {
}
var tr = tb.insertRow(-1);
//tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
tr.insertCell(-1).innerHTML = hostname;
tr.insertCell(-1).innerHTML = st[i].mac;
tr.insertCell(-1).innerHTML = st[i].ip;
tr.insertCell(-1).innerHTML = st[i].appname;
tr.insertCell(-1).innerHTML = st[i].drop_num;
tr.insertCell(-1).innerHTML = st[i].total_num;
tr.insertCell(-1).innerHTML = st[i].latest_time;
tr.insertCell(-1).innerHTML = hostname;
tr.insertCell(-1).innerHTML = "<a href='<%=url('admin/network/appfilter/')%>"+st[i].mac+"'>"+st[i].mac+"</a>";
tr.insertCell(-1).innerHTML = st[i].first_time;
var hour=parseInt(st[i].total_time / 3600);
var seconds=st[i].total_time % 3600;
var min=parseInt(seconds / 60)
var total_time_str;
if (st[i].latest_action == 1)
total_time_str="-"
else {
if (hour > 0)
total_time_str=hour + "小时" + min + "分"
else{
if (min == 0)
min = 1;
total_time_str=min + "分"
}
}
tr.insertCell(-1).innerHTML = total_time_str;
tr.insertCell(-1).innerHTML = action_status;
}
}
}
);
*/
XHR.poll(5, '<%=url('admin/network/dev_app_status')%>', null,
function(x, st)
{
var tb = document.getElementById('user_status_table');
var dev_list_str=JSON.stringify(st);
if (st && tb)
{
while(tb.rows.length > 1)
tb.deleteRow(1);
var devlist = st.devlist
for(var i = 0; i < devlist.length; i++ )
{
var hostname=""
if(devlist[i].hostname == "" || devlist[i].hostname == "*"){
hostname="?";
}
else{
hostname=devlist[i].hostname;
}
var tr = tb.insertRow(-1);
//tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
tr.insertCell(-1).innerHTML = hostname;
tr.insertCell(-1).innerHTML = "<a href='<%=url('admin/network/appfilter/')%>"+devlist[i].mac+"'>"+devlist[i].mac+"</a>";
tr.insertCell(-1).innerHTML = devlist[i].ip;
var app_list_str="";
for (var j = 0; j < devlist[i].applist.length; j++){
console.log(devlist[i].applist[j].name);
app_list_str+=devlist[i].applist[j].name;
if (j != devlist[i].applist.length - 1)
app_list_str+=","
}
tr.insertCell(-1).innerHTML = app_list_str;
}
}
}
);
//]]></script>
<fieldset class="cbi-section">
<legend><%:访问记录%></legend>
<legend><%:终端列表%></legend>
<table class="imagetable" id="user_status_table">
<!--
<tr>
<th ><%:App%></th>
<th ><%:主机名%></th>
<th ><%:mac地址%></th>
<th><%:开始时间%></th>
<th><%:访问时长%></th>
<th><%:过滤状态%></th>
</tr>
-->
<tr>
<th ><%:主机名%></th>
<th ><%:mac地址%></th>
<th ><%:ip地址%></th>
<th ><%:App名称%></th>
<th><%:丢包次数%></th>
<th ><%:访问次数%></th>
<th><%:最后访问时间%></th>
<th><%:过滤状态%></th>
<th><%:常用APP(TOP5)%></th>
</tr>
<tr>
<td colspan="8"><em><br /><%:Collecting data...%></em></td>

View File

@ -23,12 +23,15 @@
#include "af_log.h"
#include "af_utils.h"
#include "app_filter.h"
#include "cJSON.h"
DEFINE_RWLOCK(af_client_lock);
u32 total_client = 0;
struct list_head af_client_list_table[MAX_AF_CLIENT_HASH_SIZE];
int af_send_msg_to_user(char *pbuf, uint16_t len);
static void
nf_client_list_init(void)
{
@ -173,6 +176,7 @@ void flush_expired_visit_info(af_client_info_t *node)
}
if (cur_timep - node->visit_info[i].latest_time > timeout){
// 3?¡§o?¨¤??3y????
memset(&node->visit_info[i], 0x0, sizeof(app_visit_info_t));
count++;
}
@ -180,13 +184,66 @@ void flush_expired_visit_info(af_client_info_t *node)
}
int af_report_visit_info(af_client_info_t *node){
unsigned char mac_str[32] = {0};
unsigned char ip_str[32] = {0};
int i, j;
cJSON *root_obj = cJSON_CreateObject();
if(!root_obj){
AF_ERROR("create json obj failed");
return 0;
}
sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac));
sprintf(ip_str, "%pI4", &node->ip);
cJSON_AddStringToObject(root_obj, "mac", mac_str);
cJSON_AddStringToObject(root_obj, "ip", ip_str);
cJSON_AddNumberToObject(root_obj, "app_num", node->visit_app_num);
cJSON *visit_info_array = cJSON_CreateArray();
int count = 0;
for(i = 0; i < MAX_RECORD_APP_NUM; i++){
if(node->visit_info[i].app_id == 0)
continue;
if(node->visit_info[i].total_num < 3)
continue;
count++;
cJSON *visit_obj = cJSON_CreateObject();
cJSON_AddNumberToObject(visit_obj, "appid", node->visit_info[i].app_id);
cJSON_AddNumberToObject(visit_obj, "latest_action", node->visit_info[i].latest_action);
//cJSON_AddNumberToObject(visit_obj, "latest_time", node->visit_info[i].latest_time);
//cJSON_AddNumberToObject(visit_obj, "total_num", node->visit_info[i].total_num);
//cJSON_AddNumberToObject(visit_obj, "drop_num", node->visit_info[i].drop_num);
cJSON_AddNumberToObject(visit_obj, "up_bytes", node->visit_info[i].total_up_bytes);
cJSON_AddNumberToObject(visit_obj, "down_bytes", node->visit_info[i].total_down_bytes);
//clear
memset((char *)&node->visit_info[i], 0x0, sizeof(app_visit_info_t));
cJSON_AddItemToArray(visit_info_array, visit_obj);
}
cJSON_AddItemToObject(root_obj, "visit_info", visit_info_array);
char *out = cJSON_Print(root_obj);
if(!out)
return 0;
//cJSON_Minify(out);
if (count > 0 || node->report_count == 0){
AF_INFO("report:%s count=%d\n", out, node->report_count);
node->report_count++;
af_send_msg_to_user(out, strlen(out));
}
cJSON_Delete(root_obj);
kfree(out);
return 0;
}
void af_visit_info_timer_handle(void){
af_client_info_t *node;
int i;
AF_CLIENT_LOCK_W();
for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++){
list_for_each_entry(node, &af_client_list_table[i], hlist) {
flush_expired_visit_info(node);
//flush_expired_visit_info(node);
AF_INFO("report %s\n", node->mac);
af_report_visit_info(node);
}
}
AF_CLIENT_UNLOCK_W();

View File

@ -39,6 +39,8 @@ typedef struct app_visit_info{
unsigned int drop_num;
unsigned long latest_time;
unsigned int latest_action;
unsigned int total_down_bytes;
unsigned int total_up_bytes;
unsigned long history_time[MAX_VISIT_HISTORY_TIME];
unsigned int action[MAX_VISIT_HISTORY_TIME];
}app_visit_info_t;
@ -50,6 +52,7 @@ typedef struct af_client_info {
unsigned long create_jiffies;
unsigned long update_jiffies;
unsigned int visit_app_num;
int report_count;
app_visit_info_t visit_info[MAX_RECORD_APP_NUM];
}af_client_info_t;

View File

@ -1,7 +1,6 @@
/*
author: destan19@126.com
: OpenWrt
author: derry
date:2019/1/10
*/
#include <linux/init.h>
@ -38,6 +37,10 @@ DEFINE_RWLOCK(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 SET_APPID(mark, appid) (mark = appid)
#define GET_APPID(mark) (mark)
#if 0
static void show_feature_list(void)
{
@ -147,6 +150,7 @@ 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};
@ -410,14 +414,6 @@ int parse_flow_base(struct sk_buff *skb, flow_info_t *flow)
}
/*
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 ;
@ -450,7 +446,7 @@ int parse_https_proto(flow_info_t *flow) {
//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);
// dump_str("https url2", flow->https.url_pos, ntohs(url_len));
flow->https.url_len = ntohs(url_len);
return 0;
}
@ -502,7 +498,6 @@ void parse_http_proto(flow_info_t *flow)
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;
@ -692,15 +687,16 @@ int app_filter_match(flow_info_t *flow)
list_for_each_entry_safe(node, n, &af_feature_head, head) {
if(af_match_one(flow, node))
{
AF_DEBUG("appid = %d\n", node->app_id);
flow->app_id = node->app_id;
if (is_user_match_enable()){
client = find_af_client_by_ip(flow->src);
if (!client || !find_af_mac(client->mac)){
goto EXIT;
}
strncpy(flow->app_name, node->app_name, sizeof(flow->app_name) - 1);
client = find_af_client_by_ip(flow->src);
if (!client){
goto EXIT;
}
if (is_user_match_enable() && !find_af_mac(client->mac)){
AF_DEBUG("not match mac:"MAC_FMT"\n", MAC_ARRAY(client->mac));
goto EXIT;
}
if (af_get_app_status(node->app_id)){
flow->drop = AF_TRUE;
feature_list_read_unlock();
@ -719,7 +715,7 @@ EXIT:
return AF_FALSE;
}
#define APP_FILTER_DROP_BITS 0xf0000000
#define APP_FILTER_DROP_BITS 0x80000000
@ -737,29 +733,34 @@ static int af_get_visit_index(af_client_info_t *node, int app_id){
int __af_update_client_app_info(flow_info_t *flow, af_client_info_t *node)
{
int i;
int index = -1;
if(!node)
return -1;
if(!flow)
return -1;
AF_INFO("%s %d visit_app_num = %d\n", __func__, __LINE__, node->visit_app_num);
int found = 0;
index = af_get_visit_index(node, flow->app_id);
if(index < 0 || index >= MAX_RECORD_APP_NUM){
AF_ERROR("invalid index:%d\n\n", index);
return 0;
}
// todo: up bytes
node->visit_info[index].total_down_bytes += flow->l4_len + 66;
//AF_ERROR("%s %pI4(%d)--> %pI4(%d) len = %d\n ", IPPROTO_TCP == flow->l4_protocol ? "tcp" :"udp",
// &flow->src, flow->sport, &flow->dst, flow->dport, flow->l4_len);
// AF_ERROR("index = %d, appid:%d, total:%d KB, cur len=%d\n",index,
/// flow->app_id, node->visit_info[index].total_down_bytes / 1024, flow->l4_len);
//}
node->visit_info[index].total_num++;
if(flow->drop)
node->visit_info[index].drop_num++;
node->visit_info[index].app_id = flow->app_id;
node->visit_info[index].latest_time = af_get_timestamp_sec();
AF_DEBUG("update time = %lu\n", node->visit_info[index].latest_time);
node->visit_info[index].latest_action = flow->drop;
AF_INFO("[%d] %pI4 visit %d, time=%lu action=%s, %d/%d\n", index, &node->ip, flow->app_id,
AF_INFO("[%d] %pI4 visit %s(%d), time=%d action=%s, %d/%d\n", index, &node->ip, flow->app_name, flow->app_id,
node->visit_info[index].latest_time, node->visit_info[index].latest_action ? "Drop" : "Accept",
node->visit_info[index].drop_num, node->visit_info[index].total_num);
// todo: history
@ -768,6 +769,8 @@ int __af_update_client_app_info(flow_info_t *flow, af_client_info_t *node)
void af_update_client_app_info(flow_info_t *flow)
{
int i;
int index = 0;
af_client_info_t *node = NULL;
if(!flow)
return;
@ -781,6 +784,11 @@ void af_update_client_app_info(flow_info_t *flow)
AF_CLIENT_UNLOCK_W();
}
int af_send_msg_to_user(char *pbuf, uint16_t len);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
static u_int32_t app_filter_hook(void *priv,
struct sk_buff *skb,
@ -792,6 +800,8 @@ static u_int32_t app_filter_hook(unsigned int hook,
const struct net_device *out,
int (*okfn)(struct sk_buff *)){
#endif
static int bytes1 = 0;
unsigned long long total_packets = 0;
flow_info_t flow;
// 4.10-->4.11 nfct-->_nfct
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
@ -805,16 +815,18 @@ static u_int32_t app_filter_hook(unsigned int hook,
if(ct == NULL) {
return NF_ACCEPT;
}
if(!nf_ct_is_confirmed(ct)){
return NF_ACCEPT;
}
#if defined(CONFIG_NF_CONNTRACK_MARK)
if(ct->mark != 0)
if(ct->mark != 0){
//AF_LMT_ERROR("mark = %x, appid = %x\n", ct->mark, GET_APPID(ct->mark));
if(APP_FILTER_DROP_BITS == (ct->mark & APP_FILTER_DROP_BITS)){
return NF_DROP;
}
}
#endif
#if 0
// 3.12.74-->3.13-rc1
@ -843,20 +855,30 @@ static u_int32_t app_filter_hook(unsigned int hook,
#endif
memset((char *)&flow, 0x0, sizeof(flow_info_t));
if(parse_flow_base(skb, &flow) < 0)
if(parse_flow_base(skb, &flow) < 0){
return NF_ACCEPT;
}
// int appid = GET_APPID(ct->mark);
parse_http_proto(&flow);
parse_https_proto(&flow);
if (TEST_MODE())
dump_flow_info(&flow);
app_filter_match(&flow);
if (flow.app_id != 0){
//SET_APPID(ct->mark, flow.app_id);
if (flow.app_id > 1000 && flow.app_id <= 8999){
af_update_client_app_info(&flow);
AF_LMT_INFO("match %s %pI4(%d)--> %pI4(%d) len = %d, %d\n ", IPPROTO_TCP == flow.l4_protocol ? "tcp" :"udp",
&flow.src, flow.sport, &flow.dst, flow.dport, skb->len, flow.app_id);
}
}
if(flow.drop){
#if defined(CONFIG_NF_CONNTRACK_MARK)
ct->mark |= APP_FILTER_DROP_BITS;
#endif
af_update_client_app_info(&flow);
AF_LMT_INFO("##drop appid = %d\n\n\n", flow.app_id);
AF_LMT_INFO("##Drop app %s flow, appid is %d\n", flow.app_name, flow.app_id);
return NF_DROP;
}
return NF_ACCEPT;
@ -906,14 +928,14 @@ void TEST_cJSON(void)
struct timer_list oaf_timer;
#define OAF_TIMER_INTERVAL 15
#define OAF_TIMER_INTERVAL 60
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
static void oaf_timer_func(struct timer_list *t)
#else
static void oaf_timer_func(unsigned long ptr)
#endif
{
// check_client_expire();
check_client_expire();
af_visit_info_timer_handle();
mod_timer(&oaf_timer, jiffies + OAF_TIMER_INTERVAL * HZ);
}
@ -930,26 +952,108 @@ void init_oaf_timer(void)
AF_INFO("init oaf timer...ok");
}
void fini_port_timer(void)
void fini_oaf_timer(void)
{
del_timer_sync(&oaf_timer);
AF_INFO("del oaf timer...ok");
}
static struct sock *oaf_sock = NULL;
struct af_msg_hdr{
int magic;
int len;
};
#define OAF_NETLINK_ID 29
#define MAX_OAF_NL_MSG_LEN 1024
int af_send_msg_to_user(char *pbuf, uint16_t len)
{
struct sk_buff *nl_skb;
struct nlmsghdr *nlh;
int ret;
if (len > MAX_OAF_NL_MSG_LEN){
return -1;
}
nl_skb = nlmsg_new(len + sizeof(struct af_msg_hdr), GFP_ATOMIC);
if(!nl_skb)
{
printk("netlink alloc failure\n");
return -1;
}
nlh = nlmsg_put(nl_skb, 0, 0, OAF_NETLINK_ID, len + sizeof(struct af_msg_hdr), 0);
if(nlh == NULL)
{
printk("error, nlh is NULL\n");
nlmsg_free(nl_skb);
return -1;
}
char msg_buf[MAX_OAF_NL_MSG_LEN] = {0};
struct af_msg_hdr *hdr = (struct af_msg_hdr *)msg_buf;
hdr->magic = 0xa0b0c0d0;
hdr->len = len;
char *p_data = msg_buf + sizeof(struct af_msg_hdr);
memcpy(p_data, pbuf, len);
memcpy(nlmsg_data(nlh), msg_buf, len + sizeof(struct af_msg_hdr));
ret = netlink_unicast(oaf_sock, nl_skb, 999, MSG_DONTWAIT);
return ret;
}
static void oaf_msg_rcv(struct sk_buff *skb)
{
struct nlmsghdr *nlh = NULL;
char *umsg = NULL;
char *kmsg = "ok";
if(skb->len >= nlmsg_total_size(0))
{
nlh = nlmsg_hdr(skb);
umsg = NLMSG_DATA(nlh);
if(umsg)
{
AF_INFO("kernel recv from user: %s\n", umsg);
af_send_msg_to_user(kmsg, strlen(kmsg));
}
}
}
int netlink_oaf_init(void)
{
struct netlink_kernel_cfg bm_nl_cfg = {0};
bm_nl_cfg.input = oaf_msg_rcv;
oaf_sock = netlink_kernel_create(&init_net, OAF_NETLINK_ID, &bm_nl_cfg);
if (NULL == oaf_sock)
{
AF_ERROR("init oaf netlink failed, id=%d\n", OAF_NETLINK_ID);
return -1;
}
AF_INFO("init oaf netlink ok, id = %d\n", OAF_NETLINK_ID);
return 0;
}
static int __init app_filter_init(void)
{
AF_INFO("appfilter version:"AF_VERSION"\n");
printk("appfilter version:"AF_VERSION"\n");
if (0 != load_feature_config()){
printk("load feature failed\n");
return -1;
}
netlink_oaf_init();
af_log_init();
af_register_dev();
af_mac_list_init();
af_init_app_status();
init_af_client_procfs();
// show_feature_list();
af_client_init();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
nf_register_net_hooks(&init_net, app_filter_ops, ARRAY_SIZE(app_filter_ops));
@ -957,14 +1061,16 @@ static int __init app_filter_init(void)
nf_register_hooks(app_filter_ops, ARRAY_SIZE(app_filter_ops));
#endif
init_oaf_timer();
AF_INFO("init app filter ........ok\n");
return 0;
}
static void app_filter_fini(void)
{
AF_INFO("app filter module exit\n");
fini_port_timer();
fini_oaf_timer();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
nf_unregister_net_hooks(&init_net, app_filter_ops, ARRAY_SIZE(app_filter_ops));
#else
@ -977,6 +1083,8 @@ static void app_filter_fini(void)
af_log_exit();
af_client_exit();
finit_af_client_procfs();
if (oaf_sock)
netlink_kernel_release(oaf_sock);
return ;
}

View File

@ -1,7 +1,7 @@
#ifndef APP_FILTER_H
#define APP_FILTER_H
#define AF_VERSION "3.0.1"
#define AF_VERSION "5.0.1"
#define AF_FEATURE_CONFIG_FILE "/etc/appfilter/feature.cfg"
#define MAX_PARSE_PKT_NUM 16
@ -84,7 +84,10 @@ typedef struct flow_info{
http_proto_t http;
https_proto_t https;
u_int32_t app_id;
u_int8_t app_name[MAX_APP_NAME_LEN];
u_int8_t drop;
u_int8_t dir;
u_int16_t total_len;
}flow_info_t;

View File

@ -162,7 +162,7 @@ af_mac_info_t * find_af_mac(unsigned char *mac)
index = hash_mac(mac);
list_for_each_entry(node, &af_mac_list_table[index], hlist){
if (0 == memcmp(node->mac, mac, 6)){
AF_ERROR("match mac:"MAC_FMT"\n", MAC_ARRAY(node->mac));
AF_DEBUG("match mac:"MAC_FMT"\n", MAC_ARRAY(node->mac));
return node;
}
}
@ -409,7 +409,7 @@ int af_register_dev(void)
if (IS_ERR_OR_NULL(dev)) {
goto CLASS_OUT;
}
printk("register char dev....ok\n");
AF_INFO("register char dev....ok\n");
return 0;
@ -420,7 +420,7 @@ CDEV_OUT:
REGION_OUT:
unregister_chrdev_region(g_af_dev.id, 1);
printk("register char dev....fail\n");
AF_ERROR("register char dev....fail\n");
return -EINVAL;
}
@ -431,6 +431,6 @@ void af_unregister_dev(void)
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");
AF_INFO("unregister char dev....ok\n");
}

View File

@ -2,7 +2,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=appfilter
PKG_VERSION:=3.0
PKG_VERSION:=5.0
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
@ -14,14 +14,24 @@ include $(INCLUDE_DIR)/package.mk
define Package/appfilter
SECTION:=Derry Apps
CATEGORY:=Derry Apps
DEPENDS:=+libubox-lua
DEPENDS:=+libubox +libubus +libuci +libpthread +libjson-c +libblobmsg-json
TITLE:=App filter userspace module
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
mkdir -p $(PKG_BUILD_DIR)
cp -rf ./src/* $(PKG_BUILD_DIR)
endef
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR)/ \
CC="$(TARGET_CROSS)gcc" \
CFLAGS="$(TARGET_CFLAGS)" \
LIBS="$(TARGET_LDFLAGS) -lm -lpthread -lubox -luci -lubus -ljson-c -lblobmsg_json" \
all
endef
define Build/Compile/Default
endef
@ -37,11 +47,11 @@ define Package/appfilter/install
$(INSTALL_DIR) $(1)/etc/appfilter
$(INSTALL_DIR) $(1)/etc/config
$(CP) ./files/feature.cfg $(1)/etc/appfilter/
$(CP) ./files/app_class.txt $(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.lua $(1)/usr/bin/appfilter
$(INSTALL_BIN) ./files/appfilter.config $(1)/etc/config/appfilter
$(INSTALL_BIN) $(PKG_BUILD_DIR)/oafd $(1)/usr/bin
endef

View File

@ -0,0 +1,8 @@
1 聊天
2 游戏
3 视频
4 购物
5 音乐
6 招聘
7 下载
8 常用网站

View File

@ -1,29 +1,112 @@
#!/bin/sh /etc/rc.common
. /usr/share/libubox/jshn.sh
. /lib/functions.sh
START=96
USE_PROCD=1
path=/usr/bin
OAFD_BIN="/usr/bin/oafd"
service_triggers() {
procd_add_reload_trigger 'appfilter'
config_apply()
{
test -z "$1" && return 1
if [ -e "/dev/appfilter" ];then
echo "config json str=$1"
echo "$1" >/dev/appfilter
fi
}
start_instance() {
config_get_bool enable $1 'enable' '0'
clean_rule()
{
json_init
echo "clean appfilter rule..."
command $path/gen_class.sh /etc/appfilter/feature.cfg
command $path/appfilter.sh
json_add_int "op" 3
json_add_object "data"
json_str=`json_dump`
[ $enable -ne 0 ] || return 1
config_apply "$json_str"
json_cleanup
}
load_rule()
{
json_init
json_add_int "op" 1
json_add_object "data"
json_add_array "apps"
for file in `ls /tmp/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
}
load_mac_list()
{
json_init
config_load appfilter
json_add_int "op" 4
json_add_object "data"
json_add_array "mac_list"
config_get appid_list "user" "users"
echo "appid list=$appid_list"
for appid in $appid_list:
do
echo "appid=$appid"
json_add_string "" $appid
done
json_str=`json_dump`
config_apply "$json_str"
echo "json str=$json_str"
json_cleanup
}
service_triggers()
{
procd_add_reload_trigger "appfilter"
}
stop_service(){
clean_rule
}
start_service(){
gen_class.sh /etc/appfilter/feature.cfg
config_load appfilter
config_get enable "global" enable
echo "enable = $enable"
if [ x"$enable" != x"1" ];then
echo "appfilter is disabled"
echo 0 >/proc/sys/oaf/enable>/dev/null
return 0
else
insmod oaf >/dev/null
echo 1 >/proc/sys/oaf/enable
fi
load_rule
load_mac_list
procd_open_instance
procd_set_param command $path/appfilter
procd_set_param respawn
procd_set_param respawn 60 5 5
procd_set_param stderr 1
procd_set_param command "$OAFD_BIN"
procd_close_instance
}
start_service() {
config_load 'appfilter'
config_foreach start_instance 'global'
}

6
open-app-filter/src/Makefile Executable file
View File

@ -0,0 +1,6 @@
OBJS:=appfilter_user.o appfilter_netlink.o appfilter_ubus.o appfilter_config.o main.o
EXEC:=oafd
all: $(OBJS)
$(CC) -o $(EXEC) $(OBJS) $(LIBS)
clean:
rm $(EXEC) *.o

View File

@ -0,0 +1,88 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
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.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "appfilter_config.h"
app_name_info_t app_name_table[MAX_SUPPORT_APP_NUM];
int g_app_count = 0;
int g_cur_class_num = 0;
char CLASS_NAME_TABLE[MAX_APP_TYPE][MAX_CLASS_NAME_LEN];
char *get_app_name_by_id(int id){
int i;
for (i = 0;i < g_app_count; i++){
if (id == app_name_table[i].id)
return app_name_table[i].name;
}
return "";
}
void init_app_name_table(void){
int count = 0;
char line_buf[2048] = {0};
FILE * fp = fopen("/etc/appfilter/feature.cfg", "r");
if (!fp){
printf("open file failed\n");
return;
}
while (fgets(line_buf, sizeof(line_buf), fp)){
if (strstr(line_buf, "#"))
continue;
if (strlen(line_buf) < 10)
continue;
if (!strstr(line_buf, ":"))
continue;
char *pos1 = strstr(line_buf, ":");
char app_info_buf[128] = {0};
int app_id;
char app_name[64] = {0};
memset(app_name, 0x0, sizeof(app_name));
strncpy(app_info_buf, line_buf, pos1 - line_buf);
sscanf(app_info_buf, "%d %s", &app_id, app_name);
app_name_table[g_app_count].id = app_id;
strcpy(app_name_table[g_app_count].name, app_name);
g_app_count++;
}
fclose(fp);
}
void init_app_class_name_table(void){
char line_buf[2048] = {0};
int class_id;
char class_name[64] = {0};
FILE * fp = fopen("/etc/appfilter/app_class.txt", "r");
if (!fp){
printf("open file failed\n");
return;
}
while (fgets(line_buf, sizeof(line_buf), fp)){
sscanf(line_buf, "%d %s", &class_id, class_name);
printf("class id = %d, class name = %s\n", class_id, class_name);
strcpy(CLASS_NAME_TABLE[class_id - 1], class_name);
g_cur_class_num++;
}
fclose(fp);
}

View File

@ -0,0 +1,38 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
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 __APPFILTER_CONFIG_H__
#define __APPFILTER_CONFIG_H__
#define MAX_SUPPORT_APP_NUM 256
#define MAX_CLASS_NAME_LEN 32
#include "appfilter_user.h"
extern int g_cur_class_num;
extern int g_app_count;
extern char CLASS_NAME_TABLE[MAX_APP_TYPE][MAX_CLASS_NAME_LEN];
typedef struct app_name_info{
int id;
char name[64];
}app_name_info_t;
void init_app_name_table(void);
void init_app_class_name_table(void);
char *get_app_name_by_id(int id);
#endif

View File

@ -0,0 +1,173 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
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.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json-c/json.h>
#include "appfilter_user.h"
#include "appfilter_netlink.h"
#define MAX_NL_RCV_BUF_SIZE 4096
#define REPORT_INTERVAL_SECS 60
void appfilter_nl_handler(struct uloop_fd *u, unsigned int ev)
{
int ret;
int i;
char buf[MAX_NL_RCV_BUF_SIZE];
struct sockaddr_nl nladdr;
struct iovec iov = {buf, sizeof(buf)};
struct nlmsghdr *h;
char *mac = NULL;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
do{
ret = recvmsg(u->fd, &msg, 0);
} while ((-1 == ret) && (EINTR == errno));
if (ret < 0) {
printf("recv msg error\n");
return;
}
else if (0 == ret) {
return;
}
h = (struct nlmsghdr *)buf;
char *kmsg = (char *)NLMSG_DATA(h);
struct af_msg_hdr *af_hdr = (struct af_msg_hdr *)kmsg;
if (af_hdr->magic != 0xa0b0c0d0){
printf("magic error %x\n", af_hdr->magic);
return;
}
if (af_hdr->len <= 0 || af_hdr->len >= MAX_OAF_NETLINK_MSG_LEN){
printf("data len error\n");
return;
}
char *kdata = kmsg + sizeof(struct af_msg_hdr);
struct json_object *root = json_tokener_parse(kdata);
if (!root){
printf("parse json failed:%s", kdata);
return;
}
struct json_object *mac_obj = json_object_object_get(root,"mac");
if (!mac_obj){
printf("parse mac obj failed\n");
json_object_put(root);
return;
}
mac = json_object_get_string(mac_obj);
dev_node_t *node = find_dev_node(mac);
if (!node){
node = add_dev_node(mac);
}
struct json_object *ip_obj = json_object_object_get(root, "ip");
if (ip_obj)
strncpy(node->ip, json_object_get_string(ip_obj), sizeof(node->ip));
struct json_object *visit_array = json_object_object_get(root, "visit_info");
if (!visit_array){
json_object_put(root);
return;
}
for (i = 0; i < json_object_array_length(visit_array); i++){
struct json_object *visit_obj = json_object_array_get_idx(visit_array, i);
struct json_object *appid_obj = json_object_object_get(visit_obj, "appid");
struct json_object *action_obj = json_object_object_get(visit_obj, "latest_action");
struct json_object *up_obj = json_object_object_get(visit_obj, "up_bytes");
struct json_object *down_obj = json_object_object_get(visit_obj, "down_bytes");
struct timeval cur_time;
gettimeofday(&cur_time, NULL);
int appid = json_object_get_int(appid_obj);
int action = json_object_get_int(action_obj);
int type = appid / 1000;
int id = appid % 1000;
node->stat[type - 1][id - 1].total_time += REPORT_INTERVAL_SECS;
// node->stat[type - 1][id - 1].total_down_bytes += json_object_get_int(down_obj);
// node->stat[type - 1][id - 1].total_up_bytes += json_object_get_int(up_obj);
int hash = hash_appid(appid);
visit_info_t *head = node->visit_htable[hash];
if (head && (cur_time.tv_sec - head->latest_time) < 300){
printf("update visit info curtime=%d, last time=%d\n", cur_time.tv_sec, head->latest_time);
head->latest_time = cur_time.tv_sec;
}
else{
visit_info_t *visit_node = (visit_info_t *)calloc(1, sizeof(visit_info_t));
visit_node->action = action;
visit_node->appid = appid;
visit_node->latest_time = cur_time.tv_sec;
visit_node->first_time = cur_time.tv_sec - MIN_VISIT_TIME;
visit_node->next = NULL;
add_visit_info_node(&node->visit_htable[hash], visit_node);
//printf("add visit info curtime=%d\n", cur_time.tv_sec);
}
}
json_object_put(root);
}
int appfilter_nl_init(void)
{
int fd;
struct sockaddr_nl nls;
fd = socket(AF_NETLINK, SOCK_RAW, OAF_NETLINK_ID);
if(fd < 0)
{
printf("Connect netlink %d failed %s", OAF_NETLINK_ID, strerror(errno));
exit(1);
}
memset(&nls, 0, sizeof(struct sockaddr_nl));
nls.nl_pid = DEFAULT_USR_NL_PID;
nls.nl_groups = 0;
nls.nl_family = AF_NETLINK;
if (bind(fd, (void *)&nls, sizeof(struct sockaddr_nl))) {
printf("Bind failed %s\n", strerror(errno));
exit(1);
}
return fd;
}

View File

@ -0,0 +1,34 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
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 __APPFILTER_NETLINK_H__
#define __APPFILTER_NETLINK_H__
#define DEFAULT_USR_NL_PID 999
#define OAF_NETLINK_ID 29
#define MAX_OAF_NETLINK_MSG_LEN 1024
struct af_msg_hdr{
int magic;
int len;
};
int appfilter_nl_init(void);
void appfilter_nl_handler(struct uloop_fd *u, unsigned int ev);
#endif

View File

@ -0,0 +1,460 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
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.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include <json-c/json.h>
#include <sys/time.h>
#include <libubox/blobmsg_json.h>
#include <libubox/blobmsg.h>
#include "appfilter_user.h"
#include "appfilter_config.h"
struct ubus_context *ubus_ctx = NULL;
static struct blob_buf b;
extern char * format_time(int timetamp);
void get_hostname_by_mac(char *mac, char *hostname){
if (!mac || !hostname)
return;
FILE *fp = fopen("/tmp/dhcp.leases", "r");
if (!fp){
printf("open dhcp lease file....failed\n");
return;
}
char line_buf[256] = {0};
while(fgets(line_buf, sizeof(line_buf), fp)){
printf("line buf = %s\n", line_buf);
char hostname_buf[128] = {0};
char mac_buf[32] = {0};
sscanf(line_buf, "%*s %s %*s %s", mac_buf, hostname_buf);
if (0 == strcmp(mac, mac_buf)){
printf("match mac:%s\n", mac_buf);
strcpy(hostname, hostname_buf);
}
else{
printf("not match mac:%s\n", mac_buf);
}
}
fclose(fp);
}
void ubus_dump_visit_list(struct blob_buf *b, char *mac){
int i, j;
void *c, *array;
void *t;
void *s;
array = blobmsg_open_array(b, "dev_list");
for (i = 0;i < MAX_DEV_NODE_HASH_SIZE; i++){
dev_node_t *node = dev_hash_table[i];
while(node){
if (mac && strcmp(mac, node->mac)){
node = node->next;
continue;
}
t = blobmsg_open_table(b, NULL);
blobmsg_add_string(b, "hostname", "unknown");
blobmsg_add_string(b, "mac", node->mac);
blobmsg_add_string(b, "ip", node->ip);
void *visit_array;
visit_array = blobmsg_open_array(b, "visit_info");
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++){
visit_info_t *p_info = node->visit_htable[j];
while(p_info){
char *first_time_str = format_time(p_info->first_time);
char *latest_time_str = format_time(p_info->latest_time);
int total_time = p_info->latest_time - p_info->first_time;
s = blobmsg_open_table(b, NULL);
blobmsg_add_string(b, "appname", "unknown");
blobmsg_add_u32(b, "appid", p_info->appid);
blobmsg_add_u32(b, "latest_action", p_info->action);
blobmsg_add_u32(b, "first_time", p_info->first_time);
blobmsg_add_u32(b, "latest_time", p_info->latest_time);
blobmsg_close_table(b, s);
if (first_time_str)
free(first_time_str);
if (latest_time_str)
free(latest_time_str);
p_info = p_info->next;
}
}
blobmsg_close_array(b, visit_array);
blobmsg_close_table(b, t);
node = node->next;
}
}
blobmsg_close_array(b, array);
}
void update_app_visit_time_list(char *mac, struct app_visit_stat_info *visit_info){
int i, j, s;
int num = 0;
dev_node_t *node = find_dev_node(mac);
if (!node){
printf("not found mac:%s\n", mac);
return;
}
for (i = 0; i < MAX_APP_TYPE; i++){
for (j = 0; j < MAX_APP_ID_NUM; j++){
unsigned long long min = visit_info->visit_list[0].total_time;
int min_index = 0;
if (node->stat[i][j].total_time == 0)
continue;
if (num < MAX_APP_STAT_NUM){
min_index = num;
}
else{
for (s = 0; s < MAX_APP_STAT_NUM; s++){
if (visit_info->visit_list[s].total_time < min){
min_index = s;
break;
}
}
}
num++;
if (node->stat[i][j].total_time > visit_info->visit_list[min_index].total_time){
visit_info->visit_list[min_index].total_time = node->stat[i][j].total_time;
visit_info->visit_list[min_index].app_id = (i + 1) * 1000 + j + 1;
}
}
}
if (num < MAX_APP_STAT_NUM)
visit_info->num = num;
else
visit_info->num = MAX_APP_STAT_NUM;
}
void update_app_class_visit_time_list(char *mac, int *visit_time){
int i, j, s;
int num = 0;
dev_node_t *node = find_dev_node(mac);
if (!node){
printf("not found mac:%s\n", mac);
return;
}
for (i = 0; i < MAX_APP_TYPE; i++){
for (j = 0; j < MAX_APP_ID_NUM; j++){
if (node->stat[i][j].total_time == 0)
continue;
visit_time[i] += node->stat[i][j].total_time;
}
}
}
void ubus_get_dev_visit_time_info(char *mac, struct blob_buf *b){
int i, j;
void *c, *array;
void *t;
void *s;
struct app_visit_stat_info info;
memset((char *)&info, 0x0, sizeof(info));
update_app_visit_time_list(mac, &info);
}
static int
appfilter_handle_visit_list(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
int ret;
blob_buf_init(&b, 0);
char *msg_obj_str = blobmsg_format_json(msg, true);
if (!msg_obj_str){
printf("format json failed\n");
return 0;
}
struct json_object *req_obj = json_tokener_parse(msg_obj_str);
struct json_object *mac_obj = json_object_object_get(req_obj, "mac");
if(!mac_obj){
ubus_dump_visit_list(&b, NULL);
}
else
ubus_dump_visit_list(&b, json_object_get_string(mac_obj));
ubus_send_reply(ctx, req, b.head);
return 0;
}
#if 0
blobmsg_add_string(b, "hostname", "unknown");
blobmsg_add_string(b, "mac", node->mac);
blobmsg_add_string(b, "ip", node->ip);
blobmsg_add_string(b, "appname", "unknown");
blobmsg_add_u32(b, "appid", p_info->appid);
blobmsg_add_u32(b, "latest_action", p_info->action);
blobmsg_add_u32(b, "first_time", p_info->first_time);
blobmsg_add_u32(b, "latest_time", p_info->latest_time);
#endif
typedef struct app_visit_time_info{
int app_id;
unsigned long long total_time;
}app_visit_time_info_t;
int visit_time_compare(const void *a, const void *b){
app_visit_time_info_t *p1 = (app_visit_time_info_t *)a;
app_visit_time_info_t *p2 = (app_visit_time_info_t *)b;
return p1->total_time < p2->total_time ? 1: -1;
}
#define MAX_STAT_APP_NUM 128
void update_top5_app(dev_node_t *node, app_visit_time_info_t top5_app_list[]){
int i, j;
//memset(app_array, 0x0, sizeof(int) *size);
app_visit_time_info_t app_visit_array[MAX_STAT_APP_NUM];
memset(app_visit_array, 0x0, sizeof(app_visit_array));
int app_visit_num = 0;
for (i = 0; i < MAX_APP_TYPE; i++){
for (j = 0; j < MAX_APP_ID_NUM; j++){
if (node->stat[i][j].total_time == 0)
continue;
app_visit_array[app_visit_num].app_id = (i + 1) * 1000 + j + 1;
app_visit_array[app_visit_num].total_time = node->stat[i][j].total_time;
app_visit_num++;
}
}
qsort((void *)app_visit_array, app_visit_num, sizeof(app_visit_time_info_t), visit_time_compare);
for (i = 0; i < app_visit_num; i++){
printf("appid %d-----------total time %llu\n", app_visit_array[i].app_id,
app_visit_array[i].total_time);
}
for (i = 0; i < 5; i++){
top5_app_list[i] = app_visit_array[i];
printf("appid %d-----------total time %llu\n", app_visit_array[i].app_id,
app_visit_array[i].total_time);
}
}
static int
appfilter_handle_dev_list(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
int i, j;
printf("%s %d\n", __func__, __LINE__);
struct json_object * root_obj = json_object_new_object();
struct json_object * dev_array = json_object_new_array();
printf("%s %d\n", __func__, __LINE__);
for (i = 0;i < MAX_DEV_NODE_HASH_SIZE; i++){
dev_node_t *node = dev_hash_table[i];
while(node){
printf("add mac:%s\n", node->mac);
struct json_object * dev_obj = json_object_new_object();
struct json_object * app_array = json_object_new_array();
app_visit_time_info_t top5_app_list[5];
memset(top5_app_list, 0x0, sizeof(top5_app_list));
update_top5_app(node, top5_app_list);
printf("22 add mac:%s\n", node->mac);
for (j = 0; j < 5; j++){
if (top5_app_list[j].app_id == 0)
break;
struct json_object * app_obj = json_object_new_object();
json_object_object_add(app_obj, "id", json_object_new_int(top5_app_list[j].app_id));
json_object_object_add(app_obj, "name", json_object_new_string(get_app_name_by_id(top5_app_list[j].app_id)));
json_object_array_add(app_array, app_obj);
}
printf("333 add mac:%s\n", node->mac);
json_object_object_add(dev_obj, "applist", app_array);
json_object_object_add(dev_obj, "mac", json_object_new_string(node->mac));
char hostname[32] = {0};
get_hostname_by_mac(node->mac, hostname);
json_object_object_add(dev_obj, "ip", json_object_new_string(node->ip));
json_object_object_add(dev_obj, "hostname", json_object_new_string(hostname));
json_object_object_add(dev_obj, "latest_app", json_object_new_string("test"));
json_object_array_add(dev_array, dev_obj);
node = node->next;
}
}
json_object_object_add(root_obj, "devlist", dev_array);
printf("%s %d\n", __func__, __LINE__);
blob_buf_init(&b, 0);
printf("%s %d\n", __func__, __LINE__);
blobmsg_add_object(&b, root_obj);
printf("%s %d\n", __func__, __LINE__);
ubus_send_reply(ctx, req, b.head);
printf("%s %d\n", __func__, __LINE__);
json_object_put(root_obj);
printf("%s %d\n", __func__, __LINE__);
return 0;
}
static int
appfilter_handle_visit_time(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
int ret;
struct app_visit_stat_info info;
blob_buf_init(&b, 0);
memset((char *)&info, 0x0, sizeof(info));
char *msg_obj_str = blobmsg_format_json(msg, true);
if (!msg_obj_str){
printf("format json failed\n");
return 0;
}
struct json_object *req_obj = json_tokener_parse(msg_obj_str);
struct json_object *mac_obj = json_object_object_get(req_obj, "mac");
if(!mac_obj){
printf("mac is NULL\n");
return 0;
}
update_app_visit_time_list(json_object_get_string(mac_obj), &info);
struct json_object *resp_obj = json_object_new_object();
struct json_object *app_info_array = json_object_new_array();
json_object_object_add(resp_obj,"app_list", app_info_array);
int i;
for (i = 0; i < info.num; i++){
struct json_object *app_info_obj = json_object_new_object();
json_object_object_add(app_info_obj, "app_id",
json_object_new_string(get_app_name_by_id(info.visit_list[i].app_id)));
json_object_object_add(app_info_obj, "visit_time",
json_object_new_int(info.visit_list[i].total_time));
json_object_array_add(app_info_array, app_info_obj);
}
blobmsg_add_object(&b, resp_obj);
ubus_send_reply(ctx, req, b.head);
json_object_put(resp_obj);
json_object_put(req_obj);
return 0;
}
static int
handle_app_class_visit_time(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
int ret;
int i;
blob_buf_init(&b, 0);
char *msg_obj_str = blobmsg_format_json(msg, true);
if (!msg_obj_str){
printf("format json failed\n");
return 0;
}
struct json_object *req_obj = json_tokener_parse(msg_obj_str);
struct json_object *mac_obj = json_object_object_get(req_obj, "mac");
if(!mac_obj){
printf("mac is NULL\n");
return 0;
}
int app_class_visit_time[MAX_APP_TYPE];
memset(app_class_visit_time, 0x0, sizeof(app_class_visit_time));
update_app_class_visit_time_list(json_object_get_string(mac_obj), app_class_visit_time);
struct json_object *resp_obj = json_object_new_object();
struct json_object *app_class_array = json_object_new_array();
json_object_object_add(resp_obj,"class_list", app_class_array);
for (i = 0; i < MAX_APP_TYPE; i++){
if (i >= g_cur_class_num)
break;
struct json_object *app_class_obj = json_object_new_object();
json_object_object_add(app_class_obj, "type", json_object_new_int(i));
json_object_object_add(app_class_obj, "name", json_object_new_string(CLASS_NAME_TABLE[i]));
json_object_object_add(app_class_obj, "visit_time", json_object_new_int(app_class_visit_time[i]));
json_object_array_add(app_class_array, app_class_obj);
}
blobmsg_add_object(&b, resp_obj);
ubus_send_reply(ctx, req, b.head);
json_object_put(resp_obj);
json_object_put(req_obj);
return 0;
}
static const struct blobmsg_policy empty_policy[1] = {
//[DEV_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
};
static struct ubus_method appfilter_object_methods[] = {
UBUS_METHOD("visit_list", appfilter_handle_visit_list, empty_policy),
UBUS_METHOD("dev_visit_time", appfilter_handle_visit_time, empty_policy),
UBUS_METHOD("app_class_visit_time", handle_app_class_visit_time, empty_policy),
UBUS_METHOD("dev_list", appfilter_handle_dev_list, empty_policy),
};
static struct ubus_object_type main_object_type =
UBUS_OBJECT_TYPE("appfilter", appfilter_object_methods);
static struct ubus_object main_object = {
.name = "appfilter",
.type = &main_object_type,
.methods = appfilter_object_methods,
.n_methods = ARRAY_SIZE(appfilter_object_methods),
};
static void appfilter_add_object(struct ubus_object *obj)
{
int ret = ubus_add_object(ubus_ctx, obj);
if (ret != 0)
fprintf(stderr, "Failed to publish object '%s': %s\n", obj->name, ubus_strerror(ret));
}
int appfilter_ubus_init(void)
{
ubus_ctx = ubus_connect("/var/run/ubus.sock");
if (!ubus_ctx)
return -EIO;
appfilter_add_object(&main_object);
ubus_add_uloop(ubus_ctx);
return 0;
}

View File

@ -0,0 +1,25 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
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 __APPFILTER_UBUS_H__
#define __APPFILTER_UBUS_H__
int appfilter_ubus_init(void);
#endif

View File

@ -0,0 +1,185 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
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.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include "appfilter_user.h"
dev_node_t *dev_hash_table[MAX_DEV_NODE_HASH_SIZE];
unsigned int hash_mac(unsigned char *mac)
{
if (!mac)
return 0;
else
return mac[0] & (MAX_DEV_NODE_HASH_SIZE - 1);
}
int hash_appid(int appid){
return appid % (MAX_VISIT_HASH_SIZE - 1);
}
void add_visit_info_node(visit_info_t **head, visit_info_t *node){
if (*head == NULL){
*head = node;
}
else{
node->next = *head;
*head = node;
}
}
void init_dev_node_htable(){
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++){
dev_hash_table[i] = NULL;
}
}
dev_node_t *add_dev_node(char *mac){
unsigned int hash = 0;
hash = hash_mac(mac);
if (hash >= MAX_DEV_NODE_HASH_SIZE){
printf("hash code error %d\n", hash);
return NULL;
}
dev_node_t *node = (dev_node_t *)calloc(1, sizeof(dev_node_t));
if (!node)
return NULL;
strncpy(node->mac, mac, sizeof(node->mac));
if (dev_hash_table[hash] == NULL)
dev_hash_table[hash] = node;
else{
node->next = dev_hash_table[hash];
dev_hash_table[hash] = node;
}
printf("add mac:%s to htable[%d]....success\n", mac, hash);
return node;
}
dev_node_t *find_dev_node(char *mac){
unsigned int hash = 0;
dev_node_t *p = NULL;
hash = hash_mac(mac);
if (hash >= MAX_DEV_NODE_HASH_SIZE){
printf("hash code error %d\n", hash);
return NULL;
}
p = dev_hash_table[hash];
while(p){
if (0 == strncmp(p->mac, mac, sizeof(p->mac))){
return p;
}
p = p->next;
}
return NULL;
}
void dev_foreach(void *arg, iter_func iter){
int i, j;
dev_node_t *node = NULL;
for (i = 0;i < MAX_DEV_NODE_HASH_SIZE; i++){
dev_node_t *node = dev_hash_table[i];
while(node){
iter(arg, node);
node = node->next;
}
}
}
char * format_time(int timetamp){
char time_buf[64] = {0};
time_t seconds = timetamp;
struct tm *auth_tm = localtime(&seconds);
strftime(time_buf, sizeof(time_buf), "%Y %m %d %H:%M:%S", auth_tm);
return strdup(time_buf);
}
void dump_dev_list(void){
int i, j;
int count = 0;
FILE *fp = fopen(OAF_DEV_LIST_FILE, "w");
if (!fp){
return;
}
fprintf(fp, "%-4s %-20s %-20s %-32s\n", "Id", "Mac Addr", "Ip Addr", "Hostname");
for (i = 0;i < MAX_DEV_NODE_HASH_SIZE; i++){
dev_node_t *node = dev_hash_table[i];
while(node){
fprintf(fp, "%-4d %-20s %-20s %-32s\n", i + 1, node->mac, node->ip, node->hostname);
node = node->next;
}
}
EXIT:
fclose(fp);
}
void dump_dev_visit_list(void){
int i, j;
int count = 0;
FILE *fp = fopen(OAF_VISIT_LIST_FILE, "w");
if (!fp){
return;
}
fprintf(fp, "%-4s %-20s %-20s %-8s %-32s %-32s %-32s\n", "Id", "Mac Addr", \
"Ip Addr", "Appid", "First Time", "Latest Time", "Total Time(s)");
for (i = 0;i < MAX_DEV_NODE_HASH_SIZE; i++){
dev_node_t *node = dev_hash_table[i];
while(node){
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++){
visit_info_t *p_info = node->visit_htable[j];
while(p_info){
char *first_time_str = format_time(p_info->first_time);
char *latest_time_str = format_time(p_info->latest_time);
int total_time = p_info->latest_time - p_info->first_time;
fprintf(fp, "%-4d %-20s %-20s %-8d %-32s %-32s %-32d\n",
count, node->mac, node->ip, p_info->appid, first_time_str,
latest_time_str, total_time);
if (first_time_str)
free(first_time_str);
if (latest_time_str)
free(latest_time_str);
p_info = p_info->next;
count++;
if (count > 50)
goto EXIT;
}
}
node = node->next;
}
}
EXIT:
fclose(fp);
}

View File

@ -0,0 +1,99 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
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 __FILTER_USER_H__
#define __FILTER_USER_H__
#define MAX_IP_LEN 32
#define MAX_MAC_LEN 32
#define MAX_VISIT_HASH_SIZE 64
#define MAX_DEV_NODE_HASH_SIZE 64
#define MAX_HOSTNAME_SIZE 64
#define OAF_VISIT_LIST_FILE "/tmp/visit_list"
#define OAF_DEV_LIST_FILE "/tmp/dev_list"
#define MIN_VISIT_TIME 5 // default 5s
#define MAX_APP_STAT_NUM 8
#define MAX_VISITLIST_DUMP_NUM 16
#define MAX_APP_TYPE 16
#define MAX_APP_ID_NUM 128
//extern dev_node_t *dev_hash_table[MAX_DEV_NODE_HASH_SIZE];
/*
{
"mac": "10:bf:48:37:0c:94",
"ip": "192.168.100.244",
"app_num": 0,
"visit_info": [{
"appid": 8002,
"latest_action": 1,
"latest_time": 1602604293,
"total_num": 4,
"drop_num": 4,
"history_info": []
}]
}
*/
/* 单个访问记录结构 */
typedef struct visit_info{
int appid;
int first_time;
int latest_time;
int action;
struct visit_info *next;
}visit_info_t;
/* 用于记录某个app总时间和总流量 */
typedef struct visit_stat{
unsigned long long total_time;
unsigned long long total_down_bytes;
unsigned long long total_up_bytes;
}visit_stat_t;
typedef struct dev_node{
char mac[MAX_MAC_LEN];
char ip[MAX_IP_LEN];
char hostname[MAX_HOSTNAME_SIZE];
visit_info_t *visit_htable[MAX_VISIT_HASH_SIZE];
visit_stat_t stat[MAX_APP_TYPE][MAX_APP_ID_NUM];
struct dev_node *next;
}dev_node_t;
struct app_visit_info{
int app_id;
char app_name[32];
int total_time;
};
struct app_visit_stat_info{
int num;
struct app_visit_info visit_list[MAX_APP_STAT_NUM];
};
typedef void (*iter_func)(void *arg, dev_node_t *dev);
//todo:dev for each
extern dev_node_t *dev_hash_table[MAX_DEV_NODE_HASH_SIZE];
dev_node_t *add_dev_node(char *mac);
void init_dev_node_htable();
void dump_dev_list(void);
void dump_dev_visit_list(void);
dev_node_t *find_dev_node(char *mac);
void dev_foreach(void *arg, iter_func iter);
void add_visit_info_node(visit_info_t **head, visit_info_t *node);
#endif

68
open-app-filter/src/main.c Executable file
View File

@ -0,0 +1,68 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
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.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include "appfilter_user.h"
#include "appfilter_netlink.h"
#include "appfilter_ubus.h"
#include "appfilter_config.h"
void dev_list_timeout_handler(struct uloop_timeout *t){
dump_dev_list();
dump_dev_visit_list();
uloop_timeout_set(t, 5000);
}
struct uloop_timeout dev_tm={
.cb = dev_list_timeout_handler
};
static struct uloop_fd appfilter_nl_fd = {
.cb = appfilter_nl_handler,
};
int main(int argc, char **argv)
{
int ret = 0;
uloop_init();
printf("init appfilter\n");
init_dev_node_htable();
init_app_name_table();
init_app_class_name_table();
if (appfilter_ubus_init() < 0) {
fprintf(stderr, "Failed to connect to ubus\n");
return 1;
}
appfilter_nl_fd.fd = appfilter_nl_init();
uloop_fd_add(&appfilter_nl_fd, ULOOP_READ);
uloop_timeout_set(&dev_tm, 5000);
uloop_timeout_add(&dev_tm);
uloop_run();
uloop_done();
return 0;
}