mirror of
https://github.com/Ysurac/openmptcprouter-feeds.git
synced 2025-02-12 18:41:51 +00:00
Add luci-app-packet-capture
This commit is contained in:
parent
19dde3a38a
commit
93694b5983
9 changed files with 489 additions and 2 deletions
14
luci-app-packet-capture/Makefile
Normal file
14
luci-app-packet-capture/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Copyright 2020 Wojciech Jowsa (wojciech.jowsa@gmail.com)
|
||||||
|
# This is free software, licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:=Packet capture application
|
||||||
|
LUCI_DEPENDS:=+luci-mod-admin-full +tcpdump +uhttpd-mod-ubus +coreutils +coreutils-timeout
|
||||||
|
|
||||||
|
PKG_MAINTAINER:=Wojciech Jowsa <wojciech.jowsa@gmail.com>
|
||||||
|
PKG_LICENSE:=Apache-2.0
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signatureet
|
|
@ -0,0 +1,287 @@
|
||||||
|
'use strict';
|
||||||
|
'require rpc';
|
||||||
|
'require uci';
|
||||||
|
'require ui';
|
||||||
|
'require fs';
|
||||||
|
'require form';
|
||||||
|
'require network';
|
||||||
|
'require tools.widgets as widgets';
|
||||||
|
|
||||||
|
var eventSource,
|
||||||
|
captureFilePoll,
|
||||||
|
hostName;
|
||||||
|
|
||||||
|
function stopTcpdump() {
|
||||||
|
fs.exec("/usr/libexec/packet_capture_stop").then(function(replay) {
|
||||||
|
if (eventSource)
|
||||||
|
eventSource.close();
|
||||||
|
}.bind(this)).catch(function(error) {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('beforeunload', stopTcpdump);
|
||||||
|
|
||||||
|
var callLuciProcessList = rpc.declare({
|
||||||
|
object: 'luci',
|
||||||
|
method: 'getProcessList',
|
||||||
|
expect: { result: [] }
|
||||||
|
});
|
||||||
|
|
||||||
|
var callInitAction = rpc.declare({
|
||||||
|
object: 'luci',
|
||||||
|
method: 'setInitAction',
|
||||||
|
params: [ 'name', 'action' ],
|
||||||
|
expect: { result: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
function addOutput() {
|
||||||
|
var tcpdumpOut = document.querySelectorAll('[id$="tcpdump_out"]')[0];
|
||||||
|
if (tcpdumpOut)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var frameEl = E('div', {'class': 'cbi-value'});
|
||||||
|
|
||||||
|
frameEl.appendChild(E('textarea', {
|
||||||
|
'id': 'tcpdump_out',
|
||||||
|
'class': 'cbi-input-textarea',
|
||||||
|
'readonly': '',
|
||||||
|
'style': 'width:100%',
|
||||||
|
'rows': 30,
|
||||||
|
}));
|
||||||
|
|
||||||
|
frameEl.firstElementChild.style.fontFamily = 'monospace';
|
||||||
|
|
||||||
|
var downloadBtn = document.querySelectorAll('[id$="download_file"]')[0];
|
||||||
|
if (downloadBtn)
|
||||||
|
downloadBtn.parentNode.insertBefore(frameEl, downloadBtn.nextSibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
var downloadCaptureFile = function(ev) {
|
||||||
|
var form = E('form', {
|
||||||
|
method: 'post',
|
||||||
|
action: '/cgi-bin/cgi-download',
|
||||||
|
enctype: 'application/x-www-form-urlencoded'
|
||||||
|
}, E('input', { type: 'hidden', name: 'sessionid', value: rpc.getSessionID()},
|
||||||
|
E('input', { type: 'hidden', name: 'path', value: "/tmp/capture.pcap"},
|
||||||
|
E('input', { type: 'hidden', name: 'filename', value: hostName + "-" + Date.now() + ".pcap"},
|
||||||
|
E('input', { type: 'hidden', name: 'mimetype', value: 'application/vnd.tcpdump.pcap'}
|
||||||
|
)))));
|
||||||
|
|
||||||
|
ev.currentTarget.parentNode.appendChild(form);
|
||||||
|
form.submit();
|
||||||
|
form.parentNode.removeChild(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
function subscribeTcpdump() {
|
||||||
|
if (eventSource)
|
||||||
|
eventSource.close();
|
||||||
|
|
||||||
|
eventSource = new EventSource('/ubus/subscribe/tcpdump' + '?' + rpc.getSessionID());
|
||||||
|
eventSource.onerror = function(event) {
|
||||||
|
eventSource.close();
|
||||||
|
console.log(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
addOutput();
|
||||||
|
var textOut = document.querySelectorAll('[id$="tcpdump_out"]')[0];
|
||||||
|
textOut.value = "";
|
||||||
|
eventSource.addEventListener("tcpdump.data", function(event) {
|
||||||
|
textOut.value = textOut.value + "\n" + JSON.parse(event.data).data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateButtons() {
|
||||||
|
var tasks = [];
|
||||||
|
tasks.push(fs.stat("/var/run/packet_capture.pid").then(L.bind(function(res) {
|
||||||
|
var downloadBtn = document.querySelectorAll('[id$="download_file"]')[0];
|
||||||
|
if (!downloadBtn)
|
||||||
|
return;
|
||||||
|
if (!eventSource || eventSource.readyState == 2)
|
||||||
|
subscribeTcpdump();
|
||||||
|
var textOut = document.querySelectorAll('[id$="tcpdump_out"]')[0];
|
||||||
|
if (textOut)
|
||||||
|
textOut.style.borderColor = "green";
|
||||||
|
var startBtn = document.querySelectorAll('[id$="start_tcpdump"]')[0];
|
||||||
|
if (startBtn)
|
||||||
|
startBtn.hidden = true;
|
||||||
|
var stopBtn = document.querySelectorAll('[id$="stop_tcpdump"]')[0];
|
||||||
|
if (stopBtn)
|
||||||
|
stopBtn.hidden = false;
|
||||||
|
return;
|
||||||
|
})).catch(function(error) {
|
||||||
|
var textOut = document.querySelectorAll('[id$="tcpdump_out"]')[0];
|
||||||
|
if (textOut)
|
||||||
|
textOut.style.borderColor = "red";
|
||||||
|
var startBtn = document.querySelectorAll('[id$="start_tcpdump"]')[0];
|
||||||
|
if (startBtn)
|
||||||
|
startBtn.hidden = false;
|
||||||
|
var stopBtn = document.querySelectorAll('[id$="stop_tcpdump"]')[0];
|
||||||
|
if (stopBtn)
|
||||||
|
stopBtn.hidden = true;
|
||||||
|
if (eventSource)
|
||||||
|
eventSource.close();
|
||||||
|
}));
|
||||||
|
|
||||||
|
return Promise.all(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePollCheckCaptureFileExists() {
|
||||||
|
checkCaptureFileExists();
|
||||||
|
L.Poll.remove(captureFilePoll);
|
||||||
|
L.Poll.add(L.bind(checkCaptureFileExists, m),5);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCaptureFileExists() {
|
||||||
|
var tasks = [];
|
||||||
|
tasks.push(fs.stat("/tmp/capture.pcap").then(L.bind(function(res) {
|
||||||
|
var downloadBtn = document.querySelector('[data-action="download"]');
|
||||||
|
if (!downloadBtn)
|
||||||
|
return;
|
||||||
|
var downloadCheckBox = document.querySelectorAll('[data-widget-id$="file"]')[0].checked;
|
||||||
|
if (!downloadCheckBox) {
|
||||||
|
fs.remove("/tmp/capture.pcap").then(function(replay) {
|
||||||
|
downloadBtn.disabled = true;;
|
||||||
|
}.bind(this)).catch(function(error) {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
downloadBtn.disabled = false;
|
||||||
|
}
|
||||||
|
})).catch(function(error) {
|
||||||
|
var downloadBtn = document.querySelector('[data-action="download"]');
|
||||||
|
if (downloadBtn)
|
||||||
|
downloadBtn.disabled = true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
return Promise.all(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
return L.view.extend({
|
||||||
|
|
||||||
|
load: function() {
|
||||||
|
return Promise.all([
|
||||||
|
uci.load('system')
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDownload: function(ev) {
|
||||||
|
downloadCaptureFile(ev);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function(processes) {
|
||||||
|
var m, s, o;
|
||||||
|
|
||||||
|
hostName = uci.get('system', '@system[0]', 'hostname');
|
||||||
|
|
||||||
|
m = new form.Map('packet_capture', _('Packet Capture - Tcpdump'), _('Capture packets with tcpdump.'));
|
||||||
|
s = m.section(form.TypedSection, 'tcpdump');
|
||||||
|
s.anonymous = 1;
|
||||||
|
|
||||||
|
o = s.option(widgets.DeviceSelect, 'interface', _('Interface'), _(''));
|
||||||
|
o.noaliases = true;
|
||||||
|
o.modalonly = true;
|
||||||
|
o.rmempty = false;
|
||||||
|
o.filter = function(section_id, value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'filter', _('Filter'), _('Tcpdump filter like protocol, port etc.'));
|
||||||
|
o.modalonly = false;
|
||||||
|
o.datatype = 'and(minlength(1),maxlength(1024))';
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'duration', _('Duration'), _('Duration of packet capturing in seconds.'));
|
||||||
|
o.modalonly = false;
|
||||||
|
o.datatype = 'range(1,4294967296)';
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'packets', _('Packets'), _('Number of packets to be captured.'));
|
||||||
|
o.modalonly = false;
|
||||||
|
o.datatype = 'range(1,4294967296)';
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'domains', _('Resolve domains'), _("Convert host addresses to names."));
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'verbose', _('Verbose output'), _("Print the link-level header on each dump line."));
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'file', _('Save to file'), _("Save capture to pcap file."));
|
||||||
|
|
||||||
|
o = s.option(form.Button, 'start_tcpdump', _('Start tcpdump'), _(''));
|
||||||
|
o.inputstyle = 'apply';
|
||||||
|
o.onclick = ui.createHandlerFn(this, function(section_id, ev) {
|
||||||
|
var downloadBtn = document.querySelector('[data-action="download"]');
|
||||||
|
if (!downloadBtn)
|
||||||
|
return;
|
||||||
|
fs.remove("/tmp/capture.pcap").then(function(replay) {
|
||||||
|
downloadBtn.disabled = true;;
|
||||||
|
}.bind(this)).catch(function(error) {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
var iface = document.querySelectorAll('[id$="interface"]')[1].value,
|
||||||
|
filter = document.querySelectorAll('[id$="filter"]')[2].value,
|
||||||
|
packets = document.querySelectorAll('[id$="packets"]')[2].value,
|
||||||
|
duration = document.querySelectorAll('[id$="duration"]')[2].value,
|
||||||
|
verbose = document.querySelectorAll('[data-widget-id$="verbose"]')[0].checked,
|
||||||
|
domains = document.querySelectorAll('[data-widget-id$="domains"]')[0].checked,
|
||||||
|
file = document.querySelectorAll('[data-widget-id$="file"]')[0].checked
|
||||||
|
|
||||||
|
var args = {
|
||||||
|
"interface": iface,
|
||||||
|
"filter": filter,
|
||||||
|
"packets": packets,
|
||||||
|
"duration": duration,
|
||||||
|
"verbose": verbose,
|
||||||
|
"domains": domains,
|
||||||
|
"file": file
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.exec_direct('/usr/libexec/packet_capture_start', [JSON.stringify(args)]).then(function(replay) {
|
||||||
|
var error_position = replay.search("error:");
|
||||||
|
if (error_position != -1){
|
||||||
|
ui.showModal(_(replay.substring(error_position + 6, replay.length)), [
|
||||||
|
E('div', { 'class': 'right' }, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'cbi-button cbi-button-negative important',
|
||||||
|
'click': function(ev) {
|
||||||
|
ui.hideModal();
|
||||||
|
}
|
||||||
|
}, _('Close')),
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rpc.list.apply(rpc).then(function(res) {
|
||||||
|
for (var k in res) {
|
||||||
|
if (res[k] == "tcpdump" )
|
||||||
|
subscribeTcpdump()
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this)).catch(function(error) {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
o = s.option(form.Button, 'stop_tcpdump', _('Stop tcpdump'), _(''));
|
||||||
|
o.inputstyle = 'apply';
|
||||||
|
o.onclick = ui.createHandlerFn(this, function(section_id, ev) {
|
||||||
|
if (!eventSource)
|
||||||
|
return;
|
||||||
|
return fs.exec("/usr/libexec/packet_capture_stop").then(function(replay) {
|
||||||
|
eventSource.close();
|
||||||
|
}.bind(this)).catch(function(error) {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
o = s.option(form.Button, 'download_file', _('Download capture file'));
|
||||||
|
o.inputstyle = 'action important';
|
||||||
|
o.inputtitle = _('Download');
|
||||||
|
o.data_action = 'download'
|
||||||
|
o.onclick = this.handleDownload;
|
||||||
|
|
||||||
|
L.Poll.add(L.bind(updateButtons, m),1);
|
||||||
|
captureFilePoll = L.bind(updatePollCheckCaptureFileExists, m);
|
||||||
|
L.Poll.add(captureFilePoll,1);
|
||||||
|
|
||||||
|
return m.render();
|
||||||
|
},
|
||||||
|
});
|
1
luci-app-packet-capture/root/etc/config/packet_capture
Normal file
1
luci-app-packet-capture/root/etc/config/packet_capture
Normal file
|
@ -0,0 +1 @@
|
||||||
|
config tcpdump
|
64
luci-app-packet-capture/root/usr/libexec/packet_capture
Executable file
64
luci-app-packet-capture/root/usr/libexec/packet_capture
Executable file
|
@ -0,0 +1,64 @@
|
||||||
|
#!/usr/bin/env lua
|
||||||
|
|
||||||
|
local ubus = require "ubus"
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
|
||||||
|
local conn = ubus.connect()
|
||||||
|
if not conn then
|
||||||
|
error("Failed to connect to ubus")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local args = "-n"
|
||||||
|
local duration = ""
|
||||||
|
|
||||||
|
if arg[1] ~= nil then
|
||||||
|
args = arg[1]
|
||||||
|
if arg[2] ~= "" then
|
||||||
|
duration = arg[2]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local filter = fs.stat("/tmp/tcpdump_filter")
|
||||||
|
if filter then
|
||||||
|
args = args .. " -F /tmp/tcpdump_filter"
|
||||||
|
end
|
||||||
|
|
||||||
|
local ubus_objects = {
|
||||||
|
tcpdump = {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn:add( ubus_objects )
|
||||||
|
|
||||||
|
os.execute("sleep 1")
|
||||||
|
|
||||||
|
local command = "tcpdump -l " .. args .. " 2>&1"
|
||||||
|
|
||||||
|
if duration ~= "" then
|
||||||
|
command = "timeout " .. duration .. " " .. command
|
||||||
|
end
|
||||||
|
|
||||||
|
local pipe = io.popen(command)
|
||||||
|
|
||||||
|
for line in pipe:lines() do
|
||||||
|
local params = {
|
||||||
|
data = line
|
||||||
|
}
|
||||||
|
conn:notify(ubus_objects.tcpdump.__ubusobj, "tcpdump.data", params)
|
||||||
|
end
|
||||||
|
|
||||||
|
local pcap = fs.stat("/tmp/capture.pcap0")
|
||||||
|
if pcap then
|
||||||
|
fs.move("/tmp/capture.pcap0","/tmp/capture.pcap")
|
||||||
|
fs.remove("/tmp/capture.pcap1")
|
||||||
|
end
|
||||||
|
|
||||||
|
if filter then
|
||||||
|
fs.remove("/tmp/tcpdump_filter")
|
||||||
|
end
|
||||||
|
|
||||||
|
conn:close()
|
||||||
|
pipe:close()
|
||||||
|
|
||||||
|
fs.remove("/var/run/packet_capture.pid")
|
69
luci-app-packet-capture/root/usr/libexec/packet_capture_start
Executable file
69
luci-app-packet-capture/root/usr/libexec/packet_capture_start
Executable file
|
@ -0,0 +1,69 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. /usr/share/libubox/jshn.sh
|
||||||
|
|
||||||
|
PIDFILE="/var/run/packet_capture.pid"
|
||||||
|
|
||||||
|
if [ -f "$PIDFILE"];then
|
||||||
|
echo "error: Packet capture is running"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_load "$1"
|
||||||
|
json_get_var interface interface
|
||||||
|
json_get_var filter filter
|
||||||
|
json_get_var duration duration
|
||||||
|
json_get_var packets packets
|
||||||
|
json_get_var verbose verbose
|
||||||
|
json_get_var domains domains
|
||||||
|
json_get_var file file
|
||||||
|
|
||||||
|
args="-n"
|
||||||
|
|
||||||
|
if [ "$domains" == "1" ];then
|
||||||
|
args=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$interface" ];then
|
||||||
|
ip a show "$interface" > /dev/null 2>&1
|
||||||
|
if [ "$?" == "1" ]; then
|
||||||
|
echo "error: Incorrect format of an interface"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
args="$args -i $interface"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$packets" ];then
|
||||||
|
echo "$packets" | egrep '^[0-9]*$'
|
||||||
|
if [ "$?" -eq 0 ];then
|
||||||
|
args="$args -c $packets"
|
||||||
|
else
|
||||||
|
echo "error: Incorrect packets argument"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$verbose" == "1" ];then
|
||||||
|
args="$args -e"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$file" == "1" ];then
|
||||||
|
mem=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
|
||||||
|
args="$args -W 2 -C $((mem/(1024 * 10))) -w /tmp/capture.pcap -z /usr/libexec/packet_capture_stop"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$filter" ];then
|
||||||
|
tcpdump -i lo -d "$filter" >/dev/null 2>/dev/null
|
||||||
|
if [ $? -eq 1 ];then
|
||||||
|
echo "error: Incorrect filter argument"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "$filter" > /tmp/tcpdump_filter
|
||||||
|
fi
|
||||||
|
|
||||||
|
(/usr/libexec/packet_capture "$args" "$duration")&
|
||||||
|
|
||||||
|
echo $! > /var/run/packet_capture.pid
|
||||||
|
|
||||||
|
exit 0
|
9
luci-app-packet-capture/root/usr/libexec/packet_capture_stop
Executable file
9
luci-app-packet-capture/root/usr/libexec/packet_capture_stop
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
pid=$(cat /var/run/packet_capture.pid)
|
||||||
|
if [ -n "$pid" ] && grep -sq packet_capture "/proc/$pid/cmdline"; then
|
||||||
|
ppid=$(pgrep -P $pid)
|
||||||
|
kill -TERM $ppid
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"admin/services/packet_capture": {
|
||||||
|
"title": "Packet Capture",
|
||||||
|
"order": 90,
|
||||||
|
"action": {
|
||||||
|
"type": "view",
|
||||||
|
"path": "packet_capture/tcpdump"
|
||||||
|
},
|
||||||
|
"depends" : {
|
||||||
|
"acl": [ "luci-app-packet-capture" ],
|
||||||
|
"uci": { "packet_capture": true },
|
||||||
|
"fs": { "/usr/libexec/packet_capture": "executable",
|
||||||
|
"/usr/libexec/packet_capture_start": "executable",
|
||||||
|
"/usr/libexec/packet_capture_stop": "executable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"luci-app-packet-capture": {
|
||||||
|
"description": "Grant access to tcpdump ubus object",
|
||||||
|
"read": {
|
||||||
|
"cgi-io": [ "download", "exec" ],
|
||||||
|
"ubus": {
|
||||||
|
"tcpdump": [ "*" ],
|
||||||
|
"luci": [ "getProcessList" ]
|
||||||
|
},
|
||||||
|
"uci": [ "packet_capture", "system" ],
|
||||||
|
"file": {
|
||||||
|
"/tmp/capture.pcap": [ "read" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"write": {
|
||||||
|
"uci": [ "packet_capture" ],
|
||||||
|
"file": {
|
||||||
|
"/usr/libexec/packet_capture_start": [ "exec" ],
|
||||||
|
"/usr/libexec/packet_capture_stop": [ "exec" ],
|
||||||
|
"/usr/libexec/packet_capture": [ "exec" ],
|
||||||
|
"/tmp/capture.pcap": [ "write" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -78,9 +78,9 @@ MY_DEPENDS := \
|
||||||
!TARGET_mvebu:kmod-usb-net-huawei-cdc-ncm !TARGET_mvebu:kmod-usb-net-rndis !TARGET_mvebu:kmod-usb-net-cdc-ether !TARGET_mvebu:kmod-usb-net-ipheth !TARGET_mvebu:usbmuxd \
|
!TARGET_mvebu:kmod-usb-net-huawei-cdc-ncm !TARGET_mvebu:kmod-usb-net-rndis !TARGET_mvebu:kmod-usb-net-cdc-ether !TARGET_mvebu:kmod-usb-net-ipheth !TARGET_mvebu:usbmuxd \
|
||||||
kmod-rt2800-usb kmod-rtl8xxxu kmod-rtl8192cu kmod-net-rtl8192su \
|
kmod-rt2800-usb kmod-rtl8xxxu kmod-rtl8192cu kmod-net-rtl8192su \
|
||||||
!TARGET_mvebu:luci-proto-qmi wpad-basic kmod-mt7601u kmod-rtl8187 \
|
!TARGET_mvebu:luci-proto-qmi wpad-basic kmod-mt7601u kmod-rtl8187 \
|
||||||
luci-app-mlvpn mlvpn 464xlat !TARGET_mvebu:kmod-usb-net-smsc75xx kmod-zram kmod-swconfig swconfig kmod-ipt-nat kmod-ipt-nat6 luci-app-https-dns-proxy kmod-tcp-nanqinlang kmod-tcp-bbr2 iptables-mod-ipopt igmpproxy ss iptraf-ng \
|
luci-app-mlvpn mlvpn 464xlat !TARGET_mvebu:kmod-usb-net-smsc75xx kmod-zram kmod-swconfig swconfig kmod-ipt-nat kmod-ipt-nat6 luci-app-https-dns-proxy kmod-tcp-nanqinlang (TARGET_x86_64||TARGET_arm64):kmod-tcp-bbr2 iptables-mod-ipopt igmpproxy ss iptraf-ng \
|
||||||
luci-app-acl block-mount blockd fstools luci-app-shutdown libwebp luci-proto-gre tcptraceroute luci-proto-mbim kmod-rtl8xxxu kmod-ath9k-htc luci-app-ttyd luci-mod-dashboard (TARGET_x86||TARGET_x86_64):rtl8192eu-firmware kmod-usb2 libustream-wolfssl (TARGET_x86||TARGET_x86_64):kmod-ixgbevf \
|
luci-app-acl block-mount blockd fstools luci-app-shutdown libwebp luci-proto-gre tcptraceroute luci-proto-mbim kmod-rtl8xxxu kmod-ath9k-htc luci-app-ttyd luci-mod-dashboard (TARGET_x86||TARGET_x86_64):rtl8192eu-firmware kmod-usb2 libustream-wolfssl (TARGET_x86||TARGET_x86_64):kmod-ixgbevf \
|
||||||
hwinfo (TARGET_x86||TARGET_x86_64):dmidecode
|
hwinfo (TARGET_x86||TARGET_x86_64):dmidecode luci-app-packet-capture
|
||||||
# luci-theme-bootstrap luci-theme-openwrt-2020 luci-theme-openwrt luci-app-status
|
# luci-theme-bootstrap luci-theme-openwrt-2020 luci-theme-openwrt luci-app-status
|
||||||
# luci-proto-bonding luci-app-statistics luci-proto-gre
|
# luci-proto-bonding luci-app-statistics luci-proto-gre
|
||||||
# softethervpn5-client softethervpn5-server luci-app-nginx-ha
|
# softethervpn5-client softethervpn5-server luci-app-nginx-ha
|
||||||
|
|
Loading…
Reference in a new issue