Merge Mainline
Signed-off-by: CN_SZTL <cnsztl@project-openwrt.eu.org>
This commit is contained in:
commit
dfd0342804
@ -61,9 +61,7 @@ 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).<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).
|
||||
luci-proto-minieap source: [ysc3839/luci-proto-minieap](https://github.com/ysc3839/luci-proto-minieap).
|
||||
|
||||
## License
|
||||
### Depend on their own License.
|
||||
|
||||
@ -258,7 +258,7 @@ help:
|
||||
cat README.md
|
||||
|
||||
distclean:
|
||||
rm -rf bin build_dir .ccache .config* dl feeds key-build* logs package/feeds package/openwrt-packages staging_dir tmp
|
||||
rm -rf bin build_dir .ccache .config* dl feeds key-build* logs package/feeds staging_dir tmp
|
||||
@$(_SINGLE)$(SUBMAKE) -C scripts/config clean
|
||||
|
||||
ifeq ($(findstring v,$(DEBUG)),)
|
||||
|
||||
@ -23,7 +23,7 @@ PKG_CONFIG_DEPENDS += \
|
||||
sanitize = $(call tolower,$(subst _,-,$(subst $(space),-,$(1))))
|
||||
|
||||
VERSION_NUMBER:=$(call qstrip,$(CONFIG_VERSION_NUMBER))
|
||||
VERSION_NUMBER:=$(if $(VERSION_NUMBER),$(VERSION_NUMBER),SNAPSHOT)
|
||||
VERSION_NUMBER:=$(if $(VERSION_NUMBER),$(VERSION_NUMBER),18.06-SNAPSHOT)
|
||||
|
||||
VERSION_CODE:=$(call qstrip,$(CONFIG_VERSION_CODE))
|
||||
VERSION_CODE:=$(if $(VERSION_CODE),$(VERSION_CODE),$(REVISION))
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@ -1,5 +0,0 @@
|
||||
|
||||
config wrtbwmon 'general'
|
||||
option enabled '1'
|
||||
option path '/tmp/usage.db'
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
#!/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)"
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
#!/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)
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
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
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
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
|
||||
@ -1,579 +0,0 @@
|
||||
<%#
|
||||
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%>
|
||||
@ -1,152 +0,0 @@
|
||||
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 "。"
|
||||
@ -1,11 +0,0 @@
|
||||
#!/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
|
||||
@ -1,213 +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=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)
|
||||
}
|
||||
@ -1,460 +0,0 @@
|
||||
#!/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
|
||||
@ -1,34 +0,0 @@
|
||||
<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(
|
||||
@ -1,18 +0,0 @@
|
||||
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>
|
||||
84
package/ctcgfw/xray-plugin/Makefile
Normal file
84
package/ctcgfw/xray-plugin/Makefile
Normal file
@ -0,0 +1,84 @@
|
||||
#
|
||||
# Copyright (C) 2021 ImmortalWrt
|
||||
# <https://project-openwrt.eu.org>
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v3.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=xray-plugin
|
||||
PKG_VERSION:=1.3.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/teddysun/xray-plugin/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=a74adb1ea36819b634fb2b09fc2ac14ec2c57488285c82e6e26f1347de1d6324
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
PKG_MAINTAINER:=Tianling Shen <cnsztl@project-openwrt.eu.org>
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
CONFIG_XRAY_PLUGIN_PROVIDE_V2RAY_PLUGIN \
|
||||
CONFIG_XRAY_PLUGIN_COMPRESS_GOPROXY \
|
||||
CONFIG_XRAY_PLUGIN_COMPRESS_UPX
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
|
||||
GO_PKG:=github.com/teddysun/xray-plugin
|
||||
GO_PKG_LDFLAGS:=-s -w
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
|
||||
|
||||
define Package/xray-plugin/config
|
||||
config XRAY_PLUGIN_PROVIDE_V2RAY_PLUGIN
|
||||
bool "Provide v2ray-plugin binary using xray-plugin"
|
||||
default n
|
||||
|
||||
config XRAY_PLUGIN_COMPRESS_GOPROXY
|
||||
bool "Compiling with GOPROXY proxy"
|
||||
default n
|
||||
|
||||
config XRAY_PLUGIN_COMPRESS_UPX
|
||||
bool "Compress executable files with UPX"
|
||||
default y
|
||||
endef
|
||||
|
||||
ifneq ($(CONFIG_XRAY_PLUGIN_COMPRESS_GOPROXY),)
|
||||
export GO111MODULE=on
|
||||
export GOPROXY=https://goproxy.io
|
||||
endif
|
||||
|
||||
define Package/xray-plugin
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=SIP003 plugin for Shadowsocks, based on Xray
|
||||
URL:=https://github.com/teddysun/xray-plugin
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
|
||||
endef
|
||||
|
||||
define Package/xray-plugin/description
|
||||
Yet another SIP003 plugin for Shadowsocks, based on Xray.
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(call GoPackage/Build/Compile)
|
||||
ifneq ($(CONFIG_XRAY_PLUGIN_COMPRESS_UPX),)
|
||||
$(STAGING_DIR_HOST)/bin/upx --lzma --best $(GO_PKG_BUILD_BIN_DIR)/xray-plugin
|
||||
endif
|
||||
endef
|
||||
|
||||
define Package/xray-plugin/install
|
||||
$(call GoPackage/Package/Install/Bin,$(1))
|
||||
ifneq ($(CONFIG_XRAY_PLUGIN_PROVIDE_V2RAY_PLUGIN),)
|
||||
$(LN) xray-plugin $(1)/usr/bin/v2ray-plugin
|
||||
endif
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,xray-plugin))
|
||||
$(eval $(call BuildPackage,xray-plugin))
|
||||
@ -49,7 +49,7 @@
|
||||
|
||||
local user_info = luci.sys.exec("cat /proc/net/arp | grep 'br-lan' | grep '0x2' | wc -l")
|
||||
|
||||
local cpu_usage = (luci.sys.exec("expr 100 - $(top -n 1 | grep 'CPU:' | awk -F '%' '{print$4}' | awk -F ' ' '{print$2}')") or "6") .. "%"
|
||||
local cpu_usage = luci.sys.exec("top -n1 | awk '/^CPU/ {printf(\"%d%%\", 100 - $8)}'") or "6%"
|
||||
local cpu_info = luci.sys.exec("/sbin/cpuinfo") or "ARM Processor"
|
||||
|
||||
local rv = {
|
||||
|
||||
@ -21,7 +21,7 @@ if grep -q "bcm27xx" "/etc/openwrt_release"; then
|
||||
cpu_temp="$(vcgencmd measure_temp | awk -F '=' '{print $2}' | awk -F "'" '{print $1}')°C"
|
||||
else
|
||||
[ -e "/sys/class/thermal/thermal_zone0/temp" ] && \
|
||||
cpu_temp="$(awk "BEGIN{printf (\"%.1f\n\",$(cat "/sys/class/thermal/thermal_zone0/temp")/1000)}")°C"
|
||||
cpu_temp="$(awk '{printf("%.1f°C", $0 / 1000)}' /sys/class/thermal/thermal_zone0/temp)"
|
||||
fi
|
||||
|
||||
if [ -z "${cpu_freq}" ] && [ -z "${cpu_temp}" ]; then
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
|
||||
local user_info = luci.sys.exec("cat /proc/net/arp | grep '0x2' | wc -l") or 0
|
||||
|
||||
local cpu_usage = (luci.sys.exec("expr 100 - $(top -n 1 | grep 'CPU:' | awk -F '%' '{print$4}' | awk -F ' ' '{print$2}')") or "6") .. "%"
|
||||
local cpu_usage = luci.sys.exec("top -n1 | awk '/^CPU/ {printf(\"%d%%\", 100 - $8)}'") or "6%"
|
||||
|
||||
local rv = {
|
||||
cpuusage = cpu_usage,
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
[ -z "$password" ] && write_log 14 "Configuration error! [Password] cannot be empty"
|
||||
|
||||
#检查外部调用工具
|
||||
[ -n "$WGET_SSL" ] || write_log 13 "GNU Wget support is required to use Alibaba Cloud API. Please install first"
|
||||
[ -n "$WGET_SSL" ] || write_log 13 "GNU Wget support is required to use dnspod API. Please install first"
|
||||
|
||||
# 变量声明
|
||||
local __URLBASE __HOST __DOMAIN __TYPE __CMDBASE __POST __POST1 __RECIP __RECID __value __TTL
|
||||
@ -114,7 +114,7 @@ update_domain() {
|
||||
#获取域名解析记录
|
||||
describe_domain() {
|
||||
ret=0
|
||||
__POST="login_token=$username,$password&format=json&domain=$__DOMAIN&sub_domain=$__HOST"
|
||||
__POST="login_token=$username,$password&format=json&domain=$__DOMAIN&sub_domain=$__HOST&record_type=$__TYPE"
|
||||
__POST1="$__POST&value=$__IP&record_type=$__TYPE&record_line_id=0"
|
||||
dnspod_transfer 0
|
||||
__TMP=`jsonfilter -i $DATFILE -e "@.records[@.type!='NS']"`
|
||||
|
||||
17
package/lean/luci-app-wrtbwmon/Makefile
Normal file
17
package/lean/luci-app-wrtbwmon/Makefile
Normal file
@ -0,0 +1,17 @@
|
||||
# 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
|
||||
|
||||
562
package/lean/luci-app-wrtbwmon/htdocs/luci-static/wrtbwmon.js
Normal file
562
package/lean/luci-app-wrtbwmon/htdocs/luci-static/wrtbwmon.js
Normal file
@ -0,0 +1,562 @@
|
||||
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' ? '▼' : '▲');
|
||||
}
|
||||
|
||||
// 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();
|
||||
});
|
||||
|
||||
})();
|
||||
@ -0,0 +1,43 @@
|
||||
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
|
||||
@ -0,0 +1,18 @@
|
||||
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
|
||||
@ -1,11 +1,10 @@
|
||||
local USER_FILE_PATH = "/etc/wrtbwmon.user"
|
||||
local USER_FILE_PATH = "/etc/config/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. "
|
||||
.. "Each line must have the following format: <b><font color=\"red\">\"00:aa:bb:cc:ee:ff,username\"</font></b>."))
|
||||
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"))
|
||||
|
||||
local o = f:field(Value, "_custom")
|
||||
|
||||
@ -21,6 +20,4 @@ function o.write(self, section, value)
|
||||
fs.writefile(USER_FILE_PATH, value)
|
||||
end
|
||||
|
||||
f.submit = translate("Submit")
|
||||
|
||||
return f
|
||||
46
package/lean/luci-app-wrtbwmon/luasrc/view/wrtbwmon.htm
Normal file
46
package/lean/luci-app-wrtbwmon/luasrc/view/wrtbwmon.htm
Normal file
@ -0,0 +1,46 @@
|
||||
<%+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%>
|
||||
56
package/lean/luci-app-wrtbwmon/po/zh-cn/wrtbwmon.po
Normal file
56
package/lean/luci-app-wrtbwmon/po/zh-cn/wrtbwmon.po
Normal file
@ -0,0 +1,56 @@
|
||||
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 "加载中..."
|
||||
4
package/lean/luci-app-wrtbwmon/root/etc/config/wrtbwmon
Normal file
4
package/lean/luci-app-wrtbwmon/root/etc/config/wrtbwmon
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
config wrtbwmon 'general'
|
||||
option persist '0'
|
||||
|
||||
22
package/lean/luci-app-wrtbwmon/root/etc/init.d/wrtbwmon
Executable file
22
package/lean/luci-app-wrtbwmon/root/etc/init.d/wrtbwmon
Executable file
@ -0,0 +1,22 @@
|
||||
#!/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
|
||||
}
|
||||
14
package/lean/luci-app-wrtbwmon/root/etc/uci-defaults/luci-wrtbwmon
Executable file
14
package/lean/luci-app-wrtbwmon/root/etc/uci-defaults/luci-wrtbwmon
Executable file
@ -0,0 +1,14 @@
|
||||
#!/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
|
||||
1
package/lean/luci-app-wrtbwmon/root/etc/wrtbwmon.include
Executable file
1
package/lean/luci-app-wrtbwmon/root/etc/wrtbwmon.include
Executable file
@ -0,0 +1 @@
|
||||
/etc/init.d/wrtbwmon restart >/dev/null 2>&1
|
||||
157
package/lean/luci-app-wrtbwmon/root/usr/sbin/readDB.awk
Executable file
157
package/lean/luci-app-wrtbwmon/root/usr/sbin/readDB.awk
Executable file
@ -0,0 +1,157 @@
|
||||
#!/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)
|
||||
}
|
||||
301
package/lean/luci-app-wrtbwmon/root/usr/sbin/wrtbwmon
Executable file
301
package/lean/luci-app-wrtbwmon/root/usr/sbin/wrtbwmon
Executable file
@ -0,0 +1,301 @@
|
||||
#!/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
|
||||
@ -0,0 +1,23 @@
|
||||
<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(
|
||||
@ -0,0 +1,14 @@
|
||||
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>
|
||||
@ -21,10 +21,13 @@ PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:= \
|
||||
https://mirrors.cloud.tencent.com/openssl/source/ \
|
||||
https://mirrors.cloud.tencent.com/openssl/source/old/$(PKG_BASE)/ \
|
||||
https://ftp.fi.muni.cz/pub/openssl/source/ \
|
||||
ftp://ftp.pca.dfn.de/pub/tools/net/openssl/source/ \
|
||||
https://www.openssl.org/source/ \
|
||||
https://www.openssl.org/source/old/$(PKG_BASE)/
|
||||
https://www.openssl.org/source/old/$(PKG_BASE)/ \
|
||||
https://ftp.fi.muni.cz/pub/openssl/source/ \
|
||||
https://ftp.fi.muni.cz/pub/openssl/source/old/$(PKG_BASE)/ \
|
||||
ftp://ftp.pca.dfn.de/pub/tools/net/openssl/source/ \
|
||||
ftp://ftp.pca.dfn.de/pub/tools/net/openssl/source/old/$(PKG_BASE)/
|
||||
|
||||
PKG_HASH:=aaf2fcb575cdf6491b98ab4829abf78a3dec8402b8b81efc8f23c00d443981bf
|
||||
|
||||
PKG_LICENSE:=OpenSSL
|
||||
|
||||
@ -48,7 +48,7 @@ config WOLFSSL_HAS_WPAS
|
||||
default y
|
||||
|
||||
config WOLFSSL_HAS_ECC25519
|
||||
bool "Include ECC Curve 22519 support"
|
||||
bool "Include ECC Curve 25519 support"
|
||||
default n
|
||||
|
||||
config WOLFSSL_HAS_DEVCRYPTO
|
||||
|
||||
@ -96,10 +96,6 @@ function gen_outbound(node, tag, is_proxy)
|
||||
node.stream_security = "xtls"
|
||||
end
|
||||
end
|
||||
|
||||
if node.transport == "mkcp" or node.transport == "quic" then
|
||||
node.stream_security = "none"
|
||||
end
|
||||
end
|
||||
|
||||
result = {
|
||||
@ -260,77 +256,12 @@ if node_section then
|
||||
if node.protocol == "_shunt" then
|
||||
local rules = {}
|
||||
|
||||
ucursor:foreach(appname, "shunt_rules", function(e)
|
||||
local name = e[".name"]
|
||||
local _node_id = node[name] or "nil"
|
||||
local is_proxy = node[name .. "_proxy"] or "0"
|
||||
local outboundTag
|
||||
if _node_id == "_direct" then
|
||||
outboundTag = "direct"
|
||||
elseif _node_id == "_blackhole" then
|
||||
outboundTag = "blackhole"
|
||||
else
|
||||
if _node_id ~= "nil" then
|
||||
local has_outbound
|
||||
for index, value in ipairs(outbounds) do
|
||||
if value["_flag_tag"] == _node_id and value["_flag_is_proxy"] == is_proxy then
|
||||
has_outbound = api.clone(value)
|
||||
break
|
||||
end
|
||||
end
|
||||
if has_outbound then
|
||||
has_outbound["tag"] = name
|
||||
table.insert(outbounds, has_outbound)
|
||||
outboundTag = name
|
||||
else
|
||||
local _node = ucursor:get_all(appname, _node_id)
|
||||
local _outbound = gen_outbound(_node, name, is_proxy)
|
||||
if _outbound then
|
||||
if is_proxy == "1" then
|
||||
table.insert(rules, 1, {
|
||||
type = "field",
|
||||
inboundTag = {"proxy_" .. name},
|
||||
outboundTag = "default"
|
||||
})
|
||||
end
|
||||
table.insert(outbounds, _outbound)
|
||||
outboundTag = name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if outboundTag then
|
||||
if e.domain_list then
|
||||
local _domain = {}
|
||||
string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w)
|
||||
table.insert(_domain, w)
|
||||
end)
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = outboundTag,
|
||||
domain = _domain
|
||||
})
|
||||
end
|
||||
if e.ip_list then
|
||||
local _ip = {}
|
||||
string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w)
|
||||
table.insert(_ip, w)
|
||||
end)
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = outboundTag,
|
||||
ip = _ip
|
||||
})
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local default_node_id = node.default_node or "_direct"
|
||||
local outboundTag
|
||||
local default_outboundTag
|
||||
if default_node_id == "_direct" then
|
||||
outboundTag = "direct"
|
||||
default_outboundTag = "direct"
|
||||
elseif default_node_id == "_blackhole" then
|
||||
outboundTag = "blackhole"
|
||||
default_outboundTag = "blackhole"
|
||||
else
|
||||
local default_node = ucursor:get_all(appname, default_node_id)
|
||||
local main_node_id = node.main_node or "nil"
|
||||
@ -365,13 +296,100 @@ if node_section then
|
||||
local default_outbound = gen_outbound(default_node, "default")
|
||||
if default_outbound then
|
||||
table.insert(outbounds, default_outbound)
|
||||
outboundTag = "default"
|
||||
default_outboundTag = "default"
|
||||
end
|
||||
end
|
||||
if outboundTag then
|
||||
|
||||
ucursor:foreach(appname, "shunt_rules", function(e)
|
||||
local name = e[".name"]
|
||||
local _node_id = node[name] or "nil"
|
||||
local is_proxy = node[name .. "_proxy"] or "0"
|
||||
local outboundTag
|
||||
if _node_id == "_direct" then
|
||||
outboundTag = "direct"
|
||||
elseif _node_id == "_blackhole" then
|
||||
outboundTag = "blackhole"
|
||||
elseif _node_id == "_default" then
|
||||
outboundTag = "default"
|
||||
else
|
||||
if _node_id ~= "nil" then
|
||||
local has_outbound
|
||||
for index, value in ipairs(outbounds) do
|
||||
if value["_flag_tag"] == _node_id and value["_flag_is_proxy"] == is_proxy then
|
||||
has_outbound = api.clone(value)
|
||||
break
|
||||
end
|
||||
end
|
||||
if has_outbound then
|
||||
has_outbound["tag"] = name
|
||||
table.insert(outbounds, has_outbound)
|
||||
outboundTag = name
|
||||
else
|
||||
local _node = ucursor:get_all(appname, _node_id)
|
||||
local _outbound = gen_outbound(_node, name, is_proxy)
|
||||
if _outbound then
|
||||
if is_proxy == "1" then
|
||||
table.insert(rules, 1, {
|
||||
type = "field",
|
||||
inboundTag = {"proxy_" .. name},
|
||||
outboundTag = "default"
|
||||
})
|
||||
end
|
||||
table.insert(outbounds, _outbound)
|
||||
outboundTag = name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if outboundTag then
|
||||
if outboundTag == "default" then
|
||||
outboundTag = default_outboundTag
|
||||
end
|
||||
local protocols = nil
|
||||
if e["protocol"] and e["protocol"] ~= "" then
|
||||
protocols = {}
|
||||
string.gsub(e["protocol"], '[^' .. " " .. ']+', function(w)
|
||||
table.insert(protocols, w)
|
||||
end)
|
||||
end
|
||||
if e.domain_list then
|
||||
local _domain = {}
|
||||
string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w)
|
||||
table.insert(_domain, w)
|
||||
end)
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = outboundTag,
|
||||
domain = _domain,
|
||||
protocol = protocols
|
||||
})
|
||||
end
|
||||
if e.ip_list then
|
||||
local _ip = {}
|
||||
string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w)
|
||||
table.insert(_ip, w)
|
||||
end)
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = outboundTag,
|
||||
ip = _ip,
|
||||
protocol = protocols
|
||||
})
|
||||
end
|
||||
if not e.domain_list and not e.ip_list and protocols then
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = outboundTag,
|
||||
protocol = protocols
|
||||
})
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if default_outboundTag then
|
||||
table.insert(rules, {
|
||||
type = "field",
|
||||
outboundTag = outboundTag,
|
||||
outboundTag = default_outboundTag,
|
||||
network = network
|
||||
})
|
||||
end
|
||||
|
||||
@ -120,7 +120,8 @@ if has_xray and #nodes_table > 0 then
|
||||
local id = e[".name"]
|
||||
o = s:taboption("Main", ListValue, v.id .. "." .. id .. "_node", string.format('* <a href="%s" target="_blank">%s</a>', api.url("shunt_rules", id), translate(e.remarks)))
|
||||
o:depends("tcp_node", v.id)
|
||||
o:value("nil", translate("Default"))
|
||||
o:value("nil", translate("Close"))
|
||||
o:value("_default", translate("Default"))
|
||||
o:value("_direct", translate("Direct Connection"))
|
||||
o:value("_blackhole", translate("Blackhole"))
|
||||
for k1, v1 in pairs(normal_list) do
|
||||
@ -174,26 +175,6 @@ udp_node:value("tcp_", translate("Same as the tcp node"))
|
||||
|
||||
s:tab("DNS", translate("DNS"))
|
||||
|
||||
o = s:taboption("DNS", Value, "up_china_dns", translate("Local DNS") .. "(UDP)")
|
||||
o.description = translate("IP:Port mode acceptable, multi value split with english comma.") .. "<br />" .. translate("When the selection is not the default, this DNS is forced to be set to dnsmasq upstream DNS.")
|
||||
o.default = "default"
|
||||
o:value("default", translate("Default"))
|
||||
if has_xray then
|
||||
o:value("xray_doh", "Xray DNS(DoH)")
|
||||
end
|
||||
o:value("223.5.5.5", "223.5.5.5 (" .. translate("Ali") .. "DNS)")
|
||||
o:value("114.114.114.114", "114.114.114.114 (114DNS)")
|
||||
o:value("119.29.29.29", "119.29.29.29 (DNSPOD DNS)")
|
||||
o:value("180.76.76.76", "180.76.76.76 (" .. translate("Baidu") .. "DNS)")
|
||||
|
||||
---- DoH
|
||||
o = s:taboption("DNS", Value, "up_china_dns_doh", translate("DoH request address"))
|
||||
o:value("https://dns.alidns.com/dns-query,223.5.5.5", "AliDNS")
|
||||
o:value("https://doh.pub/dns-query,119.29.29.29", "DNSPod")
|
||||
o.default = "https://dns.alidns.com/dns-query,223.5.5.5"
|
||||
o.validate = doh_validate
|
||||
o:depends("up_china_dns", "xray_doh")
|
||||
|
||||
---- DNS Forward Mode
|
||||
o = s:taboption("DNS", ListValue, "dns_mode", translate("Filter Mode"))
|
||||
o.rmempty = false
|
||||
@ -209,8 +190,8 @@ if has_xray then
|
||||
o:value("xray_doh", "Xray DNS(DoH)")
|
||||
end
|
||||
o:value("udp", translatef("Requery DNS By %s", translate("UDP Node")))
|
||||
o:value("custom", translate("Custom DNS") .. "(UDP)")
|
||||
o:value("nonuse", translate("No Filter"))
|
||||
o:value("custom", translate("Custom DNS"))
|
||||
|
||||
---- Custom DNS
|
||||
o = s:taboption("DNS", Value, "custom_dns", translate("Custom DNS"))
|
||||
|
||||
@ -129,7 +129,8 @@ balancing_node:depends("protocol", "_balancing")
|
||||
-- 分流
|
||||
uci:foreach(appname, "shunt_rules", function(e)
|
||||
o = s:option(ListValue, e[".name"], string.format('* <a href="%s" target="_blank">%s</a>', api.url("shunt_rules", e[".name"]), translate(e.remarks)))
|
||||
o:value("nil", translate("Default"))
|
||||
o:value("nil", translate("Close"))
|
||||
o:value("_default", translate("Default"))
|
||||
o:value("_direct", translate("Direct Connection"))
|
||||
o:value("_blackhole", translate("Blackhole"))
|
||||
o:depends("protocol", "_shunt")
|
||||
|
||||
@ -56,7 +56,7 @@ o = s:option(Value, "xray_location_asset", translate("Location of Xray asset"),
|
||||
o.default = "/usr/share/xray/"
|
||||
o.rmempty = false
|
||||
|
||||
s = m:section(TypedSection, "shunt_rules", "Xray" .. translate("Shunt") .. translate("Rule"))
|
||||
s = m:section(TypedSection, "shunt_rules", "Xray" .. translate("Shunt") .. translate("Rule"), "<a style='color: red'>" .. translate("Please note attention to the priority, the higher the order, the higher the priority.") .. "</a>")
|
||||
s.template = "cbi/tblsection"
|
||||
s.anonymous = false
|
||||
s.addremove = true
|
||||
|
||||
@ -12,6 +12,11 @@ remarks = s:option(Value, "remarks", translate("Remarks"))
|
||||
remarks.default = arg[1]
|
||||
remarks.rmempty = false
|
||||
|
||||
protocol = s:option(MultiValue, "protocol", translate("Protocol"))
|
||||
protocol:value("http")
|
||||
protocol:value("tls")
|
||||
protocol:value("bittorrent")
|
||||
|
||||
domain_list = s:option(TextValue, "domain_list", translate("Domain"))
|
||||
domain_list.rows = 10
|
||||
domain_list.wrap = "off"
|
||||
|
||||
@ -15,7 +15,6 @@ function gen_config(user)
|
||||
clients[i] = {
|
||||
id = user.uuid[i],
|
||||
flow = ("1" == user.xtls) and user.flow or nil,
|
||||
level = user.level and tonumber(user.level) or nil,
|
||||
alterId = user.alter_id and tonumber(user.alter_id) or nil
|
||||
}
|
||||
end
|
||||
@ -50,7 +49,6 @@ function gen_config(user)
|
||||
settings = {
|
||||
method = user.method,
|
||||
password = user.password,
|
||||
level = user.level and tonumber(user.level) or nil,
|
||||
network = user.ss_network or "TCP,UDP"
|
||||
}
|
||||
elseif user.protocol == "trojan" then
|
||||
@ -60,7 +58,6 @@ function gen_config(user)
|
||||
clients[i] = {
|
||||
flow = ("1" == user.xtls) and user.flow or nil,
|
||||
password = user.uuid[i],
|
||||
level = user.level and tonumber(user.level) or nil,
|
||||
}
|
||||
end
|
||||
settings = {
|
||||
@ -71,7 +68,6 @@ function gen_config(user)
|
||||
settings = {
|
||||
users = {
|
||||
{
|
||||
level = user.level and tonumber(user.level) or nil,
|
||||
secret = (user.password == nil) and "" or user.password
|
||||
}
|
||||
}
|
||||
@ -115,9 +111,23 @@ function gen_config(user)
|
||||
}
|
||||
|
||||
if user.transit_node and user.transit_node ~= "nil" then
|
||||
local transit_node_t = ucic:get_all("passwall", user.transit_node)
|
||||
if user.transit_node == "_socks" or user.transit_node == "_http" then
|
||||
transit_node_t = {
|
||||
type = "Xray",
|
||||
protocol = user.transit_node:gsub("_", ""),
|
||||
transport = "tcp",
|
||||
address = user.transit_node_address,
|
||||
port = user.transit_node_port,
|
||||
username = (user.transit_node_username and user.transit_node_username ~= "") and user.transit_node_username or nil,
|
||||
password = (user.transit_node_password and user.transit_node_password ~= "") and user.transit_node_password or nil,
|
||||
}
|
||||
end
|
||||
local gen_xray = require("luci.model.cbi.passwall.api.gen_xray")
|
||||
local client = gen_xray.gen_outbound(ucic:get_all("passwall", user.transit_node), "transit")
|
||||
table.insert(outbounds, 1, client)
|
||||
local outbound = gen_xray.gen_outbound(transit_node_t, "transit")
|
||||
if outbound then
|
||||
table.insert(outbounds, 1, outbound)
|
||||
end
|
||||
end
|
||||
|
||||
local config = {
|
||||
@ -216,10 +226,5 @@ function gen_config(user)
|
||||
end
|
||||
end
|
||||
|
||||
if user.transport == "mkcp" or user.transport == "quic" then
|
||||
config.inbounds[1].streamSettings.security = "none"
|
||||
config.inbounds[1].streamSettings.tlsSettings = nil
|
||||
end
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
@ -246,13 +246,6 @@ uuid:depends("type", "Trojan-Plus")
|
||||
alter_id = s:option(Value, "alter_id", translate("Alter ID"))
|
||||
alter_id:depends({ type = "Xray", protocol = "vmess" })
|
||||
|
||||
level = s:option(Value, "level", translate("User Level"))
|
||||
level:depends({ type = "Xray", protocol = "vmess" })
|
||||
level:depends({ type = "Xray", protocol = "vless" })
|
||||
level:depends({ type = "Xray", protocol = "shadowsocks" })
|
||||
level:depends({ type = "Xray", protocol = "trojan" })
|
||||
level:depends({ type = "Xray", protocol = "mtproto" })
|
||||
|
||||
tls = s:option(Flag, "tls", translate("TLS"))
|
||||
tls.default = 0
|
||||
tls.validate = function(self, value, t)
|
||||
@ -565,10 +558,30 @@ end
|
||||
|
||||
transit_node = s:option(ListValue, "transit_node", translate("transit node"))
|
||||
transit_node:value("nil", translate("Close"))
|
||||
transit_node:value("_socks", translate("Custom Socks"))
|
||||
transit_node:value("_http", translate("Custom HTTP"))
|
||||
for k, v in pairs(nodes_table) do transit_node:value(v.id, v.remarks) end
|
||||
transit_node.default = "nil"
|
||||
transit_node:depends("type", "Xray")
|
||||
|
||||
transit_node_address = s:option(Value, "transit_node_address", translate("Address (Support Domain Name)"))
|
||||
transit_node_address:depends("transit_node", "_socks")
|
||||
transit_node_address:depends("transit_node", "_http")
|
||||
|
||||
transit_node_port = s:option(Value, "transit_node_port", translate("Port"))
|
||||
transit_node_port.datatype = "port"
|
||||
transit_node_port:depends("transit_node", "_socks")
|
||||
transit_node_port:depends("transit_node", "_http")
|
||||
|
||||
transit_node_username = s:option(Value, "transit_node_username", translate("Username"))
|
||||
transit_node_username:depends("transit_node", "_socks")
|
||||
transit_node_username:depends("transit_node", "_http")
|
||||
|
||||
transit_node_password = s:option(Value, "transit_node_password", translate("Password"))
|
||||
transit_node_password.password = true
|
||||
transit_node_password:depends("transit_node", "_socks")
|
||||
transit_node_password:depends("transit_node", "_http")
|
||||
|
||||
log = s:option(Flag, "log", translate("Enable") .. translate("Log"))
|
||||
log.default = "1"
|
||||
log.rmempty = false
|
||||
|
||||
@ -180,11 +180,11 @@ local api = require "luci.model.cbi.passwall.api.api"
|
||||
"@" + v_server.value +
|
||||
":" + v_port.value + "/?";
|
||||
if (opt.get("tls").checked) {
|
||||
url += "tls=1";
|
||||
url += opt.query("sni", "tls_serverName");
|
||||
url += opt.query("allowinsecure", "tls_allowInsecure");
|
||||
} else if (v_type === "Trojan-Go") {
|
||||
url += "tls=0";
|
||||
if (v_type !== "Trojan-Go") {
|
||||
url += "tls=1"
|
||||
url += opt.query("allowinsecure", "tls_allowInsecure");
|
||||
}
|
||||
}
|
||||
if (v_type === "Trojan-Go") {
|
||||
if (!opt.get("tls").checked && opt.get("trojan_transport").value === "original") {
|
||||
@ -214,7 +214,6 @@ local api = require "luci.model.cbi.passwall.api.api"
|
||||
":" + opt.get("ss_aead_pwd").value;
|
||||
}
|
||||
url += "&encryption=" + encodeURIComponent(enc);
|
||||
url += opt.query("mux", "mux");
|
||||
}
|
||||
url += "#" + encodeURI(v_alias.value);
|
||||
}
|
||||
@ -492,8 +491,10 @@ local api = require "luci.model.cbi.passwall.api.api"
|
||||
opt.set('address', m.hostname);
|
||||
opt.set('port', m.port || "443");
|
||||
opt.set(opt.client ? 'password' : 'passwords', decodeURIComponent(password));
|
||||
opt.set('tls', (queryParam.tls && queryParam.tls === '1'));
|
||||
opt.set('tls', '1');
|
||||
opt.get('tls').dispatchEvent(event);
|
||||
opt.set('tls_allowInsecure', '0');
|
||||
opt.set('tls_serverName', queryParam.peer || queryParam.sni || '');
|
||||
var plugin = queryParam.plugin !== undefined;
|
||||
if (plugin) {
|
||||
opt.set('trojan_transport', 'original');
|
||||
@ -510,13 +511,6 @@ local api = require "luci.model.cbi.passwall.api.api"
|
||||
} else
|
||||
alert(queryParam.plugin);
|
||||
}
|
||||
var tls = !plugin && queryParam.tls === '1';
|
||||
opt.set('tls', tls);
|
||||
opt.get('tls').dispatchEvent(event);
|
||||
if (tls) {
|
||||
opt.set('tls_serverName', queryParam.peer || queryParam.sni || '');
|
||||
opt.set('tls_allowInsecure', queryParam.allowinsecure === '1');
|
||||
}
|
||||
var tran = 'original';
|
||||
var or = queryParam.type === undefined || queryParam.type === 'original';
|
||||
var ws = queryParam.type.indexOf('ws') !== -1;
|
||||
@ -549,7 +543,7 @@ local api = require "luci.model.cbi.passwall.api.api"
|
||||
opt.set('ss_aead_method', enc.method.toLowerCase() || '');
|
||||
opt.set('ss_aead_pwd', enc.password || '');
|
||||
}
|
||||
opt.set('mux', queryParam.mux === '1');
|
||||
opt.set('mux', '1');
|
||||
if (m.hash) {
|
||||
opt.set('remarks', decodeURI(m.hash.substr(1)));
|
||||
}
|
||||
|
||||
@ -421,12 +421,12 @@ msgstr "黑洞"
|
||||
msgid "Use the default node for the transit."
|
||||
msgstr "使用默认节点代理转发。"
|
||||
|
||||
msgid "Use the under node for the transit."
|
||||
msgstr "使用下面的节点代理转发。"
|
||||
|
||||
msgid "No shunt rules? Click me to go to add."
|
||||
msgstr "没有分流规则?点我前往去添加。"
|
||||
|
||||
msgid "Use this node proxy to forward the default node."
|
||||
msgstr "使用此节点代理转发默认节点。"
|
||||
|
||||
msgid "Domain Strategy"
|
||||
msgstr "域名解析策略"
|
||||
|
||||
@ -757,6 +757,9 @@ msgstr "Xray 资源文件目录"
|
||||
msgid "This variable specifies a directory where geoip.dat and geosite.dat files are."
|
||||
msgstr "此变量指定geoip.dat和geosite.dat文件所在的目录。"
|
||||
|
||||
msgid "Please note attention to the priority, the higher the order, the higher the priority."
|
||||
msgstr "请注意优先级问题,排序越上面优先级越高。"
|
||||
|
||||
msgid "Update..."
|
||||
msgstr "更新中"
|
||||
|
||||
@ -1102,6 +1105,12 @@ msgstr "是否接收 PROXY protocol,当该节点要被回落或被代理转发
|
||||
msgid "transit node"
|
||||
msgstr "中转到此节点"
|
||||
|
||||
msgid "Custom Socks"
|
||||
msgstr "自定义 Socks"
|
||||
|
||||
msgid "Custom HTTP"
|
||||
msgstr "自定义 HTTP"
|
||||
|
||||
msgid "Bind Local"
|
||||
msgstr "本机监听"
|
||||
|
||||
|
||||
@ -324,7 +324,7 @@ load_config() {
|
||||
DNS_MODE=$(config_t_get global dns_mode pdnsd)
|
||||
DNS_FORWARD=$(config_t_get global dns_forward 8.8.4.4:53 | sed 's/:/#/g')
|
||||
DNS_CACHE=$(config_t_get global dns_cache 0)
|
||||
LOCAL_DNS=$(config_t_get global up_china_dns default | sed 's/:/#/g')
|
||||
LOCAL_DNS="default"
|
||||
if [ "${LOCAL_DNS}" = "default" ]; then
|
||||
DEFAULT_DNS=$(uci show dhcp | grep "@dnsmasq" | grep "\.server=" | awk -F '=' '{print $2}' | sed "s/'//g" | tr ' ' ',')
|
||||
if [ -z "${DEFAULT_DNS}" ]; then
|
||||
@ -776,19 +776,6 @@ stop_crontab() {
|
||||
}
|
||||
|
||||
start_dns() {
|
||||
if [ "${LOCAL_DNS}" = "xray_doh" ]; then
|
||||
_doh=$(config_t_get global up_china_dns_doh "https://dns.alidns.com/dns-query,223.5.5.5")
|
||||
_doh_url=$(echo $_doh | awk -F ',' '{print $1}')
|
||||
_doh_host_port=$(echo $_doh_url | sed "s/https:\/\///g" | awk -F '/' '{print $1}')
|
||||
_doh_host=$(echo $_doh_host_port | awk -F ':' '{print $1}')
|
||||
_doh_port=$(echo $_doh_host_port | awk -F ':' '{print $2}')
|
||||
_doh_bootstrap=$(echo $_doh | cut -d ',' -sf 2-)
|
||||
lua $API_GEN_XRAY -dns_listen_port "${LOCAL_DOH_PORT}" -dns_server "${_doh_bootstrap}" -doh_url "${_doh_url}" -doh_host "${_doh_host}" > $TMP_PATH/DNS1.json
|
||||
ln_start_bin "$(first_type $(config_t_get global_app xray_file) xray)" xray $TMP_PATH/DNS1.log -config="$TMP_PATH/DNS1.json"
|
||||
LOCAL_DNS="127.0.0.1#${LOCAL_DOH_PORT}"
|
||||
unset _doh _doh_url _doh_bootstrap
|
||||
fi
|
||||
|
||||
local pdnsd_forward other_port msg
|
||||
dns_listen_port=${DNS_PORT}
|
||||
pdnsd_forward=${DNS_FORWARD}
|
||||
@ -974,7 +961,7 @@ add_dnsmasq() {
|
||||
local shunt_ids=$(uci show $CONFIG | grep "=shunt_rules" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}')
|
||||
for shunt_id in $shunt_ids; do
|
||||
local shunt_node_id=$(config_n_get $TCP_NODE ${shunt_id} nil)
|
||||
if [ "$shunt_node_id" = "nil" ] || [ "$shunt_node_id" = "_direct" ] || [ "$shunt_node_id" = "_blackhole" ]; then
|
||||
if [ "$shunt_node_id" = "nil" ] || [ "$shunt_node_id" = "_default" ] || [ "$shunt_node_id" = "_direct" ] || [ "$shunt_node_id" = "_blackhole" ]; then
|
||||
continue
|
||||
fi
|
||||
local shunt_node=$(config_n_get $shunt_node_id address nil)
|
||||
|
||||
@ -208,6 +208,7 @@ load_acl() {
|
||||
msg2="${msg2}[$?]除${tcp_no_redir_ports}外的"
|
||||
}
|
||||
msg2="${msg2}所有端口"
|
||||
$ipt_tmp -A PSW $(comment "$remarks") -p tcp $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -d 1.2.3.4 $(REDIRECT $tcp_port $is_tproxy)
|
||||
$ipt_tmp -A PSW $(comment "$remarks") -p tcp $(factor $ip "-s") $(factor $mac "-m mac --mac-source") $(factor $tcp_redir_ports "-m multiport --dport") $(dst $IPSET_SHUNTLIST) $(REDIRECT $tcp_port $is_tproxy)
|
||||
$ipt_tmp -A PSW $(comment "$remarks") -p tcp $(factor $ip "-s") $(factor $mac "-m mac --mac-source") $(factor $tcp_redir_ports "-m multiport --dport") $(dst $IPSET_BLACKLIST) $(REDIRECT $tcp_port $is_tproxy)
|
||||
$ipt_tmp -A PSW $(comment "$remarks") -p tcp $(factor $ip "-s") $(factor $mac "-m mac --mac-source") $(factor $tcp_redir_ports "-m multiport --dport") $(get_redirect_ipt $tcp_proxy_mode $tcp_port $is_tproxy)
|
||||
@ -234,6 +235,7 @@ load_acl() {
|
||||
msg2="${msg2}[$?]除${udp_no_redir_ports}外的"
|
||||
}
|
||||
msg2="${msg2}所有端口"
|
||||
$ipt_m -A PSW $(comment "$remarks") -p tcp $(factor $ip "-s") $(factor $mac "-m mac --mac-source") -d 1.2.3.4 $(REDIRECT $udp_port TPROXY)
|
||||
$ipt_m -A PSW $(comment "$remarks") -p udp $(factor $ip "-s") $(factor $mac "-m mac --mac-source") $(factor $udp_redir_ports "-m multiport --dport") $(dst $IPSET_SHUNTLIST) $(REDIRECT $udp_port TPROXY)
|
||||
$ipt_m -A PSW $(comment "$remarks") -p udp $(factor $ip "-s") $(factor $mac "-m mac --mac-source") $(factor $udp_redir_ports "-m multiport --dport") $(dst $IPSET_BLACKLIST) $(REDIRECT $udp_port TPROXY)
|
||||
$ipt_m -A PSW $(comment "$remarks") -p udp $(factor $ip "-s") $(factor $mac "-m mac --mac-source") $(factor $udp_redir_ports "-m multiport --dport") $(get_redirect_ipt $udp_proxy_mode $udp_port TPROXY)
|
||||
@ -275,6 +277,7 @@ load_acl() {
|
||||
fi
|
||||
[ "$TCP_NO_REDIR_PORTS" != "disable" ] && msg="${msg}除${TCP_NO_REDIR_PORTS}外的"
|
||||
msg="${msg}所有端口"
|
||||
$ipt_tmp -A PSW $(comment "默认") -p tcp -d 1.2.3.4 $(REDIRECT $TCP_REDIR_PORT $is_tproxy)
|
||||
$ipt_tmp -A PSW $(comment "默认") -p tcp $(factor $TCP_REDIR_PORTS "-m multiport --dport") $(dst $IPSET_SHUNTLIST) $(REDIRECT $TCP_REDIR_PORT $is_tproxy)
|
||||
$ipt_tmp -A PSW $(comment "默认") -p tcp $(factor $TCP_REDIR_PORTS "-m multiport --dport") $(dst $IPSET_BLACKLIST) $(REDIRECT $TCP_REDIR_PORT $is_tproxy)
|
||||
$ipt_tmp -A PSW $(comment "默认") -p tcp $(factor $TCP_REDIR_PORTS "-m multiport --dport") $(get_redirect_ipt $TCP_PROXY_MODE $TCP_REDIR_PORT $is_tproxy)
|
||||
@ -303,6 +306,7 @@ load_acl() {
|
||||
msg="UDP默认代理:使用UDP节点 [$(get_action_chain_name $UDP_PROXY_MODE)](TPROXY:${UDP_REDIR_PORT})代理"
|
||||
[ "$UDP_NO_REDIR_PORTS" != "disable" ] && msg="${msg}除${UDP_NO_REDIR_PORTS}外的"
|
||||
msg="${msg}所有端口"
|
||||
$ipt_m -A PSW $(comment "默认") -p udp -d 1.2.3.4 $(REDIRECT $UDP_REDIR_PORT TPROXY)
|
||||
$ipt_m -A PSW $(comment "默认") -p udp $(factor $UDP_REDIR_PORTS "-m multiport --dport") $(dst $IPSET_SHUNTLIST) $(REDIRECT $UDP_REDIR_PORT TPROXY)
|
||||
$ipt_m -A PSW $(comment "默认") -p udp $(factor $UDP_REDIR_PORTS "-m multiport --dport") $(dst $IPSET_BLACKLIST) $(REDIRECT $UDP_REDIR_PORT TPROXY)
|
||||
$ipt_m -A PSW $(comment "默认") -p udp $(factor $UDP_REDIR_PORTS "-m multiport --dport") $(get_redirect_ipt $UDP_PROXY_MODE $UDP_REDIR_PORT TPROXY)
|
||||
@ -638,12 +642,12 @@ 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
|
||||
echolog " - [$?]不代理TCP 端口:$TCP_NO_REDIR_PORTS"
|
||||
}
|
||||
$ipt_tmp -A PSW_OUTPUT -p tcp -d 1.2.3.4 $blist_r
|
||||
$ipt_tmp -A PSW_OUTPUT -p tcp $(factor $TCP_REDIR_PORTS "-m multiport --dport") $(dst $IPSET_SHUNTLIST) $blist_r
|
||||
$ipt_tmp -A PSW_OUTPUT -p tcp $(factor $TCP_REDIR_PORTS "-m multiport --dport") $(dst $IPSET_BLACKLIST) $blist_r
|
||||
$ipt_tmp -A PSW_OUTPUT -p tcp $(factor $TCP_REDIR_PORTS "-m multiport --dport") $p_r
|
||||
@ -731,12 +735,12 @@ 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
|
||||
echolog " - [$?]不代理 UDP 端口:$UDP_NO_REDIR_PORTS"
|
||||
}
|
||||
$ipt_m -A PSW_OUTPUT -p udp -d 1.2.3.4 $(REDIRECT 1 MARK)
|
||||
$ipt_m -A PSW_OUTPUT -p udp $(factor $UDP_REDIR_PORTS "-m multiport --dport") $(dst $IPSET_SHUNTLIST) $(REDIRECT 1 MARK)
|
||||
$ipt_m -A PSW_OUTPUT -p udp $(factor $UDP_REDIR_PORTS "-m multiport --dport") $(dst $IPSET_BLACKLIST) $(REDIRECT 1 MARK)
|
||||
$ipt_m -A PSW_OUTPUT -p udp $(factor $UDP_REDIR_PORTS "-m multiport --dport") $(get_redirect_ipt $LOCALHOST_UDP_PROXY_MODE 1 MARK)
|
||||
|
||||
@ -473,7 +473,6 @@ local function processData(szType, content, add_mode)
|
||||
end
|
||||
if params.peer then peer = params.peer end
|
||||
sni = params.sni and params.sni or ""
|
||||
if params.mux and params.mux == "1" then result.mux = "1" end
|
||||
if params.ws and params.ws == "1" then
|
||||
result.trojan_transport = "ws"
|
||||
if params.wshost then result.ws_host = params.wshost end
|
||||
@ -486,9 +485,10 @@ local function processData(szType, content, add_mode)
|
||||
if params.sspasswd then result.ss_aead_pwd = params.sspasswd end
|
||||
end
|
||||
result.port = port
|
||||
if result.mux or result.trojan_transport == "ws" or result.ss_aead then
|
||||
if result.trojan_transport == "ws" or result.ss_aead then
|
||||
result.type = "Trojan-Go"
|
||||
result.fingerprint = "firefox"
|
||||
result.mux = "1"
|
||||
end
|
||||
result.tls = '1'
|
||||
result.tls_serverName = peer and peer or sni
|
||||
@ -525,19 +525,14 @@ local function processData(szType, content, add_mode)
|
||||
result.address = hostInfo and hostInfo[1] or Info[2]
|
||||
end
|
||||
local peer, sni = nil, ""
|
||||
local allowInsecure = allowInsecure_default
|
||||
local query = split(Info[2], "?")
|
||||
local params = {}
|
||||
for _, v in pairs(split(query[2], '&')) do
|
||||
local t = split(v, '=')
|
||||
params[string.lower(t[1])] = UrlDecode(t[2])
|
||||
end
|
||||
if params.allowinsecure then
|
||||
allowInsecure = params.allowinsecure
|
||||
end
|
||||
if params.peer then peer = params.peer end
|
||||
sni = params.sni and params.sni or ""
|
||||
if params.mux and params.mux == "1" then result.mux = "1" end
|
||||
if params.type and params.type == "ws" then
|
||||
result.trojan_transport = "ws"
|
||||
if params.host then result.ws_host = params.host end
|
||||
@ -551,9 +546,10 @@ local function processData(szType, content, add_mode)
|
||||
end
|
||||
result.port = port
|
||||
result.fingerprint = "firefox"
|
||||
result.tls = '1'
|
||||
result.tls = "1"
|
||||
result.tls_serverName = peer and peer or sni
|
||||
result.tls_allowInsecure = allowInsecure and "1" or "0"
|
||||
result.tls_allowInsecure = "0"
|
||||
result.mux = "1"
|
||||
end
|
||||
elseif szType == "ssd" then
|
||||
result.type = "SS"
|
||||
|
||||
@ -18,6 +18,52 @@ menu "Global build settings"
|
||||
bool "Cryptographically sign package lists"
|
||||
default y
|
||||
|
||||
comment "Package build options"
|
||||
|
||||
config DEBUG
|
||||
bool
|
||||
prompt "Compile packages with debugging info"
|
||||
default n
|
||||
help
|
||||
Adds -g3 to the CFLAGS.
|
||||
|
||||
comment "Stripping options"
|
||||
|
||||
choice
|
||||
prompt "Binary stripping method"
|
||||
default USE_STRIP if EXTERNAL_TOOLCHAIN
|
||||
default USE_STRIP if USE_GLIBC
|
||||
default USE_SSTRIP
|
||||
help
|
||||
Select the binary stripping method you wish to use.
|
||||
|
||||
config NO_STRIP
|
||||
bool "none"
|
||||
help
|
||||
This will install unstripped binaries (useful for native
|
||||
compiling/debugging).
|
||||
|
||||
config USE_STRIP
|
||||
bool "strip"
|
||||
help
|
||||
This will install binaries stripped using strip from binutils.
|
||||
|
||||
config USE_SSTRIP
|
||||
bool "sstrip"
|
||||
depends on !USE_GLIBC
|
||||
help
|
||||
This will install binaries stripped using sstrip.
|
||||
endchoice
|
||||
|
||||
config STRIP_ARGS
|
||||
string
|
||||
prompt "Strip arguments"
|
||||
depends on USE_STRIP
|
||||
default "--strip-unneeded --remove-section=.comment --remove-section=.note" if DEBUG
|
||||
default "--strip-all"
|
||||
help
|
||||
Specifies arguments passed to the strip command when stripping binaries.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Advanced configuration options (for developers)"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user