Merge Mainline

Signed-off-by: CN_SZTL <cnsztl@project-openwrt.eu.org>
This commit is contained in:
CN_SZTL 2021-02-19 03:30:19 +08:00
commit dd4b03a705
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
42 changed files with 1744 additions and 1330 deletions

View File

@ -61,7 +61,9 @@ luci-app-beardropper source: [NateLol/natelol](https://github.com/NateLol/natelo
luci-app-vssr source: [jerrykuku/luci-app-vssr](https://github.com/jerrykuku/luci-app-vssr).<br/>
luci-proto-modemmanager source: [nickberry17/luci-proto-modemmanager](https://github.com/nickberry17/luci-proto-modemmanager).<br/>
luci-theme-infinityfreedom source: [xiaoqingfengATGH/luci-theme-infinityfreedom](https://github.com/xiaoqingfengATGH/luci-theme-infinityfreedom).<br/>
luci-proto-minieap source: [ysc3839/luci-proto-minieap](https://github.com/ysc3839/luci-proto-minieap).
luci-proto-minieap source: [ysc3839/luci-proto-minieap](https://github.com/ysc3839/luci-proto-minieap).<br/>
wrtbwmon source: [brvphoenix/wrtbwmon](https://github.com/brvphoenix/wrtbwmon).<br/>
luci-app-wrtbwmon source: [brvphoenix/luci-app-wrtbwmon](https://github.com/brvphoenix/luci-app-wrtbwmon).
## License
### Depend on their own License.

View File

@ -175,7 +175,7 @@ Package: $(1)$$(ABIV_$(1))
Version: $(VERSION)
$$(call addfield,Depends,$$(Package/$(1)/DEPENDS)
)$$(call addfield,Conflicts,$$(call mergelist,$(CONFLICTS))
)$$(call addfield,Provides,$$(call mergelist,$$(filter-out $(1)$$(ABIV_$(1)),$(PROVIDES)$$(if $$(ABIV_$(1)), $(1) $(foreach provide,$(PROVIDES),$(provide)$$(call GetABISuffix,$(provide))))))
)$$(call addfield,Provides,$$(call mergelist,$$(filter-out $(1)$$(ABIV_$(1)),$(PROVIDES)$$(if $$(ABIV_$(1)), $(1) $(foreach provide,$(PROVIDES),$(provide)$$(ABIV_$(1))))))
)$$(call addfield,Alternatives,$$(call mergelist,$(ALTERNATIVES))
)$$(call addfield,Source,$(SOURCE)
)$$(call addfield,SourceName,$(1)

View File

@ -0,0 +1,23 @@
#
# Copyright (C) 2021 Van Waholtz <vanwaholtz@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-wrtbwmon
PKG_VERSION:=1.6.3
PKG_RELEASE:=1
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=Van Waholtz <vanwaholtz@gmail.com>
LUCI_TITLE:=A Luci module that uses wrtbwmon to track bandwidth usage
LUCI_DEPENDS:=+@BUSYBOX_CONFIG_IP +iptables
LUCI_PKGARCH:=all
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,5 @@
config wrtbwmon 'general'
option enabled '1'
option path '/tmp/usage.db'

View File

@ -0,0 +1,9 @@
#!/bin/sh
[ "$ACTION" = ifup -o "$ACTION" = ifupdate ] || exit 0
[ "$ACTION" = ifupdate -a -z "$IFUPDATE_ADDRESSES" -a -z "$IFUPDATE_DATA" ] && exit 0
/etc/init.d/wrtbwmon restart
logger -t wrtbwmon "Restart for $ACTION of $INTERFACE ($DEVICE)"

View File

@ -0,0 +1,44 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2019 OpenWrt.org
START=99
USE_PROCD=1
NAME=wrtbwmon
PID_FILE=/var/run/wrtbwmon.pid
args=/usr/sbin/wrtbwmon
create_instance() {
procd_open_instance
procd_set_param command $args
procd_set_param respawn
procd_set_param user root
# procd_set_param pidfile $PID_FILE
procd_close_instance
}
service_triggers()
{
procd_add_reload_trigger "$NAME"
}
start_service() {
local db enabled
config_load $NAME
config_get db general path
[ -z "$db" ] && db="/tmp/usage.db"
append args " -46"
append args "-f $db"
append args "-p /tmp/usage.htm"
append args "-u /etc/wrtbwmon.user"
append args "-d"
config_get enabled general enabled
[ "$enabled"0 -eq 0 ] || create_instance
}
stop_service() {
procd_kill wrtbwmon
kill -CONT $(cat $PID_FILE)
}

View File

@ -0,0 +1,20 @@
module("luci.controller.wrtbwmon", package.seeall)
function index()
if not nixio.fs.access("/etc/config/wrtbwmon") then
return
end
entry({"admin", "network", "usage"},
alias("admin", "network", "usage", "details"),
_("Traffic Status"), 60)
entry({"admin", "network", "usage", "details"},
template("wrtbwmon"),
_("Details"), 10).leaf=true
entry({"admin", "network", "usage", "config"},
arcombine(cbi("wrtbwmon/config")),
_("Configuration"), 20).leaf=true
entry({"admin", "network", "usage", "custom"},
form("wrtbwmon/custom"),
_("User file"), 30).leaf=true
end

View File

@ -0,0 +1,48 @@
local lastvalue
local cursor = luci.model.uci.cursor()
local m, s, o
m = Map("wrtbwmon", translate("Usage - Configuration"))
s = m:section(NamedSection, "general", "wrtbwmon", translate("General settings"))
o = s:option(Flag, "enabled", translate("Enabled"))
o.rmempty= true
o = s:option(Value, "path", translate("Database Path"),
translate("This box is used to select the Database path, "
.. "which is /tmp/usage.db by default."))
o:value("/tmp/usage.db")
o:value("/etc/usage.db")
o.rmempty= false
function m.on_parse(self)
lastvalue = cursor:get("wrtbwmon", "general", "path")
end
function o.write(self,section,value)
local fpath = nixio.fs.dirname(value) .. "/"
if not nixio.fs.access(fpath) then
if not nixio.fs.mkdirr(fpath) then
return Value.write(self, section, lastvalue)
end
end
io.popen("/etc/init.d/wrtbwmon stop")
io.popen("mv -f " .. fileRename(lastvalue, "*") .. " ".. fpath)
Value.write(self,section,value)
io.popen("/etc/init.d/wrtbwmon start")
return true
end
function fileRename(fileName, tag)
local idx = fileName:match(".+()%.%w+$")
if(idx) then
return fileName:sub(1, idx-1) .. tag .. fileName:sub(idx, -1)
else
return fileName .. tag
end
end
return m

View File

@ -1,10 +1,11 @@
local USER_FILE_PATH = "/etc/config/wrtbwmon.user"
local USER_FILE_PATH = "/etc/wrtbwmon.user"
local fs = require "nixio.fs"
local f = SimpleForm("wrtbwmon",
translate("Usage - Custom User File"),
translate("This file is used to match users with MAC addresses and it must have the following format: 00:aa:bb:cc:ee:ff,username"))
translate("This file is used to match users with MAC addresses. "
.. "Each line must have the following format: <b><font color=\"red\">\"00:aa:bb:cc:ee:ff,username\"</font></b>."))
local o = f:field(Value, "_custom")
@ -20,4 +21,6 @@ function o.write(self, section, value)
fs.writefile(USER_FILE_PATH, value)
end
f.submit = translate("Submit")
return f

View File

@ -0,0 +1,579 @@
<%#
Licensed to the public under the Apache License 2.0.
-%>
<%
local fs = require "nixio.fs"
local stat = require "luci.tools.status"
local dba = luci.model.uci.cursor():get("wrtbwmon", "general", "path")
-- Function to generate table from string.
local function strToTable(str)
local tb = {}
local cmd = nil
setmetatable(tb, {__index = table.insert})
str:gsub("[^%s,]+", tb)
tb.__index = nil
return tb
end
-- Function to update the mac-hostname table.
local function getmactable(family)
local mactable = {}
local leases = (family == 4 and {stat.dhcp_leases()} or {stat.dhcp6_leases()})[1]
if fs.access("/etc/wrtbwmon.user") then
for line in io.lines("/etc/wrtbwmon.user") do
local macpair = strToTable(line)
mactable[macpair[1]:lower()] = macpair[2]
end
end
for _, line in pairs(leases) do
if line.macaddr and not mactable[line.macaddr:lower()] then
mactable[line.macaddr:lower()] = line.hostname
end
end
return mactable
end
-- Rename the db file for ipv6.
local function fileRename(fn, tag)
local idx = fn:match(".+()%.%w+$")
if(idx) then
return fn:sub(1, idx-1) .. tag .. fn:sub(idx, -1)
else
return fn .. tag
end
end
local function procressData(db, family)
local dbc = (family == 6 and {fileRename(db, ".6")} or {db})[1]
local cmd_setup = "/etc/init.d/wrtbwmon restart"
local cmd_update = "wrtbwmon -" .. family .. " -f " .. db .. " >>/dev/null 2>&1"
local data, total, mactable, firstline = {}, {0.0, 0.0, 0.0, 0.0, 0.0}, getmactable(family), true
local isshow = luci.http.formvalue("isShow")
-- Setup the background update process.
if not fs.access("/var/run/wrtbwmon.pid") then
io.popen(cmd_setup)
else
io.popen(cmd_update)
end
-- Process the database.
for line in io.lines(dbc) do
if firstline then
firstline = false
else
local tbl = strToTable(line)
if isshow == "1" or tbl[8] ~= "0" then
tbl[1] = tbl[1]:lower()
if mactable[tbl[1]] then
tbl[3] = mactable[tbl[1]]
else
tbl[3] = tbl[1]
end
for i = 1,#total do
total[i] = total[i] + (tbl[i+3] .. ".0")
end
data[#data+1] = {tbl[3], tbl[1], unpack(tbl, 4)}
table.insert(data[#data], tbl[2])
end
end
end
-- Transfer the database to js.
luci.http.prepare_content("application/json")
luci.http.write_json({data, total})
return
end
if luci.http.formvalue("proto") == "ipv4" then
procressData(dba, 4)
return
elseif luci.http.formvalue("proto") == "ipv6" then
procressData(dba, 6)
return
end
if luci.http.formvalue("reset") == "1" then
os.execute("ip -4 neigh flush dev br-lan && ip -6 neigh flush dev br-lan")
os.execute("rm -f " .. fileRename(dba, "*") .. " && wrtbwmon -46 -f " .. dba .. " >>/dev/null 2>&1")
luci.http.status(200, "OK")
return
end
-%>
<%+header%>
<style type="text/css">
.showMore.hide {
display: none;
}
.tr.table-totals {
background: #eee !important;
}
.cbi-progressbar {
position: relative;
height: 18px;
margin: 3px 0;
border: thin solid #999;
background: #fff;
}
.cbi-progressbar > div {
width: 0;
height: 100%;
transition: width .1s ease-in;
}
.cbi-progressbar::after {
line-height: normal;
position: absolute;
bottom 0;
left 0;
right 0;
top 0;
overflow: hidden;
content: attr(title);
text-align: center;
white-space: pre;
text-overflow: ellipsis;
}
</style>
<div class="cbi-map">
<h2><%:Usage - Details%></h2>
<div class="cbi-section">
<div class="table">
<div class="tr">
<div class="td left" style="width:10%"><label><%:protocol:%></label></div>
<div class="td left" style="width:30%">
<select id="Select46" style="width:auto">
<option value="ipv4" selected="selected">ipv4</option>
<option value="ipv6">ipv6</option>
</select>
</div>
<div class="td left" style="width:10%"><label for="isShow"><%:Show Zeros:%></label></div>
<div class="td left" style="width:30%">
<input class="cbi-input-checkbox" type="checkbox" id="isShow"/>
</div>
<div class="td right">
<input type="button" id="resetDatabase" class="cbi-button" value="<%:Reset Database%>"/>
</div>
</div>
<div class="tr">
<div class="td left" style="width:10%"><label><%:bandwidth:%></label></div>
<div class="td left" style="width:30%">
<input type="text" id="setBD" value="1M" /><label id="checkBD"></label>
</div>
<div class="td left" style="width:10%">
<label for="showMore"><%:Show More:%></label>
</div>
<div class="td left" style="width:30%">
<input class="cbi-input-checkbox" type="checkbox" id="showMore"/>
</div>
<div class="td"></div>
</div>
</div>
</div>
<div class="cbi-section">
<div class="table">
<div class="tr">
<div class="td left" style="width:70%">
<small><div id="updated" style="display:inline"></div><div id="updating" style="display:inline"></div></small>
</div>
<div class="td right" style="width:30%">
<label for="intervalSelect"><%:Auto update every%></label>
<select id="intervalSelect">
<option value="-1"><%:Disabled%></option>
<option value="1"><%:1 second%></option>
<option value="2" selected="selected"><%:2 seconds%></option>
<option value="5"><%:5 seconds%></option>
<option value="10"><%:10 seconds%></option>
<option value="20"><%:20 seconds%></option>
<option value="30"><%:30 seconds%></option>
<option value="60"><%:60 seconds%></option>
<option value="120"><%:2 minutes%></option>
</select>
</div>
</div>
</div>
<div class="table">
<div class="tr"><div class="td left" width="10%"><%:downflow:%></div><div class="td left" style="width:90%"><div id="downflow" class="cbi-progressbar" title="-"><div></div></div></div></div>
<div class="tr"><div class="td left" width="10%"><%:upflow:%></div><div class="td left" style="width:90%"><div id="upflow" class="cbi-progressbar" title="-"><div></div></div></div></div>
</div>
<div class="table" id="traffic">
<div class="tr table-titles">
<div class="th" id="thClient" style="width:17%"><%:Clients%></div>
<div class="th showMore hide" id="thMAC" style="width:10%"><%:MAC%></div>
<div class="th" id="thDownload" style="width:8%"><%:Download%></div>
<div class="th" id="thUpload" style="width:8%"><%:Upload%></div>
<div class="th" id="thTotalDown" style="width:9%"><%:Total Down%></div>
<div class="th" id="thTotalUp" style="width:9%"><%:Total Up%></div>
<div class="th" id="thTotal" style="width:9%"><%:Total%></div>
<div class="th showMore hide" id="thFirstSeen" style="width:15%"><%:First Seen%></div>
<div class="th showMore hide" id="thLastSeen" style="width:15%"><%:Last Seen%></div>
</div>
<div class="tr placeholder">
<div class="td"><em><%:Collecting data...%></em></div>
</div>
</div>
</div>
</div>
<script src="<%=resource%>/cbi.js"></script>
<script type="text/javascript">//<![CDATA[
(function () {
var sortedId = "thTotal", sortedBy = "desc";
var cachedData = null;
function parseSize(size){
var num = parseFloat((size).match(/^[0-9]+\.?[0-9]*/g));
var base = (size).match(/[KMGTPEZ]/i).toString();
var unit = ["" , "K", "M", "G", "T", "P", "E", "Z"];
var ex = unit.indexOf(base);
return Math.round((num ? num : 1) * (ex != "-1" ? 1024 ** ex : 1));
}
function padstr(str) {
return str < 10 ? '0' + str : str;
}
function dateToString(date) {
var d = new Date((/\W/g).test(date) ? date : date * 1000);
var Y = d.getFullYear(), M = d.getMonth() + 1, D = d.getDate();
var hh = d.getHours(), mm = d.getMinutes(), ss = d.getSeconds();
return Y + '/' + padstr(M) + '/' + padstr(D) + ' ' + padstr(hh) + ':' + padstr(mm) + ':' + padstr(ss);
}
function isArray(obj) {
return obj instanceof Array;
}
function handleError() {
// TODO handle errors
// var message = 'Something went wrong...';
}
function displayTable(eltid) {
var tb = $('traffic'), bdw = parseSize($('setBD').value);
var col = setSortColumn(eltid);
cachedData[0].sort(function(x, y) {
var byCol = x[col] == y[col] ? 1 : col;
var n1 = x[byCol], n2 = y[byCol];
var flag = sortedBy =="desc" ? 1 : -1;
return sortingFunction(n1, n2, byCol) * flag;
});
// display data
// console.time('update_table');
updateTable(tb, cachedData, '<em><%:Loading...%></em>');
// console.timeEnd('update_table');
progressbar('downflow', cachedData[1][0], bdw, true);
progressbar('upflow', cachedData[1][1], bdw, true);
return
}
function updateTable(tb, values, placeholder) {
var dom = document.createDocumentFragment(), nodeLen = tb.childElementCount - 2;
var tbData = values[0], shadowNode, newNode, childTD, tabTitle = tb.firstElementChild;
var showMore = $('showMore').checked;
// Create the shadow node, which will be used in the following.
if (tbData.length > nodeLen) {
if (tb.childElementCount > 2) {
shadowNode = tabTitle.nextElementSibling.cloneNode(true);
} else {
shadowNode = document.createElement('div');
childTD = document.createElement('div');
childTD.appendChild(document.createTextNode(''));
for (var j = 0; j < tabTitle.children.length; j++) {
childTD.className = 'td' + (!showMore && '178'.indexOf(j) != -1 ? ' hide showMore' : '');
childTD.setAttribute('data-title', tabTitle.children[j].innerHTML);
shadowNode.appendChild(childTD.cloneNode(true));
}
shadowNode.firstElementChild.appendChild(document.createElement('br'));
shadowNode.firstElementChild.appendChild(document.createTextNode(''));
}
}
// Update the table data.
for (var i = 0; i < tbData.length; i++) {
if (i < nodeLen) {
newNode = tabTitle.nextElementSibling;
} else {
newNode = shadowNode.cloneNode(true);
newNode.className = 'tr cbi-rowstyle-%d'.format(i % 2 ? 2 : 1);
}
childTD = newNode.firstElementChild;
childTD.title = tbData[i][1];
childTD.lastChild.nodeValue = tbData[i].slice(-1);
for (var j = 0; j < tabTitle.childElementCount; j++, childTD = childTD.nextElementSibling){
childTD.firstChild.nodeValue = ('23456'.indexOf(j) != -1 ?
'%1024.2mB' + ('23'.indexOf(j) != -1 ? '/s' : '') :
'%s').format('78'.indexOf(j) != -1 ? dateToString(tbData[i][j]) : tbData[i][j]);
}
dom.appendChild(newNode);
}
// Remove the table data which has been deleted from the database.
while (tb.childElementCount > 2) {
tb.removeChild(tabTitle.nextElementSibling);
}
//Append the totals or placeholder row.
dom.appendChild(tb.lastElementChild);
newNode = dom.lastElementChild;
if (newNode.classList.contains('table-totals')) {
if (tbData.length == 0) {
while (newNode.firstElementChild.firstChild.nextSibling) {
newNode.removeChild(newNode.lastElementChild);
};
newNode.className = 'tr placeholder';
newNode.firstChild.innerHTML = placeholder;
}
} else {
if (tbData.length > 0) {
dom.replaceChild(shadowNode.cloneNode(true), newNode);
newNode = dom.lastElementChild;
newNode.className = 'tr table-totals';
while (newNode.firstElementChild.firstChild.nextSibling) {
newNode.firstElementChild.removeChild(newNode.firstElementChild.lastChild);
};
newNode.firstElementChild.style.fontWeight = 'bold';
newNode.firstElementChild.nextSibling.style.fontWeight = 'bold';
}
}
if (newNode.classList.contains('table-totals')) {
newNode.firstElementChild.firstChild.nodeValue = !showMore ? '<%:TOTAL%>: ' + tbData.length : '<%:TOTAL%>:';
newNode.firstElementChild.nextSibling.firstChild.nodeValue = !showMore ? '' : tbData.length + ' <%:Clients%>';
for (var j = 0; j < values[1].length; j++) {
newNode.children[j + 2].firstChild.nodeValue = '%1024.2mB'.format(values[1][j]) + (j < 2 ? '/s' : '');
}
}
tb.appendChild(dom);
}
function sortingFunction(x, y, byCol) {
var toHex = false;
var a = x.split(/[^\w\d]/g), b = y.split(/[^\w\d]/g);
if ( byCol == "9" ) {
var ipCk1 = cbi_validators.ipaddr.apply(x) ? 1 : 0;
var ipCk2 = cbi_validators.ipaddr.apply(y) ? 1 : 0;
if (ipCk1 * ipCk2 == 0) {
return ipCk2 - ipCk1;
} else {
a = cbi_validators.ip6addr.apply(x) ? (toHex = true, IPv6(x)) : a;
b = cbi_validators.ip6addr.apply(y) ? (toHex = true, IPv6(y)) : b;
}
}
var len = (a.length <= b.length ? a.length : b.length);
var num1, num2;
for (var i = 0 ; i < len ; i++ ) {
if (byCol == "1" || (byCol == "9" && toHex)) {
num1 = parseInt(a[i], 16);
num2 = parseInt(b[i], 16);
} else if (a[i].match(/[a-z]/ig) || b[i].match(/[a-z]/ig)){
num1 = a[i].toLowerCase();
num2 = b[i].toLowerCase();
} else {
num1 = parseInt(a[i]);
num2 = parseInt(b[i]);
}
if (num1 != num2) return num2 - num1;
}
return "1";
}
function progressbar(query, v, m, byte) {
var pg = $(query),
vn = parseInt(v) || 0,
mn = parseInt(m) || 100,
fv = byte ? String.format('%1024.2mB', v) : v,
pc = ((100 / mn) * vn).toFixed(2),
wt = Math.floor(pc > 100 ? 100 : pc),
bgc = (pc >= 95 ? "red" : (pc >= 80 ? "magenta" : (pc >= 60 ? "yellow" : "lime")));
if (pg) {
pg.firstElementChild.style.width = wt + '%';
pg.firstElementChild.style.background = bgc;
pg.setAttribute('title', '%s/s (%d%%)'.format(fv, pc));
}
}
function $(tid) {
return document.getElementById(tid);
}
function registerTableEventHandlers() {
$('xhr_poll_status').onclick = function() {
var e = $('intervalSelect');
XHR.running() ? (XHR.halt(), e.value = -1) : (XHR.run(), e.value = XHR._q[0].interval);
}
$('traffic').querySelectorAll('.th').forEach( function(e) {
if (e) {
e.addEventListener('click', function () {
displayTable(this.id);
});
}
});
$('intervalSelect').addEventListener('change', function () {
if (this.value > 0) {
XHR._q[0].interval = this.value;
if (!XHR.running()) XHR.run();
} else {
XHR.halt();
setUpdateMessage('');
}
});
$('resetDatabase').addEventListener('click', function () {
if (confirm('<%:This will delete the database file. Are you sure?%>')) {
(new XHR()).post('<%=REQUEST_URI%>', {reset: 1}, function(xhr) {
document.location.reload();
})
}
});
$('setBD').addEventListener('input', function () {
var strTest = (/^[0-9]+\.?[0-9]*[\s]*[KMGTP]?B?(\/s)?$/g).test(this.value);
$("checkBD").innerHTML = strTest ? '\u2714' : '\u2716';
});
$('setBD').addEventListener('focusout', function () {
if ($("checkBD").innerHTML == '\u2716') {
alert('Error! Bandwidth reset!!!');
this.value = '1M';
}
$("checkBD").innerHTML = "";
});
$('Select46').addEventListener('change', function () {
XHR._q[0].data["proto"] = this.value;
});
$('isShow').addEventListener('click', function () {
XHR._q[0].data["isShow"] = this.checked ? 1 : 0;
});
$('showMore').addEventListener('click', function () {
var tot = document.querySelector('.tr.table-totals').firstElementChild;
var showMore = this.checked;
tot.firstChild.nodeValue = '<%:TOTAL%>:' + (showMore ? '' : ' ' + $('traffic').childElementCount - 2);
tot.nextElementSibling.firstChild.nodeValue = showMore ? $('traffic').childElementCount - 2 + ' <%:Clients%>' : '';
document.querySelectorAll('.showMore').forEach(function(e) {
if(e) {
showMore ? e.classList.remove('hide') :e.classList.add('hide');
}
});
if (!showMore && ["thMAC", "thFirstSeen", "thLastSeen"].indexOf(sortedId)!= -1) displayTable("thTotal");
});
}
function updateData() {
var interval = $('intervalSelect').value;
var status = {
proto: $('Select46').value,
isShow: 0
}
XHR.poll(interval, '<%=REQUEST_URI%>', status,
function(x, info) {
// console.time('start');
if (!info) {
handleError();
} else {
cachedData = info;
// Display the sorted values.
displayTable(null);
$('updated').innerHTML = '<%:Last updated %>' + dateToString(Math.round(Date.now() / 1000)) +'<%:.%> ';
}
// console.timeEnd('start');
});
updatePerSec();
}
function updatePerSec() {
var post;
XHR.poll(1, "", "", function(x, data) {
var itv = XHR._q[0].interval;
var sec = XHR._t % itv ? itv - XHR._t % itv : 0;
setUpdateMessage('<%:Updating again in%> <b>' + sec + '</b> <%:seconds.%>');
if(sec == 0) {
setTimeout(function() {
setUpdateMessage('<%:Updating again in%> <b>' + XHR._q[0].interval + '</b> <%:seconds.%>');
}, 50);
}
}, post);
(XHR._q.slice(-1)[0]).xhr[post ? 'post' : 'get'] = function(url, data, callback, timeout) {
callback("", "");
}
}
function setUpdateMessage(msg) {
$('updating').innerHTML = msg;
}
function setSortColumn(eltid) {
var label = ["", "thMAC", "thDownload", "thUpload", "thTotalDown", "thTotalUp", "thTotal", "thFirstSeen", "thLastSeen", "thClient"];
// Remove the old sorted sign.
var e = $(sortedId);
if (e) {
e.innerHTML = e.innerHTML.replace(/\u25B2|\u25BC/, "");
}
// Toggle the sort direction.
if (eltid) {
if ( eltid == sortedId ) {
sortedBy = (sortedBy == "desc") ? "asc" : "desc";
} else {
sortedBy = "desc";
sortedId = eltid;
}
}
e = $(sortedId);
if (e) {
e.innerHTML += (sortedBy == "asc" ? "\u25B2" : "\u25BC");
}
return label.indexOf(sortedId)
}
if (<%=luci.http.write_json(io.popen("opkg status wrtbwmon") == "")%>) {
alert("<%:wrtbwmon is not installed!%>");
} else {
registerTableEventHandlers();
updateData();
}
return 0;
})();
//]]></script>
<%+footer%>

View File

@ -0,0 +1,152 @@
msgid "Traffic Status"
msgstr "流量监控"
msgid "Usage - Configuration"
msgstr "文件设置"
msgid "Details"
msgstr "流量信息"
msgid "Configuration"
msgstr "设置"
msgid "User file"
msgstr "用户文件"
msgid "General settings"
msgstr "通用设置"
msgid "Wrtbwmon is a mointor of the traffic usage."
msgstr "Wrtbwmon是一个流量监控器"
msgid "This box is used to select the Database path, which is /tmp/usage.db by default."
msgstr "该选项用于选择数据存储路径,默认路径为/tmp/usage.db。"
msgid "This box is used to set the total bandwidth (Byte/s), which is 1000000Byte/s by default."
msgstr "该选项用于选设置总的带宽Byte/s默认带宽值1000000Byte/s。"
msgid "Usage - Custom User File"
msgstr "用户文件配置"
msgid "This file is used to match users with MAC addresses. Each line must have the following format: <b><font color=\"red\">\"00:aa:bb:cc:ee:ff,username\"</font></b>."
msgstr "该文件用来进行MAC和主机进行匹配。每行必须满足<b><font color=\"red\">\"00:aa:bb:cc:ee:ff,用户名\"</font></b>的格式"
msgid "Usage - Details"
msgstr "流量详情"
msgid "downflow:"
msgstr "下行:"
msgid "upflow:"
msgstr "上行:"
msgid "protocol:"
msgstr "协议:"
msgid "Reset Database"
msgstr "重置"
msgid "Auto update every"
msgstr "自动刷新"
msgid "Database Path"
msgstr "数据存储位置"
msgid "Show Zeros:"
msgstr "显示0流量:"
msgid "Show More:"
msgstr "显示更多:"
msgid "bandwidth:"
msgstr "带宽:"
msgid "Calculate per host totals"
msgstr "单主机总流量统计"
msgid "Disabled"
msgstr "禁用"
msgid "1 second"
msgstr "1秒"
msgid "2 seconds"
msgstr "2秒"
msgid "5 seconds"
msgstr "5秒"
msgid "10 seconds"
msgstr "10秒"
msgid "20 seconds"
msgstr "20秒"
msgid "30 seconds"
msgstr "30秒"
msgid "40 seconds"
msgstr "40秒"
msgid "50 seconds"
msgstr "50秒"
msgid "60 seconds"
msgstr "60秒"
msgid "2 minutes"
msgstr "2分"
msgid "3 minutes"
msgstr "3分"
msgid "Loading..."
msgstr "数据载入中"
msgid "Clients"
msgstr "客户端"
msgid "MAC Address"
msgstr "MAC地址"
msgid "Download"
msgstr "下载"
msgid "Upload"
msgstr "上传"
msgid "Total Down"
msgstr "总下载"
msgid "Total Up"
msgstr "总上传"
msgid "Total"
msgstr "总计"
msgid "First Seen"
msgstr "初次记录"
msgid "Last Seen"
msgstr "最后记录"
msgid "TOTAL"
msgstr "总共"
msgid "Last updated"
msgstr "最后更新于"
msgid "This will delete the database file. Are you sure?"
msgstr "该操作将删除数据统计文件,确定执行该操作?"
msgid "Updating again in"
msgstr "下次更新将于"
msgid "seconds."
msgstr "秒以后。"
msgid "wrtbwmon is not installed!"
msgstr "wrtbwmon插件尚未安装"
msgid "."
msgstr "。"

View File

@ -0,0 +1,11 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@wrtbwmon[-1]
add ucitrack wrtbwmon
set ucitrack.@wrtbwmon[-1].init=wrtbwmon
commit ucitrack
EOF
rm -f /tmp/luci-indexcache
exit 0

View File

@ -0,0 +1,213 @@
#!/usr/bin/awk
function inInterfaces(host) {
return(interfaces ~ "(^| )" host "($| )")
}
function newRule(arp_ip, ipt_cmd) {
# checking for existing rules shouldn't be necessary if newRule is
# always called after db is read, arp table is read, and existing
# iptables rules are read.
ipt_cmd=iptKey " -t mangle -j RETURN -s " arp_ip
system(ipt_cmd " -C RRDIPT_FORWARD 2>/dev/null || " ipt_cmd " -A RRDIPT_FORWARD")
ipt_cmd=iptKey " -t mangle -j RETURN -d " arp_ip
system(ipt_cmd " -C RRDIPT_FORWARD 2>/dev/null || " ipt_cmd " -A RRDIPT_FORWARD")
}
function delRule(arp_ip, ipt_cmd) {
ipt_cmd=iptKey " -t mangle -D RRDIPT_FORWARD -j RETURN "
system(ipt_cmd "-s " arp_ip " 2>/dev/null")
system(ipt_cmd "-d " arp_ip " 2>/dev/null")
}
function total(i) {
return(bw[i "/in"] + bw[i "/out"])
}
BEGIN {
if (ipv6) {
iptNF = 8
iptKey = "ip6tables"
} else {
iptNF = 9
iptKey = "iptables"
}
}
/^#/ { # get DB filename
FS = ","
dbFile = FILENAME
next
}
# data from database; first file
ARGIND==1 { #!@todo this doesn't help if the DB file is empty.
lb=$1
if (lb !~ "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$") next
if (!(lb in mac)) {
mac[lb] = $1
ip[lb] = $2
inter[lb] = $3
speed[lb "/in"] = 0
speed[lb "/out"]= 0
bw[lb "/in"] = $6
bw[lb "/out"] = $7
firstDate[lb] = $9
lastDate[lb] = $10
ignore[lb] = 1
} else {
if ($9 < firstDate[lb])
firstDate[lb] = $9
if ($10 > lastDate[lb]) {
ip[lb] = $2
inter[lb] = $3
lastDate[lb] = $10
}
bw[lb "/in"] += $6
bw[lb "/out"] += $7
ignore[lb] = 0
}
next
}
# not triggered on the first file
FNR==1 {
FS=" "
if(ARGIND == 2) next
}
# arp: ip hw flags hw_addr mask device
ARGIND==2 {
#!@todo regex match IPs and MACs for sanity
if (ipv6) {
statFlag= ($4 != "FAILED" && $4 != "INCOMPLETE")
macAddr = $5
hwIF = $3
} else {
statFlag= ($3 != "0x0")
macAddr = $4
hwIF = $6
}
lb=$1
if (hwIF != wanIF && statFlag && macAddr ~ "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$") {
hosts[lb] = 1
arp_mac[lb] = macAddr
arp_ip[lb] = $1
arp_inter[lb] = hwIF
arp_bw[lb "/in"] = 0
arp_bw[lb "/out"] = 0
arp_firstDate[lb] = systime()
arp_lastDate[lb] = arp_firstDate[lb]
arp_ignore[lb] = 1
}
next
}
#!@todo could use mangle chain totals or tailing "unnact" rules to
# account for data for new hosts from their first presence on the
# network to rule creation. The "unnact" rules would have to be
# maintained at the end of the list, and new rules would be inserted
# at the top.
ARGIND==3 && NF==iptNF && $1!="pkts" { # iptables input
if (ipv6) {
lfn = 5
tag = "::/0"
} else {
lfn = 6
tag = "0.0.0.0/0"
}
if ($(lfn) != "*") {
m = $(lfn)
n = m "/in"
} else if ($(++lfn) != "*") {
m = $(lfn)
n = m "/out"
} else if ($(++lfn) != tag) {
m = $(lfn)
n = m "/out"
} else { # $(++lfn) != tag
m = $(++lfn)
n = m "/in"
}
if (mode == "diff" || mode == "noUpdate") print n, $2
if (mode != "noUpdate") {
if (inInterfaces(m)) { # if label is an interface
if (!(m in arp_mac)) {
cmd = "cat /sys/class/net/" m "/address"
cmd | getline arp_mac[m]
close(cmd)
if (length(arp_mac[m]) == 0) arp_mac[m] = "00:00:00:00:00:00"
arp_ip[m] = "NA"
arp_inter[m] = m
arp_bw[m "/in"] = 0
arp_bw[m "/out"] = 0
arp_firstDate[m] = systime()
arp_lastDate[m] = arp_firstDate[m]
arp_ignore[lb] = 1
}
} else {
if (!(m in arp_mac)) hosts[m] = 0
else delete hosts[m]
}
if ($2 > 0) {
arp_bw[n] = $2
arp_lastDate[m] = systime()
arp_ignore[m] = 0
}
}
}
END {
if (mode == "noUpdate") exit
for (i in arp_ip) {
lb = arp_mac[i]
if (!arp_ignore[i] || !(lb in mac)) {
ignore[lb] = 0
if (lb in mac) {
bw[lb "/in"] += arp_bw[i "/in"]
bw[lb "/out"] += arp_bw[i "/out"]
lastDate[lb] = arp_lastDate[i]
} else {
bw[lb "/in"] = arp_bw[i "/in"]
bw[lb "/out"] = arp_bw[i "/out"]
firstDate[lb] = arp_firstDate[i]
lastDate[lb] = arp_lastDate[i]
}
mac[lb] = arp_mac[i]
ip[lb] = arp_ip[i]
inter[lb] = arp_inter[i]
if (interval != 0) {
speed[lb "/in"] = int(arp_bw[i "/in"] / interval)
speed[lb "/out"]= int(arp_bw[i "/out"] / interval)
}
}
}
close(dbFile)
for (i in mac) {
if (!ignore[i]) {
print "#mac,ip,iface,speed_in,speed_out,in,out,total,first_date,last_date" > dbFile
OFS=","
for (i in mac)
print mac[i], ip[i], inter[i], speed[i "/in"], speed[i "/out"], bw[i "/in"], bw[i "/out"], total(i), firstDate[i], lastDate[i] > dbFile
close(dbFile)
break
}
}
# for hosts without rules
for (i in hosts)
if (hosts[i]) newRule(i)
else delRule(i)
}

View File

@ -0,0 +1,460 @@
#!/bin/sh
#
# Default input parameters for wrtbwmon.
runMode=0
Monitor46=4
# Some parameters for monitor process.
for46=
updatePID=
logFile=/var/log/wrtbwmon.log
lockFile=/var/lock/wrtbwmon.lock
pidFile=/var/run/wrtbwmon.pid
tmpDir=/var/tmp/wrtbwmon
interval4=0
interval6=0
# Debug parameters for readDB.awk.
mode=
DEBUG=
# Constant parameter for wrtbwmon.
binDir=/usr/sbin
dataDir=/usr/share/wrtbwmon
networkFuncs=/lib/functions/network.sh
uci=`which uci 2>/dev/null`
nslookup=`which nslookup 2>/dev/null`
nvram=`which nvram 2>/dev/null`
chains='INPUT OUTPUT FORWARD'
interfaces='eth0 tun0 br-lan' # in addition to detected WAN
# DNS server for reverse lookups provided in "DNS".
# don't perform reverse DNS lookups by default
DO_RDNS=${DNS-}
header="#mac,ip,iface,speed_in,speed_out,in,out,total,first_date,last_date"
createDbIfMissing() {
[ ! -f "$DB" ] && echo $header > "$DB"
[ ! -f "$DB6" ] && echo $header > "$DB6"
}
checkDbArg() {
[ -z "$DB" ] && echo "ERROR: Missing argument 2 (database file)" && exit 1
}
checkDB() {
[ ! -f "$DB" ] && echo "ERROR: $DB does not exist" && exit 1
[ ! -w "$DB" ] && echo "ERROR: $DB is not writable" && exit 1
[ ! -f "$DB6" ] && echo "ERROR: $DB6 does not exist" && exit 1
[ ! -w "$DB6" ] && echo "ERROR: $DB6 is not writable" && exit 1
}
checkWAN() {
[ -z "$1" ] && echo "Warning: failed to detect WAN interface."
}
lookup() {
local MAC=$1
local IP=$2
local userDB=$3
local USERSFILE=
local USER=
for USERSFILE in $userDB /tmp/dhcp.leases /tmp/dnsmasq.conf /etc/dnsmasq.conf /etc/hosts; do
[ -e "$USERSFILE" ] || continue
case $USERSFILE in
/tmp/dhcp.leases )
USER=$(grep -i "$MAC" $USERSFILE | cut -f4 -s -d' ')
;;
/etc/hosts )
USER=$(grep "^$IP " $USERSFILE | cut -f2 -s -d' ')
;;
* )
USER=$(grep -i "$MAC" "$USERSFILE" | cut -f2 -s -d,)
;;
esac
[ "$USER" = "*" ] && USER=
[ -n "$USER" ] && break
done
if [ -n "$DO_RDNS" -a -z "$USER" -a "$IP" != "NA" -a -n "$nslookup" ]; then
USER=`$nslookup $IP $DNS | awk '!/server can/{if($4){print $4; exit}}' | sed -re 's/[.]$//'`
fi
[ -z "$USER" ] && USER=${MAC}
echo $USER
}
detectIF() {
local IF=
if [ -f "$networkFuncs" ]; then
IF=`. $networkFuncs; network_get_device netdev $1; echo $netdev`
[ -n "$IF" ] && echo $IF && return
fi
if [ -n "$uci" -a -x "$uci" ]; then
IF=`$uci get network.${1}.ifname 2>/dev/null`
[ $? -eq 0 -a -n "$IF" ] && echo $IF && return
fi
if [ -n "$nvram" -a -x "$nvram" ]; then
IF=`$nvram get ${1}_ifname 2>/dev/null`
[ $? -eq 0 -a -n "$IF" ] && echo $IF && return
fi
}
detectLAN() {
[ -e /sys/class/net/br-lan ] && echo br-lan && return
local lan=$(detectIF lan)
[ -n "$lan" ] && echo $lan && return
}
detectWAN() {
local wan=$(detectIF wan)
[ -n "$wan" ] && echo $wan && return
wan=$(ip route show 2>/dev/null | grep default | sed -re '/^default/ s/default.*dev +([^ ]+).*/\1/')
[ -n "$wan" ] && echo $wan && return
[ -f "$networkFuncs" ] && wan=$(. $networkFuncs; network_find_wan wan; echo $wan)
[ -n "$wan" ] && echo $wan && return
}
lockFunc() {
#Realize the lock function by busybox lock or flock command.
# if !(lock -n $lockFile) >/dev/null 2>&1; then
# exit 1
# fi
#The following lock method is realized by other's function.
local attempts=0
local flag=0
while [ "$flag" = 0 ]; do
local tempfile=$(mktemp $tmpDir/lock.XXXXXX)
ln $tempfile $lockFile >/dev/null 2>&1 && flag=1
rm $tempfile
if [ "$flag" = 1 ]; then
[ -n "$DEBUG" ] && echo ${updatePID} "got lock after $attempts attempts"
flag=1
else
sleep 1
attempts=$(($attempts+1))
[ -n "$DEBUG" ] && echo ${updatePID} "The $attempts attempts."
[ "$attempts" -ge 10 ] && exit
fi
done
}
unlockFunc() {
#Realize the lock function by busybox lock or flock command.
# lock -u $lockFile
# rm -f $lockFile
# [ -n "$DEBUG" ] && echo ${updatePID} "released lock"
#The following lock method is realized by other's function.
rm -f $lockFile
[ -n "$DEBUG" ] && echo ${updatePID} "released lock"
}
# chain
newChain() {
local chain=$1
local ipt=$2
# Create the RRDIPT_$chain chain (it doesn't matter if it already exists).
$ipt -t mangle -N RRDIPT_$chain 2> /dev/null
# Add the RRDIPT_$chain CHAIN to the $chain chain if not present
$ipt -t mangle -C $chain -j RRDIPT_$chain 2>/dev/null
if [ $? -ne 0 ]; then
[ -n "$DEBUG" ] && echo "DEBUG: $ipt chain misplaced, recreating it..."
$ipt -t mangle -I $chain -j RRDIPT_$chain
fi
}
# chain tun
newRuleIF() {
local chain=$1
local IF=$2
local ipt=$3
local cmd=
if [ "$chain" = "OUTPUT" ]; then
cmd="$ipt -t mangle -o $IF -j RETURN"
elif [ "$chain" = "INPUT" ]; then
cmd="$ipt -t mangle -i $IF -j RETURN"
fi
[ -n "$cmd" ] && eval $cmd " -C RRDIPT_$chain 2>/dev/null" || eval $cmd " -A RRDIPT_$chain"
}
publish() {
# sort DB
# busybox sort truncates numbers to 32 bits
grep -v '^#' $DB | awk -F, '{OFS=","; a=sprintf("%f",$6/1e6); $6=""; print a,$0}' | tr -s ',' | sort -rn | awk -F, '{OFS=",";$1=sprintf("%f",$1*1e6);print}' > $tmpDir/sorted_${updatePID}.tmp
# create HTML page
local htmPage="$tmpDir/${pb_html##*/}"
rm -f $htmPage
cp $dataDir/usage.htm1 $htmPage
while IFS=, read PEAKUSAGE_IN MAC IP IFACE SPEED_IN SPEED_OUT PEAKUSAGE_OUT TOTAL FIRSTSEEN LASTSEEN
do
echo "
new Array(\"$(lookup $MAC $IP $user_def)\",\"$MAC\",\"$IP\",$SPEED_IN,$SPEED_OUT,
$PEAKUSAGE_IN,$PEAKUSAGE_OUT,$TOTAL,\"$FIRSTSEEN\",\"$LASTSEEN\")," >> $htmPage
done < $tmpDir/sorted_${updatePID}.tmp
echo "0);" >> $htmPage
sed "s/(date)/`date`/" < $dataDir/usage.htm2 >> $htmPage
mv $htmPage "$pb_html"
}
updatePrepare() {
checkDbArg
createDbIfMissing
checkDB
[ -e $tmpDir ] || mkdir -p $tmpDir
for46="$Monitor46"
local timeNow=$(cat /proc/uptime | awk '{print $1}')
if [ -e "$logFile" ]; then
local timeLast4=$(awk -F'[: ]+' '/ipv4/{print $2}' "$logFile")
local timeLast6=$(awk -F'[: ]+' '/ipv6/{print $2}' "$logFile")
interval4=$(awk -v now=$timeNow -v last=$timeLast4 'BEGIN{print (now-last)}');
interval6=$(awk -v now=$timeNow -v last=$timeLast6 'BEGIN{print (now-last)}');
for ii in 4 6; do
[[ -n "$(echo $for46 | grep ${ii})" ]] && {
if [[ "$(eval echo \$interval${ii})" \> "0.9" ]]; then
sed -i "s/^ipv${ii}: [0-9\.]\{1,\}/ipv${ii}: $timeNow/ig" "$logFile"
else
for46=`echo "$for46" | sed "s/${ii}//g"`
fi
}
done
else
echo -e "ipv4: $timeNow\nipv6: $timeNow" >"$logFile"
fi
return 0
}
update() {
updatePID=$( sh -c 'echo $PPID' )
lockFunc
local wan=$(detectWAN)
checkWAN $wan
interfaces="$interfaces $wan"
[ "$for46" = 4 ] && IPT='iptables'
[ "$for46" = 6 ] && IPT='ip6tables'
[ "$for46" = 46 ] && IPT='iptables ip6tables'
for ii in $IPT ; do
if [ -z "$( ${ii}-save | grep RRDIPT )" ]; then
for chain in $chains; do
newChain $chain $ii
done
# track local data
for chain in INPUT OUTPUT; do
for interface in $interfaces; do
[ -n "$interface" ] && [ -e "/sys/class/net/$interface" ] && newRuleIF $chain $interface $ii
done
done
fi
# this will add rules for hosts in arp table
> $tmpDir/${ii}_${updatePID}.tmp
for chain in $chains; do
$ii -nvxL RRDIPT_$chain -t mangle -Z >> $tmpDir/${ii}_${updatePID}.tmp
done
done
[ -f $tmpDir/iptables_${updatePID}.tmp ] && (
awk -v mode="$mode" -v interfaces="$interfaces" -v wanIF="$wan" -v interval=$interval4 \
-v ipv6="0" -f $binDir/readDB.awk \
$DB \
/proc/net/arp \
$tmpDir/iptables_${updatePID}.tmp
)
[ -f $tmpDir/ip6tables_${updatePID}.tmp ] && (
echo "This file is geneated by 'ip -6 neigh'" > $tmpDir/ip6addr_${updatePID}.tmp
`ip -6 neigh >> $tmpDir/ip6addr_${updatePID}.tmp`;
awk -v mode="$mode" -v interfaces="$interfaces" -v wanIF="$wan" -v interval=$interval6 \
-v ipv6="1" -f $binDir/readDB.awk \
"$DB6" \
$tmpDir/ip6addr_${updatePID}.tmp \
$tmpDir/ip6tables_${updatePID}.tmp
)
[ "$Monitor46" = 46 ] && (
cp $DB $DB46
cat $DB6 >> $DB46
awk -f $binDir/readDB.awk "$DB46"
)
[ -n "$pb_html" ] && publish
rm -f $tmpDir/*_${updatePID}.tmp
unlockFunc
}
renamefile() {
local base=$(basename -- "$1")
local ext=$([ -z "${base/*.*/}" ] && echo ".${base##*.}" || echo '')
local base="${base%.*}"
echo "$(dirname $1)/${base}$2$ext" && return
}
ending() {
iptables-save | grep -v RRDIPT | iptables-restore
ip6tables-save | grep -v RRDIPT | ip6tables-restore
if checkPid $pidFile; then
local pid=$( cat $pidFile )
rm -rf $lockFile $logFile $pidFile $tmpDir/*
kill -9 $pid >> /dev/null 2>&1
fi
echo "exit!!"
}
checkPid() {
[ -e "$1" ] && local pid=$(cat $1) || return 1
[ -d "/proc/$pid" ] && {
[ -n "$( cat /proc/$pid/cmdline | grep wrtbwmon )" ] && return 0
}
return 1
}
sleepProcess() {
sleep 1m
kill -CONT $1 >>/dev/null 2>&1
}
loop() {
trap 'ending' INT TERM HUP QUIT
if checkPid $pidFile; then
echo "Another wrtbwmon is on running!!!"
else
local loopPID=$( sh -c 'echo $PPID' )
local SPID=
echo $loopPID > $pidFile
while true ;do
[ -n "$SPID" ] && kill -9 $SPID >>/dev/null 2>&1
sleepProcess $loopPID &
SPID=$!
updatePrepare && update
kill -STOP $loopPID >>/dev/null 2>&1
done
fi
trap INT TERM HUP QUIT
}
tips() {
echo \
"Usage: $0 [options...]
Options:
-k Exit the wrtbwmon!
-f dbfile Set the DB file path
-u usrfile Set the user_def file path
-p htmlfile Set the publish htm file path
-d Enter the foreground mode.
-D Enter the daemo mode.
-4 Listen to ipv4 only.
-6 Listen to ipv6 only.
-46 Listen to ipv4 and ipv6.
Note: [user_file] is an optional file to match users with MAC addresses.
Its format is \"00:MA:CA:DD:RE:SS,username\", with one entry per line."
}
############################################################
while [ $# != 0 ];do
case $1 in
"-k" )
/etc/init.d/wrtbwmon stop
exit 0
;;
"-f" )
shift
if [ $# -gt 0 ];then
DB=$1
DB6="$(renamefile $DB .6)"
DB46="$(renamefile $DB .46)"
else
echo "No db file path seted, exit!!"
exit 1
fi
;;
"-u")
shift
if [ $# -gt 0 ];then
user_def=$1
else
echo "No user define file path seted, exit!!"
exit 1
fi
;;
"-p")
shift
if [ $# -gt 0 ];then
pb_html=$1
else
echo "No publish html file path seted, exit!!"
exit 1
fi
;;
"-d")
runMode=1
;;
"-D")
runMode=2
;;
"-4")
Monitor46=4
;;
"-6")
Monitor46=6
;;
"-46")
Monitor46=46
;;
"&&" | "||" | ";")
break
;;
"*")
tips
;;
esac
shift
done
if [ "$runMode" = '1' ]; then
loop
elif [ "$runMode" = '2' ]; then
loop >>/dev/null 2>&1 &
else
updatePrepare && update
fi

View File

@ -0,0 +1,34 @@
<html><head><title>Traffic</title>
<script type="text/javascript">
function getSize(size) {
if (size === 0) return '0 ';
var prefix=["","k","M","G","T","P","E","Z"];
var base=1024, precision = 1;
var pos=Math.floor(Math.log(size)/Math.log(base));
if (pos > 2) precision=100;
return (Math.round(size/Math.pow(base,pos)*precision)/precision)+' '+prefix[pos];
}
function padstr(str) {
return str < 10 ? '0' + str : str;
}
function dateToString(date) {
var d = new Date((/\W/g).test(date) ? date : date * 1000);
var Y = d.getFullYear(), M = d.getMonth(), D = d.getDate();
var hh = d.getHours(), mm = d.getMinutes(), ss = d.getSeconds();
return Y + '/' + padstr(M) + '/' + padstr(D) + ' ' + padstr(hh) + ':' + padstr(mm) + ':' + padstr(ss);
}
</script></head>
<body><h1>Total Usage:</h1>
<table border="1">
<tr bgcolor=silver>
<th>User</th>
<th>Down Speed</th>
<th>Up Speed</th>
<th>Download</th>
<th>Upload</th>
<th>Total</th>
<th>First seen</th>
<th>Last seen</th>
</tr>
<script type="text/javascript">
var values = new Array(

View File

@ -0,0 +1,18 @@
var speedIn = 0;
var speedOut = 0;
var totalIn = 0;
var totalOut = 0;
for (i=0; i < values.length-1; i++) {
speedIn += values[i][3];
speedOut += values[i][4]
totalIn += values[i][5];
totalOut += values[i][6];
document.write("<tr><td><div title=\"" + values[i][1] + " (" + values[i][2] + ")" + "\">" + values[i][0] + "</div></td>");
for (j=3; j < 8; j++)
document.write("<td>" + getSize(values[i][j]) + ((j == 3) || (j ==4) ? 'B/s' : 'B') + "</td>");
document.write("<td>" + dateToString(values[i][8]) + "</td><td>" + dateToString(values[i][9]) + "</td></tr>");
}
document.write("<tr><td>TOTAL</td><td>" + getSize(speedIn) + "B/s" + "</td><td>" + getSize(speedOut) + "B/s" + "</td><td>" + getSize(totalIn) + "</td><td>" + getSize(totalOut) + "</td><td>" + getSize(totalIn + totalOut) + "</td><td></td><td></td></tr>");
</script></table>
<br /><small>This page was generated on (date)</small>
</body></html>

View File

@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=naiveproxy
PKG_VERSION:=88.0.4324.96-1
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/klzgrad/naiveproxy/tar.gz/v$(PKG_VERSION)?

View File

@ -0,0 +1,22 @@
From 63f89cd454bcd1f2cdd14e63de0587a2f692cd07 Mon Sep 17 00:00:00 2001
From: klzgrad <kizdiv@gmail.com>
Date: Sun, 31 Jan 2021 20:15:28 +0800
Subject: [PATCH] Fix OpenWrt x86 builds
---
src/build/toolchain/linux/BUILD.gn | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
--- a/src/build/toolchain/linux/BUILD.gn
+++ b/src/build/toolchain/linux/BUILD.gn
@@ -99,8 +99,8 @@ clang_toolchain("clang_x86") {
clang_toolchain("clang_x86_openwrt") {
# Output linker map files for binary size analysis.
enable_linker_map = true
- extra_cppflags = "--target=i386-openwrt-linux-musl -D_LIBCPP_HAS_MUSL_LIBC -D__UCLIBC__"
- extra_ldflags = "--target=i386-openwrt-linux-musl"
+ extra_cppflags = "--target=i486-openwrt-linux-musl -D_LIBCPP_HAS_MUSL_LIBC -D__UCLIBC__"
+ extra_ldflags = "--target=i486-openwrt-linux-musl"
toolchain_args = {
current_cpu = "x86"

View File

@ -9,11 +9,13 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=tinyfecVPN
PKG_VERSION:=20210116.0
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/wangyu-/tinyfecVPN/tar.gz/$(PKG_VERSION)?
PKG_HASH:=4e7494dcee9398a51497689c319bea7184152780fc06a2e05b6f9c16a6a9ea6e
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/wangyu-/tinyfecVPN.git
PKG_SOURCE_DATE:=2021-01-17
PKG_SOURCE_VERSION:=c9b98721ea1d62ab8e1c4c9e2a0ed04394dfe0ae
PKG_MIRROR_HASH:=28cdadf0f162e52d3c6b10b6a6cdcd22e80783b36b33e341b5dea156bfb75b74
PKG_LICENSE:=MIT
PKG_MAINTAINER:=Yu Wang
@ -22,7 +24,7 @@ PKG_BUILD_PARALLEL:=1
include $(INCLUDE_DIR)/package.mk
define Package/tinyfecVPN
define Package/tinyfecvpn
SECTION:=net
CATEGORY:=Network
SUBMENU:=VPN
@ -31,7 +33,7 @@ define Package/tinyfecVPN
DEPENDS:=+libpthread +libstdcpp +kmod-tun
endef
define Package/tinyfecVPN/description
define Package/tinyfecvpn/description
A Lightweight VPN with Build-in Forward Error Correction Support (or A Network Improving Tool
which works at VPN mode). Improves your Network Quality on a High-latency Lossy Link.
endef
@ -41,12 +43,14 @@ MAKE_FLAGS += cross
define Build/Configure
sed -i 's/cc_cross=.*/cc_cross=$(TARGET_CXX)/g' $(PKG_BUILD_DIR)/makefile
sed -i '/\*gitversion/d' $(PKG_BUILD_DIR)/makefile
echo 'const char *gitversion = "$(PKG_VERSION)";' > $(PKG_BUILD_DIR)/git_version.h
echo 'const char *gitversion = "$(PKG_SOURCE_VERSION)";' > $(PKG_BUILD_DIR)/git_version.h
$(call Build/Configure/Default)
endef
define Package/tinyfecVPN/install
define Package/tinyfecvpn/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/tinyvpn_cross $(1)/usr/bin/tinyvpn
endef
$(eval $(call BuildPackage,tinyfecVPN))
$(eval $(call BuildPackage,tinyfecvpn))

View File

@ -139,8 +139,7 @@ _exit() {
}
first_type() {
local ret=$(which "/bin/${path_name}" "${TMP_BIN_PATH}/${path_name}" "${path_name}" "$@" | head -n1)
echo ${ret:=$@}
type -t -p "/bin/${1}" -p "${TMP_BIN_PATH}/${1}" -p "${1}" "$@" | head -n1
}
ln_start_bin() {

View File

@ -232,7 +232,7 @@ local function processData(szType, content)
local userinfo = hostInfo[1]
local password = userinfo
result.alias = UrlDecode(alias)
result.type = "trojan"
result.type = luci.sys.exec('type -t -p trojan') ~= "" and "trojan" or "v2ray"
result.v2ray_protocol = "trojan"
result.server = host[1]
-- 按照官方的建议 默认验证ssl证书

View File

@ -1,17 +0,0 @@
# Copyright (C) 2016 Openwrt.org
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for Wrtbwmon
LUCI_DEPENDS:=+luci-app-nlbwmon
LUCI_PKGARCH:=all
PKG_VERSION:=1.0
PKG_RELEASE:=7
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -1,562 +0,0 @@
var wrt = {
// variables for auto-update, interval is in seconds
scheduleTimeout: undefined,
updateTimeout: undefined,
isScheduled: true,
interval: 5,
// option on whether to show per host sub-totals
perHostTotals: false,
// variables for sorting
sortData: {
column: 7,
elId: 'thTotal',
dir: 'desc',
cache: {}
}
};
(function () {
var oldDate, oldValues = [];
// find base path
var re = /(.*?admin\/nlbw\/[^/]+)/;
var basePath = window.location.pathname.match(re)[1];
//----------------------
// HELPER FUNCTIONS
//----------------------
/**
* Human readable text for size
* @param size
* @returns {string}
*/
function getSize(size) {
var prefix = [' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z'];
var precision, base = 1000, pos = 0;
while (size > base) {
size /= base;
pos++;
}
if (pos > 2) precision = 1000; else precision = 1;
return (Math.round(size * precision) / precision) + ' ' + prefix[pos] + 'B';
}
/**
* Human readable text for date
* @param date
* @returns {string}
*/
function dateToString(date) {
return date.toString().substring(0, 24);
}
/**
* Gets the string representation of the date received from BE
* @param value
* @returns {*}
*/
function getDateString(value) {
var tmp = value.split('_'),
str = tmp[0].split('-').reverse().join('-') + 'T' + tmp[1];
return dateToString(new Date(str));
}
/**
* Create a `tr` element with content
* @param content
* @returns {string}
*/
function createTR(content) {
var res = '<tr';
res += ' class="tr">';
res += content;
res += '</tr>';
return res;
}
/**
* Create a `th` element with content and options
* @param content
* @param opts
* @returns {string}
*/
function createTH(content, opts) {
opts = opts || {};
var res = '<th';
if (opts.right) {
res += ' align="right"';
}
if (opts.title) {
res += ' title="' + opts.title + '"';
}
if (opts.id) {
res += ' id="' + opts.id + '"';
}
res += ' class="th">';
res += content;
res += '</th>';
return res;
}
/**
* Create a `td` element with content and options
* @param content
* @param opts
* @returns {string}
*/
function createTD(content, opts) {
opts = opts || {};
var res = '<td';
if (opts.right) {
res += ' align="right"';
}
if (opts.title) {
res += ' title="' + opts.title + '"';
}
res += ' class="td">';
res += content;
res += '</td>';
return res;
}
/**
* Returns true if obj is instance of Array
* @param obj
* @returns {boolean}
*/
function isArray(obj) {
return obj instanceof Array;
}
//----------------------
// END HELPER FUNCTIONS
//----------------------
/**
* Handle the error that happened during the call to the BE
*/
function handleError() {
// TODO handle errors
// var message = 'Something went wrong...';
}
/**
* Handle the new `values` that were received from the BE
* @param values
* @returns {string}
*/
function handleValues(values) {
if (!isArray(values)) return '';
// find data and totals
var res = parseValues(values);
var data = res[0];
var totals = res[1];
// aggregate (sub-total) by hostname (or MAC address) after the global totals are computed, before sort and display
aggregateHostTotals(data);
// store them in cache for quicker re-rendering
wrt.sortData.cache.data = data;
wrt.sortData.cache.totals = totals;
renderTableData(data, totals);
}
/**
* Renders the table body
* @param data
* @param totals
*/
function renderTableData(data, totals) {
// sort data
data.sort(sortingFunction);
// display data
document.getElementById('tableBody').innerHTML = getDisplayData(data, totals);
// set sorting arrows
var el = document.getElementById(wrt.sortData.elId);
if (el) {
el.innerHTML = el.innerHTML + (wrt.sortData.dir === 'desc' ? '&#x25BC' : '&#x25B2');
}
// register table events
registerTableEventHandlers();
}
/**
* Parses the values and returns a data array, where each element in the data array is an array with two elements,
* and a totals array, that holds aggregated values for each column.
* The first element of each row in the data array, is the HTML output of the row as a `tr` element
* and the second is the actual data:
* [ result, data ]
* @param values The `values` array
* @returns {Array}
*/
function parseValues(values) {
var data = [], totals = [0, 0, 0, 0, 0];
for (var i = 0; i < values.length; i++) {
var d = parseValueRow(values[i]);
if (d[1]) {
data.push(d);
// get totals
for (var j = 0; j < totals.length; j++) {
totals[j] += d[1][3 + j];
}
}
}
return [data, totals];
}
/**
* Parse each row in the `values` array and return an array with two elements.
* The first element is the HTML output of the row as a `tr` element and the second is the actual data
* [ result, data ]
* @param data A row from the `values` array
* @returns {[ string, [] ]}
*/
function parseValueRow(data) {
// check if data is array
if (!isArray(data)) return [''];
// find old data
var oldData;
for (var i = 0; i < oldValues.length; i++) {
var cur = oldValues[i];
// compare mac addresses and ip addresses
if (oldValues[i][1] === data[1] && oldValues[i][2] === data[2]) {
oldData = cur;
break;
}
}
// find download and upload speeds
var dlSpeed = 0, upSpeed = 0;
if (oldData) {
var now = new Date(),
seconds = (now - oldDate) / 1000;
dlSpeed = (data[3] - oldData[3]) / seconds;
upSpeed = (data[4] - oldData[4]) / seconds;
}
// create rowData
var rowData = [];
for (var j = 0; j < data.length; j++) {
rowData.push(data[j]);
if (j === 2) {
rowData.push(dlSpeed, upSpeed);
}
}
// create displayData
var displayData = [
createTD(data[0] + '<br />' + data[2], {title: data[1]}),
createTD(getSize(dlSpeed) + '/s', {right: true}),
createTD(getSize(upSpeed) + '/s', {right: true}),
createTD(getSize(data[3]), {right: true}),
createTD(getSize(data[4]), {right: true}),
createTD(getSize(data[5]), {right: true}),
createTD(getDateString(data[6])),
createTD(getDateString(data[7]))
];
// display row data
var result = '';
for (var k = 0; k < displayData.length; k++) {
result += displayData[k];
}
result = createTR(result);
return [result, rowData];
}
/**
* Creates the HTML output based on the `data` and `totals` inputs
* @param data
* @param totals
* @returns {string} HTML output
*/
function getDisplayData(data, totals) {
var result =
createTH('客户端', {id: 'thClient'}) +
createTH('下载带宽', {id: 'thDownload'}) +
createTH('上传带宽', {id: 'thUpload'}) +
createTH('总下载流量', {id: 'thTotalDown'}) +
createTH('总上传流量', {id: 'thTotalUp'}) +
createTH('流量合计', {id: 'thTotal'}) +
createTH('首次上线时间', {id: 'thFirstSeen'}) +
createTH('最后上线时间', {id: 'thLastSeen'});
result = createTR(result);
for (var k = 0; k < data.length; k++) {
result += data[k][0];
}
var totalsRow = createTH('总计');
for (var m = 0; m < totals.length; m++) {
var t = totals[m];
totalsRow += createTD(getSize(t) + (m < 2 ? '/s' : ''), {right: true});
}
result += createTR(totalsRow);
return result;
}
/**
* Calculates per host sub-totals and adds them in the data input
* @param data The data input
*/
function aggregateHostTotals(data) {
if (!wrt.perHostTotals) return;
var curHost = 0, insertAt = 1;
while (curHost < data.length && insertAt < data.length) {
// grab the current hostname/mac, and walk the data looking for rows with the same host/mac
var hostName = data[curHost][1][0].toLowerCase();
for (var k = curHost + 1; k < data.length; k++) {
if (data[k][1][0].toLowerCase() === hostName) {
// this is another row for the same host, group it with any other rows for this host
data.splice(insertAt, 0, data.splice(k, 1)[0]);
insertAt++;
}
}
// if we found more than one row for the host, add a subtotal row
if (insertAt > curHost + 1) {
var hostTotals = [data[curHost][1][0], '', '', 0, 0, 0, 0, 0];
for (var i = curHost; i < insertAt && i < data.length; i++) {
for (var j = 3; j < hostTotals.length; j++) {
hostTotals[j] += data[i][1][j];
}
}
var hostTotalRow = createTH(data[curHost][1][0] + '<br/> (host total)', {title: data[curHost][1][1]});
for (var m = 3; m < hostTotals.length; m++) {
var t = hostTotals[m];
hostTotalRow += createTD(getSize(t) + (m < 5 ? '/s' : ''), {right: true});
}
hostTotalRow = createTR(hostTotalRow);
data.splice(insertAt, 0, [hostTotalRow, hostTotals]);
}
curHost = insertAt;
insertAt = curHost + 1;
}
}
/**
* Sorting function used to sort the `data`. Uses the global sort settings
* @param x first item to compare
* @param y second item to compare
* @returns {number} 1 for desc, -1 for asc, 0 for equal
*/
function sortingFunction(x, y) {
// get data from global variable
var sortColumn = wrt.sortData.column, sortDirection = wrt.sortData.dir;
var a = x[1][sortColumn];
var b = y[1][sortColumn];
if (a === b) {
return 0;
} else if (sortDirection === 'desc') {
return a < b ? 1 : -1;
} else {
return a > b ? 1 : -1;
}
}
/**
* Sets the relevant global sort variables and re-renders the table to apply the new sorting
* @param elId
* @param column
*/
function setSortColumn(elId, column) {
if (column === wrt.sortData.column) {
// same column clicked, switch direction
wrt.sortData.dir = wrt.sortData.dir === 'desc' ? 'asc' : 'desc';
} else {
// change sort column
wrt.sortData.column = column;
// reset sort direction
wrt.sortData.dir = 'desc';
}
wrt.sortData.elId = elId;
// render table data from cache
renderTableData(wrt.sortData.cache.data, wrt.sortData.cache.totals);
}
/**
* Registers the table events handlers for sorting when clicking the column headers
*/
function registerTableEventHandlers() {
// note these ordinals are into the data array, not the table output
document.getElementById('thClient').addEventListener('click', function () {
setSortColumn(this.id, 0); // hostname
});
document.getElementById('thDownload').addEventListener('click', function () {
setSortColumn(this.id, 3); // dl speed
});
document.getElementById('thUpload').addEventListener('click', function () {
setSortColumn(this.id, 4); // ul speed
});
document.getElementById('thTotalDown').addEventListener('click', function () {
setSortColumn(this.id, 5); // total down
});
document.getElementById('thTotalUp').addEventListener('click', function () {
setSortColumn(this.id, 6); // total up
});
document.getElementById('thTotal').addEventListener('click', function () {
setSortColumn(this.id, 7); // total
});
}
/**
* Fetches and handles the updated `values` from the BE
* @param once If set to true, it re-schedules itself for execution based on selected interval
*/
function receiveData(once) {
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function () {
// noinspection EqualityComparisonWithCoercionJS
if (this.readyState == 4 && this.status == 200) {
var re = /(var values = new Array[^;]*;)/,
match = ajax.responseText.match(re);
if (!match) {
handleError();
} else {
// evaluate values
eval(match[1]);
//noinspection JSUnresolvedVariable
var v = values;
if (!v) {
handleError();
} else {
handleValues(v);
// set old values
oldValues = v;
// set old date
oldDate = new Date();
document.getElementById('updated').innerHTML = '数据更新时间 ' + dateToString(oldDate);
}
}
var int = wrt.interval;
if (!once && int > 0) reschedule(int);
}
};
ajax.open('GET', basePath + '/usage_data', true);
ajax.send();
}
/**
* Registers DOM event listeners for user interaction
*/
function addEventListeners() {
document.getElementById('intervalSelect').addEventListener('change', function () {
var int = wrt.interval = this.value;
if (int > 0) {
// it is not scheduled, schedule it
if (!wrt.isScheduled) {
reschedule(int);
}
} else {
// stop the scheduling
stopSchedule();
}
});
document.getElementById('resetDatabase').addEventListener('click', function () {
if (confirm('This will delete the database file. Are you sure?')) {
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function () {
// noinspection EqualityComparisonWithCoercionJS
if (this.readyState == 4 && this.status == 204) {
location.reload();
}
};
ajax.open('GET', basePath + '/usage_reset', true);
ajax.send();
}
});
document.getElementById('perHostTotals').addEventListener('change', function () {
wrt.perHostTotals = !wrt.perHostTotals;
});
}
//----------------------
// AUTO-UPDATE
//----------------------
/**
* Stop auto-update schedule
*/
function stopSchedule() {
window.clearTimeout(wrt.scheduleTimeout);
window.clearTimeout(wrt.updateTimeout);
setUpdateMessage('');
wrt.isScheduled = false;
}
/**
* Start auto-update schedule
* @param seconds
*/
function reschedule(seconds) {
wrt.isScheduled = true;
seconds = seconds || 60;
updateSeconds(seconds);
wrt.scheduleTimeout = window.setTimeout(receiveData, seconds * 1000);
}
/**
* Sets the text of the `#updating` element
* @param msg
*/
function setUpdateMessage(msg) {
document.getElementById('updating').innerHTML = msg;
}
/**
* Updates the 'Updating in X seconds' message
* @param start
*/
function updateSeconds(start) {
setUpdateMessage('倒数 <b><font color="blue">' + start + '</font></b> 秒后刷新.');
if (start > 0) {
wrt.updateTimeout = window.setTimeout(function () {
updateSeconds(start - 1);
}, 1000);
}
}
//----------------------
// END AUTO-UPDATE
//----------------------
/**
* Check for dependency, and if all is well, run callback
* @param cb Callback function
*/
function checkForDependency(cb) {
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function () {
// noinspection EqualityComparisonWithCoercionJS
if (this.readyState == 4 && this.status == 200) {
// noinspection EqualityComparisonWithCoercionJS
if (ajax.responseText == "1") {
cb();
} else {
alert("wrtbwmon is not installed!");
}
}
};
ajax.open('GET', basePath + '/check_dependency', true);
ajax.send();
}
checkForDependency(function () {
// register events
addEventListeners();
// Main entry point
receiveData();
});
})();

View File

@ -1,43 +0,0 @@
module("luci.controller.wrtbwmon", package.seeall)
function index()
entry({"admin", "nlbw", "usage"}, alias("admin", "nlbw", "usage", "details"), _("Usage"), 60)
entry({"admin", "nlbw", "usage", "details"}, template("wrtbwmon"), _("Details"), 10).leaf=true
entry({"admin", "nlbw", "usage", "config"}, cbi("wrtbwmon/config"), _("Configuration"), 20).leaf=true
entry({"admin", "nlbw", "usage", "custom"}, form("wrtbwmon/custom"), _("User file"), 30).leaf=true
entry({"admin", "nlbw", "usage", "check_dependency"}, call("check_dependency")).dependent=true
entry({"admin", "nlbw", "usage", "usage_data"}, call("usage_data")).dependent=true
entry({"admin", "nlbw", "usage", "usage_reset"}, call("usage_reset")).dependent=true
end
function usage_database_path()
local cursor = luci.model.uci.cursor()
if cursor:get("wrtbwmon", "general", "persist") == "1" then
return "/etc/config/usage.db"
else
return "/tmp/usage.db"
end
end
function check_dependency()
local ret = "0"
if require("luci.model.ipkg").installed('iptables') then
ret = "1"
end
luci.http.prepare_content("text/plain")
luci.http.write(ret)
end
function usage_data()
local db = usage_database_path()
local publish_cmd = "wrtbwmon publish " .. db .. " /tmp/usage.htm /etc/config/wrtbwmon.user"
local cmd = "wrtbwmon update " .. db .. " && " .. publish_cmd .. " && cat /tmp/usage.htm"
luci.http.prepare_content("text/html")
luci.http.write(luci.sys.exec(cmd))
end
function usage_reset()
local db = usage_database_path()
local ret = luci.sys.call("wrtbwmon update " .. db .. " && rm " .. db)
luci.http.status(204)
end

View File

@ -1,18 +0,0 @@
local m = Map("wrtbwmon", translate("Details"))
local s = m:section(NamedSection, "general", "wrtbwmon", translate("General settings"))
local o = s:option(Flag, "persist", translate("Persist database"),
translate("Check this to persist the database file"))
o.rmempty = false
function o.write(self, section, value)
if value == '1' then
luci.sys.call("mv /tmp/usage.db /etc/config/usage.db")
elseif value == '0' then
luci.sys.call("mv /etc/config/usage.db /tmp/usage.db")
end
return Flag.write(self, section ,value)
end
return m

View File

@ -1,46 +0,0 @@
<%+header%>
<h2><%=translate("Usage")%></h2>
<p style="position:relative;">
<button id="resetDatabase" class="cbi-button" style="position:absolute;right:0;bottom:0;"><%=translate("Reset Database")%></button>
</p>
<p>
<small><span id="updated"></span> <span id="updating"></span></small>
<span style="float:right;text-align:right;">
<label>
<small><%=translate("Auto Refresh Interval")%></small>
<select id="intervalSelect" style="width:90px;height:20px;font-size:11px;">
<option value="-1"><%=translate("Disabled")%></option>
<option value="1">1 <%=translate("Second")%></option>
<option value="2">2 <%=translate("Seconds")%></option>
<option value="3">3 <%=translate("Seconds")%></option>
<option value="4">4 <%=translate("Seconds")%></option>
<option value="5" selected="selected">5 <%=translate("Seconds")%></option>
<option value="10">10 <%=translate("Seconds")%></option>
<option value="20">20 <%=translate("Seconds")%></option>
<option value="30">30 <%=translate("Seconds")%></option>
<option value="40">40 <%=translate("Seconds")%></option>
<option value="50">50 <%=translate("Seconds")%></option>
<option value="60">60 <%=translate("Seconds")%></option>
<option value="120">2 <%=translate("Minutes")%></option>
<option value="180">3 <%=translate("Minutes")%></option>
<option value="240">4 <%=translate("Minutes")%></option>
<option value="360">5 <%=translate("Minutes")%></option>
</select>
</label>
<br/>
<label>
<small><%=translate("Per-host Totals")%></small>
<input id="perHostTotals" type="checkbox" style="vertical-align:middle;"/>
</label>
<!-- label>
<small>Show per host aggregates only</small>
<input id="showPerHostTotalsOnly" type="checkbox"/>
</label -->
</span>
</p>
<table class="table">
<tbody id="tableBody"><tr><td><%=translate("Loading...")%></td></tr></tbody>
</table>
<!--suppress HtmlUnknownTarget -->
<script src="/luci-static/wrtbwmon.js"></script>
<%+footer%>

View File

@ -1,56 +0,0 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Usage"
msgstr "实时流量监测"
msgid "Details"
msgstr "详细信息"
msgid "Configuration"
msgstr "配置"
msgid "User file"
msgstr "自定义主机信息"
msgid "Usage - Configuration"
msgstr "详细设置"
msgid "General settings"
msgstr "通用设置"
msgid "Persist database"
msgstr "写入数据库到硬盘"
msgid "Check this to persist the database file"
msgstr "把统计数据写入 /etc/config 中避免重启或者升级后丢失"
msgid "Usage - Custom User File"
msgstr "自定义MAC地址对应的主机名"
msgid "This file is used to match users with MAC addresses and it must have the following format: 00:aa:bb:cc:ee:ff,username"
msgstr "每一行的格式为 00:aa:bb:cc:ee:ff,username"
msgid "Reset Database"
msgstr "重置数据库"
msgid "Auto Refresh Interval"
msgstr "自动刷新间隔"
msgid "Disabled"
msgstr "禁用"
msgid "Second"
msgstr "秒"
msgid "Seconds"
msgstr "秒"
msgid "Minutes"
msgstr "分钟"
msgid "Per-host Totals"
msgstr "合并每一客户端数据"
msgid "Loading..."
msgstr "加载中..."

View File

@ -1,4 +0,0 @@
config wrtbwmon 'general'
option persist '0'

View File

@ -1,22 +0,0 @@
#!/bin/sh /etc/rc.common
#
# start/stop wrtbwmon bandwidth monitor
### BEGIN INIT INFO
# Provides: wrtbwmon
# Required-Start: $network $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: iptables-based bandwidth monitor
### END INIT INFO
START=91
start(){
/usr/sbin/wrtbwmon setup /tmp/usage.db
}
stop(){
/usr/sbin/wrtbwmon remove
}

View File

@ -1,14 +0,0 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete firewall.wrtbwmon
set firewall.wrtbwmon=include
set firewall.wrtbwmon.type=script
set firewall.wrtbwmon.path='/etc/wrtbwmon.include'
set firewall.wrtbwmon.reload=1
commit firewall
EOF
/etc/init.d/wrtbwmon enable
/etc/init.d/wrtbwmon start
exit 0

View File

@ -1 +0,0 @@
/etc/init.d/wrtbwmon restart >/dev/null 2>&1

View File

@ -1,157 +0,0 @@
#!/usr/bin/awk
function inInterfaces(host){
return(interfaces ~ "(^| )"host"($| )")
}
function newRule(arp_ip,
ipt_cmd){
# checking for existing rules shouldn't be necessary if newRule is
# always called after db is read, arp table is read, and existing
# iptables rules are read.
ipt_cmd="iptables -t mangle -j RETURN -s " arp_ip
system(ipt_cmd " -C RRDIPT_FORWARD 2>/dev/null || " ipt_cmd " -A RRDIPT_FORWARD")
ipt_cmd="iptables -t mangle -j RETURN -d " arp_ip
system(ipt_cmd " -C RRDIPT_FORWARD 2>/dev/null || " ipt_cmd " -A RRDIPT_FORWARD")
}
function total(i){
return(bw[i "/in"] + bw[i "/out"])
}
function date( cmd, d){
cmd="date +%d-%m-%Y_%H:%M:%S"
cmd | getline d
close(cmd)
#!@todo could start a process with "while true; do date ...; done"
return(d)
}
BEGIN {
od=""
fid=1
debug=0
rrd=0
}
/^#/ { # get DB filename
FS=","
dbFile=FILENAME
next
}
# data from database; first file
FNR==NR { #!@todo this doesn't help if the DB file is empty.
if($2 == "NA")
#!@todo could get interface IP here
n=$1
else
n=$2
hosts[n] = "" # add this host/interface to hosts
mac[n] = $1
ip[n] = $2
inter[n] = $3
bw[n "/in"] = $4
bw[n "/out"] = $5
firstDate[n] = $7
lastDate[n] = $8
next
}
# not triggered on the first file
FNR==1 {
FS=" "
fid++ #!@todo use fid for all files; may be problematic for empty files
next
}
# arp: ip hw flags hw_addr mask device
fid==2 {
#!@todo regex match IPs and MACs for sanity
arp_ip = $1
arp_flags = $3
arp_mac = $4
arp_dev = $6
if(arp_flags != "0x0" && !(arp_ip in ip)){
if(debug)
print "new host:", arp_ip, arp_flags > "/dev/stderr"
hosts[arp_ip] = ""
mac[arp_ip] = arp_mac
ip[arp_ip] = arp_ip
inter[arp_ip] = arp_dev
bw[arp_ip "/in"] = bw[arp_ip "/out"] = 0
firstDate[arp_ip] = lastDate[arp_ip] = date()
}
next
}
#!@todo could use mangle chain totals or tailing "unnact" rules to
# account for data for new hosts from their first presence on the
# network to rule creation. The "unnact" rules would have to be
# maintained at the end of the list, and new rules would be inserted
# at the top.
# skip line
# read the chain name and deal with the data accordingly
fid==3 && $1 == "Chain"{
rrd=$2 ~ /RRDIPT_.*/
next
}
fid==3 && rrd && (NF < 9 || $1=="pkts"){ next }
fid==3 && rrd { # iptables input
if($6 != "*"){
m=$6
n=m "/out"
} else if($7 != "*"){
m=$7
n=m "/in"
} else if($8 != "0.0.0.0/0"){
m=$8
n=m "/out"
} else { # $9 != "0.0.0.0/0"
m=$9
n=m "/in"
}
# remove host from array; any hosts left in array at END get new
# iptables rules
#!@todo this deletes a host if any rule exists; if only one
# directional rule is removed, this will not remedy the situation
delete hosts[m]
if($2 > 0){ # counted some bytes
if(mode == "diff" || mode == "noUpdate")
print n, $2
if(mode!="noUpdate"){
if(inInterfaces(m)){ # if label is an interface
if(!(m in mac)){ # if label was not in db (also not in
# arp table, but interfaces won't be
# there anyway)
firstDate[m] = date()
mac[m] = inter[m] = m
ip[m] = "NA"
bw[m "/in"]=bw[m "/out"]= 0
}
}
bw[n]+=$2
lastDate[m] = date()
}
}
}
END {
if(mode=="noUpdate") exit
close(dbFile)
system("rm -f " dbFile)
print "#mac,ip,iface,in,out,total,first_date,last_date" > dbFile
OFS=","
for(i in mac)
print mac[i], ip[i], inter[i], bw[i "/in"], bw[i "/out"], total(i), firstDate[i], lastDate[i] > dbFile
close(dbFile)
# for hosts without rules
for(host in hosts) if(!inInterfaces(host)) newRule(host)
}

View File

@ -1,301 +0,0 @@
#!/bin/sh
#
# wrtbwmon: traffic logging tool for routers
#
# Peter Bailey (peter.eldridge.bailey+wrtbwmon AT gmail.com)
#
# Based on work by:
# Emmanuel Brucy (e.brucy AT qut.edu.au)
# Fredrik Erlandsson (erlis AT linux.nu)
# twist - http://wiki.openwrt.org/RrdTrafficWatch
trap "rm -f /tmp/*_$$.tmp; kill $$" INT
binDir=/usr/sbin
dataDir=/usr/share/wrtbwmon
lockDir=/tmp/wrtbwmon.lock
pidFile=$lockDir/pid
networkFuncs=/lib/functions/network.sh
uci=`which uci 2>/dev/null`
nslookup=`which nslookup 2>/dev/null`
nvram=`which nvram 2>/dev/null`
chains='INPUT OUTPUT FORWARD'
DEBUG=
interfaces='eth0 tun0' # in addition to detected WAN
DB=$2
mode=
# DNS server for reverse lookups provided in "DNS".
# don't perform reverse DNS lookups by default
DO_RDNS=${DNS-}
header="#mac,ip,iface,in,out,total,first_date,last_date"
createDbIfMissing()
{
[ ! -f "$DB" ] && echo $header > "$DB"
}
checkDbArg()
{
[ -z "$DB" ] && echo "ERROR: Missing argument 2 (database file)" && exit 1
}
checkDB()
{
[ ! -f "$DB" ] && echo "ERROR: $DB does not exist" && exit 1
[ ! -w "$DB" ] && echo "ERROR: $DB is not writable" && exit 1
}
checkWAN()
{
[ -z "$wan" ] && echo "Warning: failed to detect WAN interface."
}
lookup()
{
MAC=$1
IP=$2
userDB=$3
for USERSFILE in $userDB /tmp/dhcp.leases /tmp/dnsmasq.conf /etc/dnsmasq.conf /etc/hosts; do
[ -e "$USERSFILE" ] || continue
case $USERSFILE in
/tmp/dhcp.leases )
USER=$(grep -i "$MAC" $USERSFILE | cut -f4 -s -d' ')
;;
/etc/hosts )
USER=$(grep "^$IP " $USERSFILE | cut -f2 -s -d' ')
;;
* )
USER=$(grep -i "$MAC" "$USERSFILE" | cut -f2 -s -d,)
;;
esac
[ "$USER" = "*" ] && USER=
[ -n "$USER" ] && break
done
if [ -n "$DO_RDNS" -a -z "$USER" -a "$IP" != "NA" -a -n "$nslookup" ]; then
USER=`$nslookup $IP $DNS | awk '!/server can/{if($4){print $4; exit}}' | sed -re 's/[.]$//'`
fi
[ -z "$USER" ] && USER=${MAC}
echo $USER
}
detectIF()
{
if [ -f "$networkFuncs" ]; then
IF=`. $networkFuncs; network_get_device netdev $1; echo $netdev`
[ -n "$IF" ] && echo $IF && return
fi
if [ -n "$uci" -a -x "$uci" ]; then
IF=`$uci get network.${1}.ifname 2>/dev/null`
[ $? -eq 0 -a -n "$IF" ] && echo $IF && return
fi
if [ -n "$nvram" -a -x "$nvram" ]; then
IF=`$nvram get ${1}_ifname 2>/dev/null`
[ $? -eq 0 -a -n "$IF" ] && echo $IF && return
fi
}
detectLAN()
{
[ -e /sys/class/net/br-lan ] && echo br-lan && return
lan=$(detectIF lan)
[ -n "$lan" ] && echo $lan && return
}
detectWAN()
{
[ -n "$WAN_IF" ] && echo $WAN_IF && return
wan=$(detectIF wan)
[ -n "$wan" ] && echo $wan && return
wan=$(ip route show 2>/dev/null | grep default | sed -re '/^default/ s/default.*dev +([^ ]+).*/\1/')
[ -n "$wan" ] && echo $wan && return
[ -f "$networkFuncs" ] && wan=$(. $networkFuncs; network_find_wan wan; echo $wan)
[ -n "$wan" ] && echo $wan && return
}
lock()
{
attempts=0
while [ $attempts -lt 10 ]; do
mkdir $lockDir 2>/dev/null && break
attempts=$((attempts+1))
pid=`cat $pidFile 2>/dev/null`
if [ -n "$pid" ]; then
if [ -d "/proc/$pid" ]; then
[ -n "$DEBUG" ] && echo "WARNING: Lockfile detected but process $(cat $pidFile) does not exist !"
rm -rf $lockDir
else
sleep 1
fi
fi
done
mkdir $lockDir 2>/dev/null
echo $$ > $pidFile
[ -n "$DEBUG" ] && echo $$ "got lock after $attempts attempts"
trap '' INT
}
unlock()
{
rm -rf $lockDir
[ -n "$DEBUG" ] && echo $$ "released lock"
trap "rm -f /tmp/*_$$.tmp; kill $$" INT
}
# chain
newChain()
{
chain=$1
# Create the RRDIPT_$chain chain (it doesn't matter if it already exists).
iptables -t mangle -N RRDIPT_$chain 2> /dev/null
# Add the RRDIPT_$chain CHAIN to the $chain chain if not present
iptables -t mangle -C $chain -j RRDIPT_$chain 2>/dev/null
if [ $? -ne 0 ]; then
[ -n "$DEBUG" ] && echo "DEBUG: iptables chain misplaced, recreating it..."
iptables -t mangle -I $chain -j RRDIPT_$chain
fi
}
# chain tun
newRuleIF()
{
chain=$1
IF=$2
#!@todo test
if [ "$chain" = "OUTPUT" ]; then
cmd="iptables -t mangle -o $IF -j RETURN"
eval $cmd " -C RRDIPT_$chain 2>/dev/null" || eval $cmd " -A RRDIPT_$chain"
elif [ "$chain" = "INPUT" ]; then
cmd="iptables -t mangle -i $IF -j RETURN"
eval $cmd " -C RRDIPT_$chain 2>/dev/null" || eval $cmd " -A RRDIPT_$chain"
fi
}
update()
{
#!@todo could let readDB.awk handle this; that would place header
#!info in fewer places
createDbIfMissing
checkDB
checkWAN
> /tmp/iptables_$$.tmp
lock
# only zero our own chains
for chain in $chains; do
iptables -nvxL RRDIPT_$chain -t mangle -Z >> /tmp/iptables_$$.tmp
done
# the iptables and readDB commands have to be separate. Otherwise,
# they will fight over iptables locks
awk -v mode="$mode" -v interfaces=\""$interfaces"\" -f $binDir/readDB.awk \
$DB \
/proc/net/arp \
/tmp/iptables_$$.tmp
unlock
}
############################################################
case $1 in
"dump" )
checkDbArg
lock
tr ',' '\t' < "$DB"
unlock
;;
"update" )
checkDbArg
wan=$(detectWAN)
interfaces="$interfaces $wan"
update
rm -f /tmp/*_$$.tmp
exit
;;
"publish" )
checkDbArg
[ -z "$3" ] && echo "ERROR: Missing argument 3 (output html file)" && exit 1
# sort DB
lock
# busybox sort truncates numbers to 32 bits
grep -v '^#' $DB | awk -F, '{OFS=","; a=sprintf("%f",$4/1e6); $4=""; print a,$0}' | tr -s ',' | sort -rn | awk -F, '{OFS=",";$1=sprintf("%f",$1*1e6);print}' > /tmp/sorted_$$.tmp
# create HTML page
rm -f $3.tmp
cp $dataDir/usage.htm1 $3.tmp
#!@todo fix publishing
while IFS=, read PEAKUSAGE_IN MAC IP IFACE PEAKUSAGE_OUT TOTAL FIRSTSEEN LASTSEEN
do
echo "
new Array(\"$(lookup $MAC $IP $4)\",\"$MAC\",\"$IP\",
$PEAKUSAGE_IN,$PEAKUSAGE_OUT,$TOTAL,\"$FIRSTSEEN\",\"$LASTSEEN\")," >> $3.tmp
done < /tmp/sorted_$$.tmp
echo "0);" >> $3.tmp
sed "s/(date)/`date`/" < $dataDir/usage.htm2 >> $3.tmp
mv $3.tmp $3
unlock
#Free some memory
rm -f /tmp/*_$$.tmp
;;
"setup" )
checkDbArg
[ -w "$DB" ] && echo "Warning: using existing $DB"
createDbIfMissing
for chain in $chains; do
newChain $chain
done
#lan=$(detectLAN)
wan=$(detectWAN)
checkWAN
interfaces="$interfaces $wan"
# track local data
for chain in INPUT OUTPUT; do
for interface in $interfaces; do
[ -n "$interface" ] && [ -e "/sys/class/net/$interface" ] && newRuleIF $chain $interface
done
done
# this will add rules for hosts in arp table
update
rm -f /tmp/*_$$.tmp
;;
"remove" )
iptables-save | grep -v RRDIPT | iptables-restore
rm -rf "$lockDir"
;;
*)
echo \
"Usage: $0 {setup|update|publish|remove} [options...]
Options:
$0 setup database_file
$0 update database_file
$0 publish database_file path_of_html_report [user_file]
Examples:
$0 setup /tmp/usage.db
$0 update /tmp/usage.db
$0 publish /tmp/usage.db /www/user/usage.htm /jffs/users.txt
$0 remove
Note: [user_file] is an optional file to match users with MAC addresses.
Its format is \"00:MA:CA:DD:RE:SS,username\", with one entry per line."
;;
esac

View File

@ -1,23 +0,0 @@
<html><head><title>Traffic</title>
<script type="text/javascript">
function getSize(size) {
var prefix=new Array("","k","M","G","T","P","E","Z"); var base=1000;
var pos=0;
while (size>base) {
size/=base; pos++;
}
if (pos > 2) precision=1000; else precision = 1;
return (Math.round(size*precision)/precision)+' '+prefix[pos];}
</script></head>
<body><h1>Total Usage:</h1>
<table border="1">
<tr bgcolor=silver>
<th>User</th>
<th>Download</th>
<th>Upload</th>
<th>Total</th>
<th>First seen</th>
<th>Last seen</th>
</tr>
<script type="text/javascript">
var values = new Array(

View File

@ -1,14 +0,0 @@
var totalIn = 0;
var totalOut = 0;
for (i=0; i < values.length-1; i++) {
totalIn += values[i][3];
totalOut += values[i][4];
document.write("<tr><td><div title=\"" + values[i][1] + " (" + values[i][2] + ")" + "\">" + values[i][0] + "</div></td>");
for (j=3; j < 6; j++)
document.write("<td>" + getSize(values[i][j]) + "</td>");
document.write("<td>" + values[i][6] + "</td><td>" + values[i][7] + "</td></tr>");
}
document.write("<tr><td>TOTAL</td><td>" + getSize(totalIn) + "</td><td>" + getSize(totalOut) + "</td><td>" + getSize(totalIn + totalOut) + "</td><td></td><td></td></tr>");
</script></table>
<br /><small>This page was generated on (date)</small>
</body></html>

View File

@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=ncurses
PKG_VERSION:=6.2
PKG_RELEASE:=2
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=@GNU/$(PKG_NAME)
@ -40,7 +40,7 @@ define Package/libncurses
URL:=http://www.gnu.org/software/ncurses/
PROVIDES:=libncursesw
DEPENDS:= +terminfo
ABI_VERSION:=$(PKG_ABI_VERSION)
ABI_VERSION:=6
endef
define Package/libncurses-dev

View File

@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=readline
PKG_VERSION:=8.1
PKG_RELEASE:=2
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=@GNU/readline
@ -33,7 +33,7 @@ define Package/libreadline
TITLE:=Command lines edition library
DEPENDS:=+libncursesw
URL:=http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html
ABI_VERSION:=$(PKG_ABI_VERSION)
ABI_VERSION:=8
endef
define Package/libreadline/description

View File

@ -198,6 +198,7 @@ o:depends("up_china_dns", "xray_doh")
o = s:taboption("DNS", ListValue, "dns_mode", translate("Filter Mode"))
o.rmempty = false
o:reset_values()
o:value("fake_ip", translatef("Fake IP"))
if api.is_finded("pdnsd") then
o:value("pdnsd", "pdnsd " .. translatef("Requery DNS By %s", translate("TCP Node")))
end

View File

@ -211,6 +211,26 @@ gen_dnsmasq_items() {
'
}
gen_dnsmasq_fake_items() {
local fwd_dns="1.2.3.4"
local outf=${1}; shift 1
awk -v fwd_dns="${fwd_dns}" -v outf="${outf}" '
BEGIN {
if(outf == "") outf="/dev/stdout";
split(fwd_dns, dns, ","); setdns=length(dns)>0;
if(setdns) for(i in dns) if(length(dns[i])==0) delete dns[i];
fail=1;
}
! /^$/&&!/^#/ {
fail=0
if(! setdns) {printf("address=%s\n", $0) >>outf; next;}
if(setdns) for(i in dns) printf("address=/.%s/%s\n", $0, dns[i]) >>outf;
}
END {fflush(outf); close(outf); exit(fail);}
'
}
check_port_exists() {
port=$1
protocol=$2
@ -841,14 +861,18 @@ start_dns() {
TUN_DNS=${DNS_FORWARD}
echolog " - 域名解析直接使用UDP节点请求DNS$TUN_DNS"
;;
fake_ip)
TUN_DNS="1.2.3.4"
echolog " - 域名解析使用FakeIP方案..."
;;
custom)
custom_dns=$(config_t_get global custom_dns)
TUN_DNS="$(echo ${custom_dns} | sed 's/:/#/g')"
echolog " - 域名解析直接使用UDP协议自定义DNS$TUN_DNS)解析..."
echolog " - 域名解析:使用UDP协议自定义DNS$TUN_DNS)解析..."
;;
esac
[ -n "$chnlist" ] && [ "$DNS_MODE" != "custom" ] && {
[ -n "$chnlist" ] && [ "$DNS_MODE" != "custom" ] && [ "$DNS_MODE" != "fake_ip" ] && {
[ -f "${RULES_PATH}/chnlist" ] && cp -a "${RULES_PATH}/chnlist" "${TMP_PATH}/chnlist"
[ -n "$(first_type chinadns-ng)" ] && {
echolog "发现ChinaDNS-NG将启动。"
@ -909,31 +933,38 @@ add_dnsmasq() {
#始终用国内DNS解析节点域名
fwd_dns="${LOCAL_DNS}"
servers=$(uci show "${CONFIG}" | grep ".address=" | cut -d "'" -f 2)
hosts_foreach "servers" host_from_url | grep -v "google.c" | grep '[a-zA-Z]$' | sort -u | gen_dnsmasq_items "vpsiplist" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/00-vpsiplist_host.conf"
hosts_foreach "servers" host_from_url | grep -v "google.c" | grep '[a-zA-Z]$' | sort -u | gen_dnsmasq_items "vpsiplist,vpsiplist6" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/00-vpsiplist_host.conf"
echolog " - [$?]节点列表中的域名(vpsiplist)${fwd_dns:-默认}"
#始终用国内DNS解析直连白名单列表
fwd_dns="${LOCAL_DNS}"
[ -n "$CHINADNS_NG" ] && unset fwd_dns
sort -u "${RULES_PATH}/direct_host" | gen_dnsmasq_items "whitelist" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/01-direct_host.conf"
sort -u "${RULES_PATH}/direct_host" | gen_dnsmasq_items "whitelist,whitelist_6" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/01-direct_host.conf"
echolog " - [$?]域名白名单(whitelist)${fwd_dns:-默认}"
#始终使用远程DNS解析代理黑名单列表
fwd_dns="${TUN_DNS}"
[ -n "$CHINADNS_NG" ] && fwd_dns="${china_ng_gfw}"
[ -n "$CHINADNS_NG" ] && unset fwd_dns
sort -u "${RULES_PATH}/proxy_host" | gen_dnsmasq_items "blacklist" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/99-proxy_host.conf"
echolog " - [$?]代理域名表(blacklist)${fwd_dns:-默认}"
if [ "${DNS_MODE}" = "fake_ip" ]; then
sort -u "${RULES_PATH}/proxy_host" | gen_dnsmasq_fake_items "${TMP_DNSMASQ_PATH}/99-proxy_host.conf"
else
fwd_dns="${TUN_DNS}"
[ -n "$CHINADNS_NG" ] && fwd_dns="${china_ng_gfw}"
[ -n "$CHINADNS_NG" ] && unset fwd_dns
sort -u "${RULES_PATH}/proxy_host" | gen_dnsmasq_items "blacklist,blacklist_6" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/99-proxy_host.conf"
echolog " - [$?]代理域名表(blacklist)${fwd_dns:-默认}"
fi
#如果开启了通过代理订阅
[ "$(config_t_get global_subscribe subscribe_proxy 0)" = "1" ] && {
fwd_dns="${TUN_DNS}"
[ -n "$CHINADNS_NG" ] && fwd_dns="${china_ng_gfw}"
items=$(get_enabled_anonymous_secs "@subscribe_list")
for item in ${items}; do
host_from_url "$(config_n_get ${item} url)" | gen_dnsmasq_items "blacklist" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/99-subscribe.conf"
for item in $(get_enabled_anonymous_secs "@subscribe_list"); do
if [ "${DNS_MODE}" = "fake_ip" ]; then
host_from_url "$(config_n_get ${item} url)" | gen_dnsmasq_fake_items "${TMP_DNSMASQ_PATH}/99-subscribe.conf"
else
host_from_url "$(config_n_get ${item} url)" | gen_dnsmasq_items "blacklist,blacklist_6" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/99-subscribe.conf"
fi
done
echolog " - [$?]节点订阅域名(blacklist)${fwd_dns:-默认}"
[ "${DNS_MODE}" != "fake_ip" ] && echolog " - [$?]节点订阅域名(blacklist)${fwd_dns:-默认}"
}
#分流规则
@ -948,25 +979,36 @@ add_dnsmasq() {
fi
local shunt_node=$(config_n_get $shunt_node_id address nil)
[ "$shunt_node" = "nil" ] && continue
config_n_get $shunt_id domain_list | grep -v 'regexp:\|geosite:\|ext:' | sed 's/domain:\|full:\|//g' | tr -s "\r\n" "\n" | sort -u | gen_dnsmasq_items "shuntlist" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/998-shunt_host.conf"
if [ "${DNS_MODE}" = "fake_ip" ]; then
config_n_get $shunt_id domain_list | grep -v 'regexp:\|geosite:\|ext:' | sed 's/domain:\|full:\|//g' | tr -s "\r\n" "\n" | sort -u | gen_dnsmasq_fake_items "${TMP_DNSMASQ_PATH}/998-shunt_host.conf"
else
config_n_get $shunt_id domain_list | grep -v 'regexp:\|geosite:\|ext:' | sed 's/domain:\|full:\|//g' | tr -s "\r\n" "\n" | sort -u | gen_dnsmasq_items "shuntlist,shuntlist6" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/998-shunt_host.conf"
fi
done
echolog " - [$?]Xray分流规则(shuntlist)${fwd_dns:-默认}"
[ "${DNS_MODE}" != "fake_ip" ] && echolog " - [$?]Xray分流规则(shuntlist)${fwd_dns:-默认}"
}
#如果没有使用回国模式
if [ -z "${returnhome}" ]; then
fwd_dns="${TUN_DNS}"
[ -n "$CHINADNS_NG" ] && fwd_dns="${china_ng_gfw}"
[ -n "$CHINADNS_NG" ] && unset fwd_dns
[ ! -f "${TMP_PATH}/gfwlist.txt" ] && sed -n 's/^ipset=\/\.\?\([^/]*\).*$/\1/p' "${RULES_PATH}/gfwlist.conf" | sort -u > "${TMP_PATH}/gfwlist.txt"
#sort -u "${TMP_PATH}/gfwlist.txt" | gen_dnsmasq_items "gfwlist" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/999-gfwlist.conf"
sort -u "${TMP_PATH}/gfwlist.txt" | gen_dnsmasq_items "gfwlist,gfwlist6" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/999-gfwlist.conf"
echolog " - [$?]防火墙域名表(gfwlist)${fwd_dns:-默认}"
if [ "${DNS_MODE}" = "fake_ip" ]; then
sort -u "${TMP_PATH}/gfwlist.txt" | gen_dnsmasq_fake_items "${TMP_DNSMASQ_PATH}/999-gfwlist.conf"
else
fwd_dns="${TUN_DNS}"
[ -n "$CHINADNS_NG" ] && fwd_dns="${china_ng_gfw}"
[ -n "$CHINADNS_NG" ] && unset fwd_dns
sort -u "${TMP_PATH}/gfwlist.txt" | gen_dnsmasq_items "gfwlist,gfwlist6" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/999-gfwlist.conf"
echolog " - [$?]防火墙域名表(gfwlist)${fwd_dns:-默认}"
fi
else
#回国模式
fwd_dns="${TUN_DNS}"
sort -u "${RULES_PATH}/chnlist" | gen_dnsmasq_items "chnroute" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/02-chinalist_host.conf"
echolog " - [$?]中国域名表(chnroute)${fwd_dns:-默认}"
if [ "${DNS_MODE}" = "fake_ip" ]; then
sort -u "${RULES_PATH}/chnlist" | gen_dnsmasq_fake_items "${TMP_DNSMASQ_PATH}/02-chinalist_host.conf"
else
fwd_dns="${TUN_DNS}"
sort -u "${RULES_PATH}/chnlist" | gen_dnsmasq_items "chnroute,chnroute6" "${fwd_dns}" "${TMP_DNSMASQ_PATH}/02-chinalist_host.conf"
echolog " - [$?]中国域名表(chnroute)${fwd_dns:-默认}"
fi
fi
fi

View File

@ -476,7 +476,7 @@ add_firewall_rule() {
done
for shunt_id in $shunt_ids; do
config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "([A-Fa-f0-9]{0,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $IPSET_SHUNTLIST_6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $IPSET_SHUNTLIST_6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
done
cat $RULES_PATH/chnroute | sed -e "/^$/d" | sed -e "s/^/add $IPSET_CHN &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
@ -486,10 +486,10 @@ add_firewall_rule() {
cat $RULES_PATH/direct_ip | sed -e "/^$/d" | grep -E "(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}" | sed -e "s/^/add $IPSET_WHITELIST &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
cat $RULES_PATH/chnroute6 | sed -e "/^$/d" | sed -e "s/^/add $IPSET_CHN6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
cat $RULES_PATH/proxy_ip | grep -E "([A-Fa-f0-9]{0,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "/^$/d" | sed -e "s/^/add $IPSET_BLACKLIST_6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
[ -f "$RULES_PATH/proxy_ip2" ] && cat $RULES_PATH/proxy_ip2 | grep -E "([A-Fa-f0-9]{0,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "/^$/d" | sed -e "s/^/add $IPSET_BLACKLIST2_6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
[ -f "$RULES_PATH/proxy_ip3" ] && cat $RULES_PATH/proxy_ip3 | grep -E "([A-Fa-f0-9]{0,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "/^$/d" | sed -e "s/^/add $IPSET_BLACKLIST3_6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
cat $RULES_PATH/direct_ip | grep -E "([A-Fa-f0-9]{0,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "/^$/d" | sed -e "s/^/add $IPSET_WHITELIST_6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
cat $RULES_PATH/proxy_ip | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "/^$/d" | sed -e "s/^/add $IPSET_BLACKLIST_6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
[ -f "$RULES_PATH/proxy_ip2" ] && cat $RULES_PATH/proxy_ip2 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "/^$/d" | sed -e "s/^/add $IPSET_BLACKLIST2_6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
[ -f "$RULES_PATH/proxy_ip3" ] && cat $RULES_PATH/proxy_ip3 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "/^$/d" | sed -e "s/^/add $IPSET_BLACKLIST3_6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
cat $RULES_PATH/direct_ip | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "/^$/d" | sed -e "s/^/add $IPSET_WHITELIST_6 &/g" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
ipset -! -R <<-EOF
$(gen_laniplist | sed -e "s/^/add $IPSET_LANIPLIST /")
@ -534,7 +534,7 @@ add_firewall_rule() {
done
}
local ISP_DNS6=$(cat $RESOLVFILE 2>/dev/null | grep -E "([A-Fa-f0-9]{0,4}::?){1,7}[A-Fa-f0-9]{1,4}" | awk -F % '{print $1}' | awk -F " " '{print $2}'| sort -u )
local ISP_DNS6=$(cat $RESOLVFILE 2>/dev/null | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | awk -F % '{print $1}' | awk -F " " '{print $2}'| sort -u )
[ -n "$ISP_DNS" ] && {
#echolog "处理 ISP IPv6 DNS 例外..."
for ispip6 in $ISP_DNS; do
@ -638,6 +638,7 @@ add_firewall_rule() {
}
[ "$use_tcp_node_resolve_dns" == 1 ] && hosts_foreach DNS_FORWARD _proxy_tcp_access 53
$ipt_tmp -A OUTPUT -p tcp -j PSW_OUTPUT
$ipt_tmp -I PSW_OUTPUT -p tcp -d 1.2.3.4 $blist_r
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && {
$ipt_tmp -A PSW_OUTPUT -p tcp -m multiport --dport $TCP_NO_REDIR_PORTS -j RETURN
$ip6t_m -A PSW_OUTPUT -p tcp -m multiport --dport $TCP_NO_REDIR_PORTS -j RETURN
@ -730,6 +731,7 @@ add_firewall_rule() {
}
[ "$use_udp_node_resolve_dns" == 1 ] && hosts_foreach DNS_FORWARD _proxy_udp_access 53
$ipt_m -A OUTPUT -p udp -j PSW_OUTPUT
$ipt_m -I PSW_OUTPUT -p udp -d 1.2.3.4 $(REDIRECT 1 MARK)
[ "$UDP_NO_REDIR_PORTS" != "disable" ] && {
$ipt_m -A PSW_OUTPUT -p udp -m multiport --dport $UDP_NO_REDIR_PORTS -j RETURN
$ip6t_m -A PSW_OUTPUT -p udp -m multiport --dport $UDP_NO_REDIR_PORTS -j RETURN

View File

@ -65,7 +65,7 @@ do
#dns
dns_mode=$(config_t_get global dns_mode)
if [ "$dns_mode" != "nonuse" ] && [ "$dns_mode" != "custom" ]; then
if [ "$dns_mode" != "nonuse" ] && [ "$dns_mode" != "custom" ] && [ "$dns_mode" != "fake_ip" ]; then
icount=$(netstat -apn | grep 7913 | wc -l)
if [ $icount = 0 ]; then
/etc/init.d/$CONFIG restart

View File

@ -195,6 +195,7 @@ local function fetch_chnlist()
end
else
sret = 0
log("chnlist 第"..k.."条规则:"..v.."下载失败!")
end
os.remove("/tmp/chnlist_dl"..k)
end