Merge Mainline

Signed-off-by: CN_SZTL <cnsztl@project-openwrt.eu.org>
This commit is contained in:
CN_SZTL 2021-02-21 15:57:16 +08:00
commit dfd0342804
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
51 changed files with 1598 additions and 1795 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

@ -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 "。"

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@ -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 = {

View File

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

View File

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

View File

@ -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']"`

View 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

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

View File

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

View File

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

View File

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

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

View 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 "加载中..."

View File

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

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

View 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

View File

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

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 "本机监听"

View File

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

View File

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

View File

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

View File

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