v5.0.1-Support app access time statistics
This commit is contained in:
parent
cd2387c88a
commit
2489a517e5
22
luci-app-oaf/htdocs/luci-static/resources/echarts.min.js
vendored
Executable file
22
luci-app-oaf/htdocs/luci-static/resources/echarts.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
@ -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
|
||||
|
@ -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);
|
||||
|
24
luci-app-oaf/luasrc/model/cbi/appfilter/dev_status.lua
Executable file
24
luci-app-oaf/luasrc/model/cbi/appfilter/dev_status.lua
Executable 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
|
353
luci-app-oaf/luasrc/view/admin_network/dev_status.htm
Executable file
353
luci-app-oaf/luasrc/view/admin_network/dev_status.htm
Executable 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>
|
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 ;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
8
open-app-filter/files/app_class.txt
Executable file
8
open-app-filter/files/app_class.txt
Executable file
@ -0,0 +1,8 @@
|
||||
1 聊天
|
||||
2 游戏
|
||||
3 视频
|
||||
4 购物
|
||||
5 音乐
|
||||
6 招聘
|
||||
7 下载
|
||||
8 常用网站
|
@ -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
6
open-app-filter/src/Makefile
Executable 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
|
88
open-app-filter/src/appfilter_config.c
Executable file
88
open-app-filter/src/appfilter_config.c
Executable 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);
|
||||
}
|
38
open-app-filter/src/appfilter_config.h
Executable file
38
open-app-filter/src/appfilter_config.h
Executable 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
|
173
open-app-filter/src/appfilter_netlink.c
Executable file
173
open-app-filter/src/appfilter_netlink.c
Executable 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;
|
||||
}
|
||||
|
34
open-app-filter/src/appfilter_netlink.h
Executable file
34
open-app-filter/src/appfilter_netlink.h
Executable 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
|
460
open-app-filter/src/appfilter_ubus.c
Executable file
460
open-app-filter/src/appfilter_ubus.c
Executable 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;
|
||||
}
|
25
open-app-filter/src/appfilter_ubus.h
Executable file
25
open-app-filter/src/appfilter_ubus.h
Executable 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
|
185
open-app-filter/src/appfilter_user.c
Executable file
185
open-app-filter/src/appfilter_user.c
Executable 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);
|
||||
}
|
||||
|
99
open-app-filter/src/appfilter_user.h
Executable file
99
open-app-filter/src/appfilter_user.h
Executable 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
68
open-app-filter/src/main.c
Executable 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user