Merge branch 'develop' into ipq60xx
169
bpftools/Makefile
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
#
|
||||||
|
# Copyright (C) 2020 Tony Ambardar <itugrok@yahoo.com>
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the GNU General Public License v2.
|
||||||
|
# See /LICENSE for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=bpftools
|
||||||
|
PKG_VERSION:=5.11.16
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
PKG_SOURCE:=linux-$(PKG_VERSION).tar.xz
|
||||||
|
PKG_SOURCE_URL:=@KERNEL/linux/kernel/v5.x
|
||||||
|
PKG_HASH:=21163681d130cbce5a6be39019e2c69e44f284855ddd70b1a3bd039249540f43
|
||||||
|
|
||||||
|
PKG_MAINTAINER:=Tony Ambardar <itugrok@yahoo.com>
|
||||||
|
|
||||||
|
PKG_USE_MIPS16:=0
|
||||||
|
PKG_BUILD_PARALLEL:=1
|
||||||
|
PKG_INSTALL:=1
|
||||||
|
|
||||||
|
LINUX_VERSION:=$(PKG_VERSION)
|
||||||
|
LINUX_TLD:=linux-$(LINUX_VERSION)
|
||||||
|
|
||||||
|
BPF_FILES:= \
|
||||||
|
kernel/bpf scripts tools/Makefile tools/bpf tools/perf/perf-sys.h \
|
||||||
|
tools/arch tools/build tools/include tools/lib tools/scripts
|
||||||
|
TAR_OPTIONS+= \
|
||||||
|
--transform="s;$(LINUX_TLD)/;$(PKG_NAME)-$(PKG_VERSION)/;" \
|
||||||
|
$(addprefix $(LINUX_TLD)/,$(BPF_FILES))
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
include $(INCLUDE_DIR)/nls.mk
|
||||||
|
|
||||||
|
define Package/bpftool/Default
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
TITLE:=bpftool - eBPF subsystem utility
|
||||||
|
LICENSE:=GPL-2.0-only OR BSD-2-Clause
|
||||||
|
URL:=http://www.kernel.org
|
||||||
|
DEPENDS:=+libelf
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/bpftool-minimal
|
||||||
|
$(call Package/bpftool/Default)
|
||||||
|
TITLE+= (Minimal)
|
||||||
|
VARIANT:=minimal
|
||||||
|
DEFAULT_VARIANT:=1
|
||||||
|
PROVIDES:=bpftool
|
||||||
|
ALTERNATIVES:=200:/usr/sbin/bpftool:/usr/libexec/bpftool-minimal
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/bpftool-full
|
||||||
|
$(call Package/bpftool/Default)
|
||||||
|
TITLE+= (Full)
|
||||||
|
VARIANT:=full
|
||||||
|
PROVIDES:=bpftool
|
||||||
|
ALTERNATIVES:=300:/usr/sbin/bpftool:/usr/libexec/bpftool-full
|
||||||
|
DEPENDS+= +libbfd +libopcodes
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/bpftool-minimal/description
|
||||||
|
A tool for inspection and simple manipulation of eBPF programs and maps.
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/bpftool-full/description
|
||||||
|
A tool for inspection and simple manipulation of eBPF programs and maps.
|
||||||
|
This full version uses libbfd and libopcodes to support disassembly of
|
||||||
|
eBPF programs and jited code.
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/libbpf
|
||||||
|
SECTION:=libs
|
||||||
|
CATEGORY:=Libraries
|
||||||
|
TITLE:=libbpf - eBPF helper library
|
||||||
|
VARIANT:=lib
|
||||||
|
LICENSE:=LGPL-2.1 OR BSD-2-Clause
|
||||||
|
ABI_VERSION:=0
|
||||||
|
URL:=http://www.kernel.org
|
||||||
|
DEPENDS:=+libelf
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/libbpf/description
|
||||||
|
libbpf is a library for loading eBPF programs and reading and manipulating eBPF objects from user-space.
|
||||||
|
endef
|
||||||
|
|
||||||
|
|
||||||
|
# LTO not compatible with DSO using PIC
|
||||||
|
ifneq ($(BUILD_VARIANT),lib)
|
||||||
|
TARGET_CFLAGS += -ffunction-sections -fdata-sections -flto
|
||||||
|
TARGET_LDFLAGS += -Wl,--gc-sections
|
||||||
|
endif
|
||||||
|
|
||||||
|
MAKE_VARS = \
|
||||||
|
EXTRA_CFLAGS="$(TARGET_CFLAGS) $(TARGET_CPPFLAGS)" \
|
||||||
|
LDFLAGS="$(TARGET_LDFLAGS)"
|
||||||
|
|
||||||
|
MAKE_FLAGS += \
|
||||||
|
BPFTOOL_VERSION="$(LINUX_VERSION)" \
|
||||||
|
FEATURES_DUMP="$(PKG_BUILD_DIR)/FEATURE-DUMP.openwrt" \
|
||||||
|
OUTPUT="$(PKG_BUILD_DIR)/" \
|
||||||
|
prefix="/usr" \
|
||||||
|
$(if $(findstring c,$(OPENWRT_VERBOSE)),V=1,V='')
|
||||||
|
|
||||||
|
ifeq ($(BUILD_VARIANT),full)
|
||||||
|
HAVE_LIBBFD:=1
|
||||||
|
HAVE_LIBCAP:=0
|
||||||
|
HAVE_CLANG:=0
|
||||||
|
MAKE_PATH:=tools/bpf/bpftool
|
||||||
|
else ifeq ($(BUILD_VARIANT),minimal)
|
||||||
|
HAVE_LIBBFD:=0
|
||||||
|
HAVE_LIBCAP:=0
|
||||||
|
HAVE_CLANG:=0
|
||||||
|
MAKE_PATH:=tools/bpf/bpftool
|
||||||
|
else ifeq ($(BUILD_VARIANT),lib)
|
||||||
|
HAVE_LIBBFD:=0
|
||||||
|
HAVE_LIBCAP:=0
|
||||||
|
HAVE_CLANG:=0
|
||||||
|
MAKE_PATH:=tools/lib/bpf
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Perform a "throw-away" make to create a FEATURE-DUMP.* file to edit later.
|
||||||
|
# The "//" in the make target is actually needed, very unPOSIXly.
|
||||||
|
define Build/Configure
|
||||||
|
+$(MAKE_VARS) $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/tools/bpf/bpftool \
|
||||||
|
$(MAKE_FLAGS) FEATURES_DUMP= $(PKG_BUILD_DIR)//libbpf/libbpf.a
|
||||||
|
(cd $(PKG_BUILD_DIR); cat FEATURE-DUMP.bpftool libbpf/FEATURE-DUMP.libbpf \
|
||||||
|
| sort | uniq > FEATURE-DUMP.openwrt)
|
||||||
|
$(SED) 's/feature-libbfd=1/feature-libbfd=$(HAVE_LIBBFD)/' \
|
||||||
|
-e 's/feature-libcap=1/feature-libcap=$(HAVE_LIBCAP)/' \
|
||||||
|
-e 's/feature-clang-bpf-co-re=1/feature-clang-bpf-co-re=$(HAVE_CLANG)/' \
|
||||||
|
$(PKG_BUILD_DIR)/FEATURE-DUMP.openwrt
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/InstallDev/libbpf
|
||||||
|
$(INSTALL_DIR) $(1)/usr/include/bpf
|
||||||
|
$(CP) $(PKG_INSTALL_DIR)/usr/include/bpf/*.h $(1)/usr/include/bpf/
|
||||||
|
$(INSTALL_DIR) $(1)/usr/lib
|
||||||
|
$(CP) $(PKG_INSTALL_DIR)/usr/lib$(LIB_SUFFIX)/libbpf.{a,so*} \
|
||||||
|
$(1)/usr/lib/
|
||||||
|
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
|
||||||
|
$(CP) $(PKG_INSTALL_DIR)/usr/lib$(LIB_SUFFIX)/pkgconfig/libbpf.pc \
|
||||||
|
$(1)/usr/lib/pkgconfig/
|
||||||
|
$(SED) 's,/usr/include,$$$${prefix}/include,g' \
|
||||||
|
$(1)/usr/lib/pkgconfig/libbpf.pc
|
||||||
|
$(SED) 's,/usr/lib,$$$${exec_prefix}/lib,g' \
|
||||||
|
$(1)/usr/lib/pkgconfig/libbpf.pc
|
||||||
|
endef
|
||||||
|
|
||||||
|
ifeq ($(BUILD_VARIANT),lib)
|
||||||
|
Build/InstallDev=$(Build/InstallDev/libbpf)
|
||||||
|
endif
|
||||||
|
|
||||||
|
define Package/bpftool-$(BUILD_VARIANT)/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/libexec
|
||||||
|
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/bpftool \
|
||||||
|
$(1)/usr/libexec/bpftool-$(BUILD_VARIANT)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/libbpf/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/lib
|
||||||
|
$(CP) $(PKG_INSTALL_DIR)/usr/lib$(LIB_SUFFIX)/libbpf.so.* $(1)/usr/lib/
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,libbpf))
|
||||||
|
$(eval $(call BuildPackage,bpftool-full))
|
||||||
|
$(eval $(call BuildPackage,bpftool-minimal))
|
|
@ -0,0 +1,51 @@
|
||||||
|
From afe3f4c765b17ced23811fe652c7f7adf7a0c0cf Mon Sep 17 00:00:00 2001
|
||||||
|
From: Tony Ambardar <Tony.Ambardar@gmail.com>
|
||||||
|
Date: Mon, 14 Sep 2020 23:05:26 -0700
|
||||||
|
Subject: [PATCH] tools/arch/powerpc: fix EDEADLOCK redefinition errors in
|
||||||
|
errno.h
|
||||||
|
|
||||||
|
A few archs like powerpc have different errno.h values for macros
|
||||||
|
EDEADLOCK and EDEADLK. In code including both libc and linux versions of
|
||||||
|
errno.h, this can result in multiple definitions of EDEADLOCK in the
|
||||||
|
include chain. Definitions to the same value (e.g. seen with mips) do
|
||||||
|
not raise warnings, but on powerpc there are redefinitions changing the
|
||||||
|
value, which raise warnings and errors (with "-Werror").
|
||||||
|
|
||||||
|
Guard against these redefinitions to avoid build errors like the following,
|
||||||
|
first seen cross-compiling libbpf v5.8.9 for powerpc using GCC 8.4.0 with
|
||||||
|
musl 1.1.24:
|
||||||
|
|
||||||
|
In file included from ../../arch/powerpc/include/uapi/asm/errno.h:5,
|
||||||
|
from ../../include/linux/err.h:8,
|
||||||
|
from libbpf.c:29:
|
||||||
|
../../include/uapi/asm-generic/errno.h:40: error: "EDEADLOCK" redefined [-Werror]
|
||||||
|
#define EDEADLOCK EDEADLK
|
||||||
|
|
||||||
|
In file included from toolchain-powerpc_8540_gcc-8.4.0_musl/include/errno.h:10,
|
||||||
|
from libbpf.c:26:
|
||||||
|
toolchain-powerpc_8540_gcc-8.4.0_musl/include/bits/errno.h:58: note: this is the location of the previous definition
|
||||||
|
#define EDEADLOCK 58
|
||||||
|
|
||||||
|
cc1: all warnings being treated as errors
|
||||||
|
make[5]: *** [target-powerpc_8540_musl/bpftools-5.8.9/tools/build/Makefile.build:97: /home/kodidev/openwrt-project/build_dir/target-powerpc_8540_musl/bpftools-minimal/bpftools-5.8.9//libbpf/staticobjs/libbpf.o] Error 1
|
||||||
|
|
||||||
|
Fixes: 95f28190aa01 ("tools include arch: Grab a copy of errno.h for arch's
|
||||||
|
supported by perf")
|
||||||
|
Fixes: c3617f72036c ("UAPI: (Scripted) Disintegrate arch/powerpc/include/asm")
|
||||||
|
|
||||||
|
Reported-by: Rosen Penev <rosenp@gmail.com>
|
||||||
|
Signed-off-by: Tony Ambardar <Tony.Ambardar@gmail.com>
|
||||||
|
---
|
||||||
|
tools/arch/powerpc/include/uapi/asm/errno.h | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
--- a/tools/arch/powerpc/include/uapi/asm/errno.h
|
||||||
|
+++ b/tools/arch/powerpc/include/uapi/asm/errno.h
|
||||||
|
@@ -2,6 +2,7 @@
|
||||||
|
#ifndef _ASM_POWERPC_ERRNO_H
|
||||||
|
#define _ASM_POWERPC_ERRNO_H
|
||||||
|
|
||||||
|
+#undef EDEADLOCK
|
||||||
|
#include <asm-generic/errno.h>
|
||||||
|
|
||||||
|
#undef EDEADLOCK
|
61
dnsforwarder/Makefile
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#
|
||||||
|
# Copyright (C) 2021 ImmortalWrt
|
||||||
|
# <https://immortalwrt.org>
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the GNU General Public License v3.
|
||||||
|
# See /LICENSE for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=dnsforwarder
|
||||||
|
PKG_VERSION:=6.1.15
|
||||||
|
PKG_RELEASE:=11
|
||||||
|
|
||||||
|
PKG_SOURCE_PROTO:=git
|
||||||
|
PKG_SOURCE_URL:=https://github.com/1715173329/dnsforwarder.git
|
||||||
|
PKG_SOURCE_DATE:=2018-06-26
|
||||||
|
PKG_SOURCE_VERSION:=587e61ae4d75dc976f538088b715a3c8ee26c144
|
||||||
|
PKG_MIRROR_HASH:=7c141040ae384d254d90b3c3ee502d87330c9fdcd201ff29a669336a27b176d4
|
||||||
|
|
||||||
|
PKG_LICENSE:=GPL-3.0
|
||||||
|
PKG_LICENSE_FILE:=LICENSE
|
||||||
|
PKG_MAINTAINER:=Dennis <openwrt@tossp.com>
|
||||||
|
|
||||||
|
PKG_FIXUP:=autoreconf
|
||||||
|
PKG_BUILD_PARALLEL:=1
|
||||||
|
PKG_INSTALL:=1
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/dnsforwarder
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
TITLE:=A simple DNS forwarder
|
||||||
|
URL:=https://github.com/holmium/dnsforwarder
|
||||||
|
DEPENDS:=+coreutils +coreutils-base64 +dnsmasq-full +libpthread +wget-ssl
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/dnsforwarder/description
|
||||||
|
Forwarding queries to customized domains (and their subdomains) to specified servers
|
||||||
|
over a specified protocol (UDP or TCP). non-standard ports are supported.
|
||||||
|
endef
|
||||||
|
|
||||||
|
CONFIGURE_ARGS+= --enable-downloader=wget
|
||||||
|
|
||||||
|
define Package/dnsforwarder/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/bin
|
||||||
|
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/dnsforwarder $(1)/usr/bin/dnsforwarder
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/etc/config
|
||||||
|
$(INSTALL_CONF) files/etc/config/dnsforwarder $(1)/etc/config/dnsforwarder
|
||||||
|
$(INSTALL_DIR) $(1)/etc/init.d
|
||||||
|
$(INSTALL_BIN) files/etc/init.d/dnsforwarder $(1)/etc/init.d/dnsforwarder
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/etc/dnsforwarder
|
||||||
|
$(INSTALL_CONF) files/etc/dnsforwarder/gfw.txt $(1)/etc/dnsforwarder/gfw.txt
|
||||||
|
$(INSTALL_DIR) $(1)/usr/share/dnsforwarder
|
||||||
|
$(INSTALL_BIN) files/usr/share/dnsforwarder/gfwlist.sh $(1)/usr/share/dnsforwarder/gfwlist.sh
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,dnsforwarder))
|
79
dnsforwarder/files/etc/config/dnsforwarder
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
|
||||||
|
config arguments
|
||||||
|
option enabled '0'
|
||||||
|
option addr '127.0.0.1:5053'
|
||||||
|
|
||||||
|
config config
|
||||||
|
option cache 'true'
|
||||||
|
option cache_size '102400'
|
||||||
|
option cache_ignore 'false'
|
||||||
|
option gfw 'true'
|
||||||
|
list block_ip '74.125.127.102'
|
||||||
|
list block_ip '74.125.155.102'
|
||||||
|
list block_ip '74.125.39.102'
|
||||||
|
list block_ip '74.125.39.113'
|
||||||
|
list block_ip '209.85.229.138'
|
||||||
|
list block_ip '128.121.126.139'
|
||||||
|
list block_ip '159.106.121.75'
|
||||||
|
list block_ip '169.132.13.103'
|
||||||
|
list block_ip '192.67.198.6'
|
||||||
|
list block_ip '202.106.1.2'
|
||||||
|
list block_ip '202.181.7.85'
|
||||||
|
list block_ip '203.161.230.171'
|
||||||
|
list block_ip '203.98.7.65'
|
||||||
|
list block_ip '207.12.88.98'
|
||||||
|
list block_ip '208.56.31.43'
|
||||||
|
list block_ip '209.145.54.50'
|
||||||
|
list block_ip '209.220.30.174'
|
||||||
|
list block_ip '209.36.73.33'
|
||||||
|
list block_ip '211.94.66.147'
|
||||||
|
list block_ip '213.169.251.35'
|
||||||
|
list block_ip '216.221.188.182'
|
||||||
|
list block_ip '216.234.179.13'
|
||||||
|
list block_ip '243.185.187.39'
|
||||||
|
list block_ip '37.61.54.158'
|
||||||
|
list block_ip '4.36.66.178'
|
||||||
|
list block_ip '46.82.174.68'
|
||||||
|
list block_ip '59.24.3.173'
|
||||||
|
list block_ip '64.33.88.161'
|
||||||
|
list block_ip '64.33.99.47'
|
||||||
|
list block_ip '64.66.163.251'
|
||||||
|
list block_ip '65.104.202.252'
|
||||||
|
list block_ip '65.160.219.113'
|
||||||
|
list block_ip '66.45.252.237'
|
||||||
|
list block_ip '69.55.52.253'
|
||||||
|
list block_ip '72.14.205.104'
|
||||||
|
list block_ip '72.14.205.99'
|
||||||
|
list block_ip '78.16.49.15'
|
||||||
|
list block_ip '8.7.198.45'
|
||||||
|
list block_ip '93.46.8.89'
|
||||||
|
list block_ip '37.61.54.158'
|
||||||
|
list block_ip '243.185.187.39'
|
||||||
|
list block_ip '190.93.247.4'
|
||||||
|
list block_ip '190.93.246.4'
|
||||||
|
list block_ip '190.93.245.4'
|
||||||
|
list block_ip '190.93.244.4'
|
||||||
|
list block_ip '65.49.2.178'
|
||||||
|
list block_ip '189.163.17.5'
|
||||||
|
list block_ip '23.89.5.60'
|
||||||
|
list block_ip '49.2.123.56'
|
||||||
|
list block_ip '54.76.135.1'
|
||||||
|
list block_ip '77.4.7.92'
|
||||||
|
list block_ip '118.5.49.6'
|
||||||
|
list block_ip '159.24.3.173'
|
||||||
|
list block_ip '188.5.4.96'
|
||||||
|
list block_ip '197.4.4.12'
|
||||||
|
list block_ip '220.250.64.24'
|
||||||
|
list block_ip '243.185.187.30'
|
||||||
|
list block_ip '249.129.46.48'
|
||||||
|
list block_ip '253.157.14.165'
|
||||||
|
option block_ipv6 'false'
|
||||||
|
list cache_control 'tossp.com $orig'
|
||||||
|
list cache_control '* fixed 3600'
|
||||||
|
option log 'false'
|
||||||
|
list udp_group '9.9.9.9,119.29.29.29,223.5.5.5,114.114.114.114 * on'
|
||||||
|
option block_negative_resp 'true'
|
||||||
|
list udp_local '0.0.0.0:5053'
|
||||||
|
list udp_local '[::0]:5053'
|
||||||
|
option domain_statistic 'false'
|
||||||
|
|
5605
dnsforwarder/files/etc/dnsforwarder/gfw.txt
Normal file
260
dnsforwarder/files/etc/init.d/dnsforwarder
Executable file
|
@ -0,0 +1,260 @@
|
||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
|
||||||
|
START=60
|
||||||
|
|
||||||
|
EXTRA_COMMANDS="makeconfig makegfwlist health"
|
||||||
|
|
||||||
|
CRON_FILE=/etc/crontabs/root
|
||||||
|
PID_PATH=/var/run/dnsforwarder
|
||||||
|
PID_FILE=${PID_PATH}/dns.pid
|
||||||
|
DNSFORWARDER_CONF=/tmp/dnsforwarder.conf
|
||||||
|
|
||||||
|
add_cron()
|
||||||
|
{
|
||||||
|
sed -i '/dnsforwarder/d' $CRON_FILE
|
||||||
|
echo '*/5 * * * * /etc/init.d/dnsforwarder health' >> $CRON_FILE
|
||||||
|
echo '0 1 * * 0 /etc/init.d/dnsforwarder makegfwlist' >> $CRON_FILE
|
||||||
|
crontab $CRON_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
del_cron()
|
||||||
|
{
|
||||||
|
sed -i '/dnsforwarder/d' $CRON_FILE
|
||||||
|
/etc/init.d/cron restart
|
||||||
|
}
|
||||||
|
|
||||||
|
fixturboacc(){
|
||||||
|
dns=$(uci get turboacc.config.dns_caching 2>/dev/null)
|
||||||
|
if [ $dns -eq 1 ]; then
|
||||||
|
uci set turboacc.config.dns_caching=0 && uci commit turboacc
|
||||||
|
/etc/init.d/turboacc restart
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
makelist() {
|
||||||
|
[ -z "$2" ] && return
|
||||||
|
local i
|
||||||
|
local t="$1"; shift
|
||||||
|
for i in "$@"
|
||||||
|
do
|
||||||
|
echo "$t $i"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
health(){
|
||||||
|
rm /var/log/dnsforwarder.log.* 2>/dev/null
|
||||||
|
local pid=$(cat ${PID_FILE} 2>/dev/null)
|
||||||
|
if [ -n "${pid}" -a -d /proc/$pid ]; then
|
||||||
|
echo "[health] process exists ${pid}"
|
||||||
|
else
|
||||||
|
echo "[health] Dnsforwarder is not running ${pid}"
|
||||||
|
start
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
makegfwlist(){
|
||||||
|
local GFW_FILE='/etc/dnsforwarder/gfw.txt'
|
||||||
|
local GFW_TMP_FILE='/tmp/dnsforwarder-gfw.old'
|
||||||
|
local TSTIME=`date '+%Y-%m-%d %H:%M:%S'`
|
||||||
|
touch ${GFW_TMP_FILE}
|
||||||
|
cat /etc/config/gfw.list 2>/dev/null > /tmp/edf.ts
|
||||||
|
cat /etc/dnsmasq.ssr/gfw_base.conf 2>/dev/null | awk -F '/' '{print $2}' | sed 's/^.//g' >> /tmp/edf.ts
|
||||||
|
cat /etc/dnsmasq.ssr/gfw_list.conf 2>/dev/null | awk -F '/' '{print $2}' | sed 's/^.//g' >> /tmp/edf.ts
|
||||||
|
sort /tmp/edf.ts | uniq > /tmp/edf.ts
|
||||||
|
/usr/share/dnsforwarder/gfwlist.sh -i -l -o /tmp/dnsforwarder-gfw.tmp --extra-domain-file /tmp/edf.ts
|
||||||
|
if [ $? != 0 ]; then
|
||||||
|
echo 'Failed to fetch gfwlist'
|
||||||
|
logger -t Failed to fetch gfwlist
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
local gfw=$(cat /tmp/dnsforwarder-gfw.tmp)
|
||||||
|
echo "# GenerationAt TS_BUILD_TIME" > ${GFW_TMP_FILE}.new
|
||||||
|
echo "protocol tcp" >> ${GFW_TMP_FILE}.new
|
||||||
|
echo "server 8.8.8.8,8.8.4.4,1.1.1.1,1.0.0.1,208.67.222.222,208.67.220.220,209.244.0.3,209.244.0.4,8.26.56.26,8.20.247.20,156.154.70.1,156.154.71.1,199.85.126.10" >> ${GFW_TMP_FILE}.new
|
||||||
|
echo -e 'proxy no\n\n\n' >> ${GFW_TMP_FILE}.new
|
||||||
|
echo "${gfw}" >> ${GFW_TMP_FILE}.new
|
||||||
|
if [ "`cat ${GFW_TMP_FILE}.new | md5sum`" == "`cat ${GFW_TMP_FILE} | md5sum`" ]; then
|
||||||
|
printf "[\e[32m%s\e[0m]\n" "hold"
|
||||||
|
else
|
||||||
|
cp ${GFW_TMP_FILE}.new ${GFW_TMP_FILE}
|
||||||
|
cp ${GFW_TMP_FILE} ${GFW_FILE}
|
||||||
|
sed -i "s/TS_BUILD_TIME/${TSTIME}/g" ${GFW_FILE}
|
||||||
|
printf "[\e[33m%s\e[0m]" "PID"
|
||||||
|
restart
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
makeconfig () {
|
||||||
|
config_load dnsforwarder
|
||||||
|
|
||||||
|
local log=$(uci get dnsforwarder.@config[0].log 2>/dev/null)
|
||||||
|
local log_size=$(uci get dnsforwarder.@config[0].log_size 2>/dev/null)
|
||||||
|
|
||||||
|
local gfw=$(uci get dnsforwarder.@config[0].gfw 2>/dev/null)
|
||||||
|
|
||||||
|
local udp_local=$(uci -d ',' get dnsforwarder.@config[0].udp_local 2>/dev/null)
|
||||||
|
local udp_local_list=$(uci get dnsforwarder.@config[0].udp_local 2>/dev/null)
|
||||||
|
local tcp_group=$(uci get dnsforwarder.@config[0].tcp_group 2>/dev/null)
|
||||||
|
local udp_group=$(uci get dnsforwarder.@config[0].udp_group 2>/dev/null)
|
||||||
|
local group_file=$(uci get dnsforwarder.@config[0].group_file 2>/dev/null)
|
||||||
|
local block_ip=$(uci -d ',' get dnsforwarder.@config[0].block_ip 2>/dev/null)
|
||||||
|
local ip_substituting=$(uci -d ',' get dnsforwarder.@config[0].ip_substituting 2>/dev/null)
|
||||||
|
local block_negative_resp=$(uci get dnsforwarder.@config[0].block_negative_resp 2>/dev/null)
|
||||||
|
local append_host=$(uci get dnsforwarder.@config[0].append_host 2>/dev/null)
|
||||||
|
local block_ipv6=$(uci get dnsforwarder.@config[0].block_ipv6 2>/dev/null)
|
||||||
|
|
||||||
|
local cache=$(uci get dnsforwarder.@config[0].cache 2>/dev/null)
|
||||||
|
local cache_size=$(uci get dnsforwarder.@config[0].cache_size 2>/dev/null)
|
||||||
|
local cache_ignore=$(uci get dnsforwarder.@config[0].cache_ignore 2>/dev/null)
|
||||||
|
local cache_control=$(uci get dnsforwarder.@config[0].cache_control 2>/dev/null)
|
||||||
|
|
||||||
|
local domain_statistic=$(uci get dnsforwarder.@config[0].domain_statistic 2>/dev/null)
|
||||||
|
local udp_local_addr=$(uci get dnsforwarder.@arguments[0].addr 2>/dev/null)
|
||||||
|
udp_local_addr=${udp_local_addr/:/#}
|
||||||
|
|
||||||
|
echo "LogOn ${log}" > $DNSFORWARDER_CONF
|
||||||
|
if [ $log = "true" ]; then
|
||||||
|
rm /var/log/dnsforwarder.log.* 2>/dev/null
|
||||||
|
echo '' > /var/log/dnsforwarder.log
|
||||||
|
echo "LogFileThresholdLength ${log_size}" >> $DNSFORWARDER_CONF
|
||||||
|
echo "LogFileFolder /var/log" >> $DNSFORWARDER_CONF
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -n "$udp_local" ] && echo "UDPLocal ${udp_local}" >> $DNSFORWARDER_CONF
|
||||||
|
[ -n "$udp_local_addr" ] && eval "makelist 'server=' $udp_local_addr" > /tmp/dnsmasq.dnsforwarder.conf
|
||||||
|
sed -i "s/ //g" /tmp/dnsmasq.dnsforwarder.conf
|
||||||
|
|
||||||
|
eval "makelist 'TCPGroup' $tcp_group" >> $DNSFORWARDER_CONF
|
||||||
|
eval "makelist 'UDPGroup' $udp_group" >> $DNSFORWARDER_CONF
|
||||||
|
eval "makelist 'GroupFile' $group_file" >> $DNSFORWARDER_CONF
|
||||||
|
|
||||||
|
if [ $gfw = "true" ]; then
|
||||||
|
echo 'GroupFile /etc/dnsforwarder/gfw.txt' >> $DNSFORWARDER_CONF
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "BlockIP ${block_ip}" >> $DNSFORWARDER_CONF
|
||||||
|
eval "makelist 'IPSubstituting' $ip_substituting" >> $DNSFORWARDER_CONF
|
||||||
|
echo "BlockNegativeResponse ${block_negative_resp}" >> $DNSFORWARDER_CONF
|
||||||
|
eval "makelist 'AppendHosts' $append_host" >> $DNSFORWARDER_CONF
|
||||||
|
echo "BlockIpv6WhenIpv4Exists ${block_ipv6}" >> $DNSFORWARDER_CONF
|
||||||
|
|
||||||
|
echo "UseCache ${cache}" >> $DNSFORWARDER_CONF
|
||||||
|
if [ $cache = "true" ]; then
|
||||||
|
echo "CacheSize ${cache_size}" >> $DNSFORWARDER_CONF
|
||||||
|
echo "MemoryCache false" >> $DNSFORWARDER_CONF
|
||||||
|
echo "CacheFile /tmp/dnsforwarder.cache" >> $DNSFORWARDER_CONF
|
||||||
|
echo "IgnoreTTL ${cache_ignore}" >> $DNSFORWARDER_CONF
|
||||||
|
eval "makelist 'CacheControl' $cache_control" >> $DNSFORWARDER_CONF
|
||||||
|
echo "ReloadCache true" >> $DNSFORWARDER_CONF
|
||||||
|
echo "OverwriteCache true" >> $DNSFORWARDER_CONF
|
||||||
|
fi
|
||||||
|
echo "DomainStatistic ${domain_statistic}" >> $DNSFORWARDER_CONF
|
||||||
|
if [ $domain_statistic = "true" ]; then
|
||||||
|
touch /tmp/dnsforwarder-statistic.html
|
||||||
|
mkdir -p /root/.dnsforwarder
|
||||||
|
rm /root/.dnsforwarder/statistic.html 2 > /dev/null
|
||||||
|
ln -s /tmp/dnsforwarder-statistic.html /root/.dnsforwarder/statistic.html
|
||||||
|
local domain_statistic_tag='<!-- TS DNSFORWARDER -->'
|
||||||
|
echo "DomainStatisticTempletFile /tmp/dnsforwarder-statistic.html" >> $DNSFORWARDER_CONF
|
||||||
|
echo "StatisticInsertionPosition ${domain_statistic_tag}" >> $DNSFORWARDER_CONF
|
||||||
|
echo "StatisticUpdateInterval 60" >> $DNSFORWARDER_CONF
|
||||||
|
echo "${domain_statistic_tag}" > /tmp/dnsforwarder-statistic.html
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
{
|
||||||
|
echo luci for dnsforwarder
|
||||||
|
local vt_enabled=$(uci get dnsforwarder.@arguments[0].enabled 2>/dev/null)
|
||||||
|
if [ $vt_enabled = 0 ]; then
|
||||||
|
echo dnsforwarder is not enabled
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
makeconfig
|
||||||
|
fixturboacc
|
||||||
|
dnsforwarder -f $DNSFORWARDER_CONF -d
|
||||||
|
sleep 10
|
||||||
|
mkdir -p ${PID_PATH}
|
||||||
|
pid=$(ps | awk '$5 ~ /\[dnsforwarder\]/ {print $1}')
|
||||||
|
echo "dnsforwarder running pid is ${pid}"
|
||||||
|
logger -t The pid of dnsforwarder is ${PID_FILE} ${pid}
|
||||||
|
echo ${pid} > ${PID_FILE}
|
||||||
|
/etc/init.d/dnsforwarder enable
|
||||||
|
local dnsmasq=$(uci get dnsforwarder.@arguments[0].dnsmasq 2>/dev/null)
|
||||||
|
local addr=$(uci get dnsforwarder.@arguments[0].addr 2>/dev/null)
|
||||||
|
[ -n "${addr}" ] && addr=${addr/:/#}
|
||||||
|
|
||||||
|
if [ "${dnsmasq}" = "1" ]; then
|
||||||
|
uci delete dhcp.@dnsmasq[0].server 2>/dev/null
|
||||||
|
# uci add_list dhcp.@dnsmasq[0].server=$addr
|
||||||
|
uci delete dhcp.@dnsmasq[0].resolvfile 2>/dev/null
|
||||||
|
uci set dhcp.@dnsmasq[0].noresolv=1
|
||||||
|
uci set dhcp.@dnsmasq[0].serversfile=/tmp/dnsmasq.dnsforwarder.conf
|
||||||
|
uci commit dhcp
|
||||||
|
/etc/init.d/dnsmasq restart
|
||||||
|
fi
|
||||||
|
local dnsmasq_server_addr=$(uci get dhcp.@dnsmasq[0].server 2>/dev/null)
|
||||||
|
if [ -n "${dnsmasq_server_addr}" ]; then
|
||||||
|
uci set dhcp.@dnsmasq[0].noresolv=1
|
||||||
|
uci commit dhcp
|
||||||
|
/etc/init.d/dnsmasq restart
|
||||||
|
fi
|
||||||
|
add_cron
|
||||||
|
}
|
||||||
|
|
||||||
|
stop()
|
||||||
|
{
|
||||||
|
del_cron
|
||||||
|
logger -t stopping dnsforwarder
|
||||||
|
local addr=$(uci get dnsforwarder.@arguments[0].addr 2>/dev/null)
|
||||||
|
local dnsmasq=$(uci get dnsforwarder.@arguments[0].dnsmasq 2>/dev/null)
|
||||||
|
addr=${addr/:/#}
|
||||||
|
if [ "${dnsmasq}" = "1" ]; then
|
||||||
|
uci del_list dhcp.@dnsmasq[0].server=$addr 2>/dev/null
|
||||||
|
fi
|
||||||
|
uci set dhcp.@dnsmasq[0].resolvfile=/tmp/resolv.conf.d/resolv.conf.auto 2>/dev/null
|
||||||
|
uci delete dhcp.@dnsmasq[0].noresolv 2>/dev/null
|
||||||
|
uci delete dhcp.@dnsmasq[0].serversfile 2>/dev/null
|
||||||
|
uci commit dhcp
|
||||||
|
/etc/init.d/dnsmasq restart
|
||||||
|
[ -e ${PID_FILE} ] && {
|
||||||
|
pid=$(cat ${PID_FILE})
|
||||||
|
logger -t killing dnsforwarder pid ${pid}
|
||||||
|
echo killing dnsforwarder pid ${pid}
|
||||||
|
kill ${pid}
|
||||||
|
rm -f ${PID_FILE}
|
||||||
|
} || {
|
||||||
|
logger -t Cannot find dnsforwarder pid file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restart()
|
||||||
|
{
|
||||||
|
pid=$(cat ${PID_FILE} 2>/dev/null)
|
||||||
|
echo Dnsforwarder pid file is ${pid}
|
||||||
|
[ -n "$pid" ] && {
|
||||||
|
echo stopping pid ${pid}
|
||||||
|
logger -t There is dnsforwarder pid ${pid}
|
||||||
|
stop
|
||||||
|
} || {
|
||||||
|
logger -t Dnsforwarder is not running
|
||||||
|
}
|
||||||
|
sleep 7
|
||||||
|
local vt_enabled=$(uci get dnsforwarder.@arguments[0].enabled 2>/dev/null)
|
||||||
|
echo dnsforwarder status is ${vt_enabled}
|
||||||
|
logger -t Dnsforwarder is initializing enabled is ${vt_enabled}
|
||||||
|
if [ ${vt_enabled} = 1 ]; then
|
||||||
|
[ -n "$pid" ] && {
|
||||||
|
logger -t There is dnsforwarder pid ${pid}
|
||||||
|
stop
|
||||||
|
} || {
|
||||||
|
logger -t Dnsforwarder is not running
|
||||||
|
}
|
||||||
|
|
||||||
|
logger -t Restarting dnsforwarder
|
||||||
|
start
|
||||||
|
else
|
||||||
|
/etc/init.d/dnsforwarder disable
|
||||||
|
fi
|
||||||
|
}
|
313
dnsforwarder/files/usr/share/dnsforwarder/gfwlist.sh
Executable file
|
@ -0,0 +1,313 @@
|
||||||
|
#/bin/sh
|
||||||
|
|
||||||
|
# Name: gfwlist2dnsmasq.sh
|
||||||
|
# Desription: A shell script which convert gfwlist into dnsmasq rules.
|
||||||
|
# Version: 0.8.0 (2017.12.25)
|
||||||
|
# Author: Cokebar Chi
|
||||||
|
# Website: https://github.com/cokebar
|
||||||
|
|
||||||
|
_green() {
|
||||||
|
printf '\033[1;31;32m'
|
||||||
|
printf -- "%b" "$1"
|
||||||
|
printf '\033[0m'
|
||||||
|
}
|
||||||
|
|
||||||
|
_red() {
|
||||||
|
printf '\033[1;31;31m'
|
||||||
|
printf -- "%b" "$1"
|
||||||
|
printf '\033[0m'
|
||||||
|
}
|
||||||
|
|
||||||
|
_yellow() {
|
||||||
|
printf '\033[1;31;33m'
|
||||||
|
printf -- "%b" "$1"
|
||||||
|
printf '\033[0m'
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<-EOF
|
||||||
|
|
||||||
|
Name: gfwlist2dnsmasq.sh
|
||||||
|
Desription: A shell script which convert gfwlist into dnsmasq rules.
|
||||||
|
Version: 0.8.0 (2017.12.25)
|
||||||
|
Author: Cokebar Chi
|
||||||
|
Website: https://github.com/cokebar
|
||||||
|
|
||||||
|
Usage: sh gfwlist2dnsmasq.sh [options] -o FILE
|
||||||
|
Valid options are:
|
||||||
|
-d, --dns <dns_ip>
|
||||||
|
DNS IP address for the GfwList Domains (Default: 127.0.0.1)
|
||||||
|
-p, --port <dns_port>
|
||||||
|
DNS Port for the GfwList Domains (Default: 5353)
|
||||||
|
-s, --ipset <ipset_name>
|
||||||
|
Ipset name for the GfwList domains
|
||||||
|
(If not given, ipset rules will not be generated.)
|
||||||
|
-o, --output <FILE>
|
||||||
|
/path/to/output_filename
|
||||||
|
-i, --insecure
|
||||||
|
Force bypass certificate validation (insecure)
|
||||||
|
-l, --domain-list
|
||||||
|
Convert Gfwlist into domain list instead of dnsmasq rules
|
||||||
|
(If this option is set, DNS IP/Port & ipset are not needed)
|
||||||
|
--exclude-domain-file <FILE>
|
||||||
|
Delete specific domains in the result from a domain list text file
|
||||||
|
Please put one domain per line
|
||||||
|
--extra-domain-file <FILE>
|
||||||
|
Include extra domains to the result from a domain list text file
|
||||||
|
This file will be processed after the exclude-domain-file
|
||||||
|
Please put one domain per line
|
||||||
|
-h, --help
|
||||||
|
Usage
|
||||||
|
EOF
|
||||||
|
exit $1
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_and_exit(){
|
||||||
|
# Clean up temp files
|
||||||
|
printf 'Cleaning up... '
|
||||||
|
rm -rf $TMP_DIR
|
||||||
|
_green 'Done\n\n'
|
||||||
|
[ $1 -eq 0 ] && _green 'Job Finished.\n\n' || _red 'Exit with Error code '$1'.\n'
|
||||||
|
exit $1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_depends(){
|
||||||
|
which sed base64 curl >/dev/null
|
||||||
|
if [ $? != 0 ]; then
|
||||||
|
_red 'Error: Missing Dependency.\nPlease check whether you have the following binaries on you system:\nwhich, sed, base64, curl.\n'
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
SYS_KERNEL=`uname -s`
|
||||||
|
if [ $SYS_KERNEL = "Darwin" -o $SYS_KERNEL = "FreeBSD" ]; then
|
||||||
|
BASE64_DECODE='base64 -D'
|
||||||
|
SED_ERES='sed -E'
|
||||||
|
else
|
||||||
|
BASE64_DECODE='base64 -d'
|
||||||
|
SED_ERES='sed -r'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_args(){
|
||||||
|
OUT_TYPE='DNSMASQ_RULES'
|
||||||
|
DNS_IP='127.0.0.1'
|
||||||
|
DNS_PORT='5353'
|
||||||
|
IPSET_NAME=''
|
||||||
|
FILE_FULLPATH=''
|
||||||
|
CURL_EXTARG=''
|
||||||
|
WITH_IPSET=0
|
||||||
|
EXTRA_DOMAIN_FILE=''
|
||||||
|
EXCLUDE_DOMAIN_FILE=''
|
||||||
|
IPV4_PATTERN='^((2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?)\.){3}(2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?)$'
|
||||||
|
IPV6_PATTERN='^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?$'
|
||||||
|
|
||||||
|
while [ ${#} -gt 0 ]; do
|
||||||
|
case "${1}" in
|
||||||
|
--help | -h)
|
||||||
|
usage 0
|
||||||
|
;;
|
||||||
|
--domain-list | -l)
|
||||||
|
OUT_TYPE='DOMAIN_LIST'
|
||||||
|
;;
|
||||||
|
--insecure | -i)
|
||||||
|
CURL_EXTARG='--insecure'
|
||||||
|
;;
|
||||||
|
--dns | -d)
|
||||||
|
DNS_IP="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--port | -p)
|
||||||
|
DNS_PORT="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--ipset | -s)
|
||||||
|
IPSET_NAME="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--output | -o)
|
||||||
|
OUT_FILE="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--extra-domain-file)
|
||||||
|
EXTRA_DOMAIN_FILE="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--exclude-domain-file)
|
||||||
|
EXCLUDE_DOMAIN_FILE="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_red "Invalid argument: $1"
|
||||||
|
usage 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check path & file name
|
||||||
|
if [ -z $OUT_FILE ]; then
|
||||||
|
_red 'Error: Please specify the path to the output file(using -o/--output argument).\n'
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
if [ -z ${OUT_FILE##*/} ]; then
|
||||||
|
_red 'Error: '$OUT_FILE' is a path, not a file.\n'
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
if [ ${OUT_FILE}a != ${OUT_FILE%/*}a ] && [ ! -d ${OUT_FILE%/*} ]; then
|
||||||
|
_red 'Error: Folder do not exist: '${OUT_FILE%/*}'\n'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $OUT_TYPE = 'DNSMASQ_RULES' ]; then
|
||||||
|
# Check DNS IP
|
||||||
|
IPV4_TEST=$(echo $DNS_IP | grep -E $IPV4_PATTERN)
|
||||||
|
IPV6_TEST=$(echo $DNS_IP | grep -E $IPV6_PATTERN)
|
||||||
|
if [ "$IPV4_TEST" != "$DNS_IP" -a "$IPV6_TEST" != "$DNS_IP" ]; then
|
||||||
|
_red 'Error: Please enter a valid DNS server IP address.\n'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check DNS port
|
||||||
|
if [ $DNS_PORT -lt 1 -o $DNS_PORT -gt 65535 ]; then
|
||||||
|
_red 'Error: Please enter a valid DNS server port.\n'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check ipset name
|
||||||
|
if [ -z $IPSET_NAME ]; then
|
||||||
|
WITH_IPSET=0
|
||||||
|
else
|
||||||
|
IPSET_TEST=$(echo $IPSET_NAME | grep -E '^\w+$')
|
||||||
|
if [ "$IPSET_TEST" != "$IPSET_NAME" ]; then
|
||||||
|
_red 'Error: Please enter a valid IP set name.\n'
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
WITH_IPSET=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -z $EXTRA_DOMAIN_FILE ] && [ ! -f $EXTRA_DOMAIN_FILE ]; then
|
||||||
|
_yellow 'WARNING:\nExtra domain file does not exist, ignored.\n\n'
|
||||||
|
EXTRA_DOMAIN_FILE=''
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -z $EXCLUDE_DOMAIN_FILE ] && [ ! -f $EXCLUDE_DOMAIN_FILE ]; then
|
||||||
|
_yellow 'WARNING:\nExclude domain file does not exist, ignored.\n\n'
|
||||||
|
EXCLUDE_DOMAIN_FILE=''
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
process(){
|
||||||
|
# Set Global Var
|
||||||
|
BASE_URL='https://github.com/gfwlist/gfwlist/raw/master/gfwlist.txt'
|
||||||
|
TMP_DIR=`mktemp -d /tmp/gfwlist2dnsmasq.XXXXXX`
|
||||||
|
BASE64_FILE="$TMP_DIR/base64.txt"
|
||||||
|
GFWLIST_FILE="$TMP_DIR/gfwlist.txt"
|
||||||
|
DOMAIN_TEMP_FILE="$TMP_DIR/gfwlist2domain.tmp"
|
||||||
|
DOMAIN_FILE="$TMP_DIR/gfwlist2domain.txt"
|
||||||
|
CONF_TMP_FILE="$TMP_DIR/gfwlist.conf.tmp"
|
||||||
|
OUT_TMP_FILE="$TMP_DIR/gfwlist.out.tmp"
|
||||||
|
|
||||||
|
# Fetch GfwList and decode it into plain text
|
||||||
|
printf 'Fetching GfwList... '
|
||||||
|
local tscurl='curl -L --connect-timeout 5 -m 300 --retry 3 --retry-delay 1'
|
||||||
|
$tscurl $CURL_EXTARG -o$BASE64_FILE $BASE_URL \
|
||||||
|
|| $tscurl $CURL_EXTARG -o$BASE64_FILE https://gitlab.com/gfwlist/gfwlist/raw/master/gfwlist.txt \
|
||||||
|
|| $tscurl $CURL_EXTARG -o$BASE64_FILE https://git.tuxfamily.org/gfwlist/gfwlist.git/plain/gfwlist.txt \
|
||||||
|
|| $tscurl $CURL_EXTARG -o$BASE64_FILE https://pagure.io/gfwlist/raw/master/f/gfwlist.txt \
|
||||||
|
|| $tscurl $CURL_EXTARG -o$BASE64_FILE http://repo.or.cz/gfwlist.git/blob_plain/HEAD:/gfwlist.txt \
|
||||||
|
|| $tscurl $CURL_EXTARG -o$BASE64_FILE https://bitbucket.org/gfwlist/gfwlist/raw/HEAD/gfwlist.txt \
|
||||||
|
|| $tscurl $CURL_EXTARG -o$BASE64_FILE $BASE_URL
|
||||||
|
if [ $? != 0 ]; then
|
||||||
|
_red '\nFailed to fetch gfwlist.txt. Please check your Internet connection.\n'
|
||||||
|
clean_and_exit 2
|
||||||
|
fi
|
||||||
|
$BASE64_DECODE $BASE64_FILE > $GFWLIST_FILE || ( _red 'Failed to decode gfwlist.txt. Quit.\n'; clean_and_exit 2 )
|
||||||
|
_green 'Done.\n\n'
|
||||||
|
|
||||||
|
# Convert
|
||||||
|
IGNORE_PATTERN='^\!|\[|^@@|(https?://){0,1}[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'
|
||||||
|
HEAD_FILTER_PATTERN='s#^(\|\|?)?(https?://)?##g'
|
||||||
|
TAIL_FILTER_PATTERN='s#/.*$|%2F.*$##g'
|
||||||
|
DOMAIN_PATTERN='([a-zA-Z0-9][-a-zA-Z0-9]*(\.[a-zA-Z0-9][-a-zA-Z0-9]*)+)'
|
||||||
|
HANDLE_WILDCARD_PATTERN='s#^(([a-zA-Z0-9]*\*[-a-zA-Z0-9]*)?(\.))?([a-zA-Z0-9][-a-zA-Z0-9]*(\.[a-zA-Z0-9][-a-zA-Z0-9]*)+)(\*)?#\4#g'
|
||||||
|
|
||||||
|
printf 'Converting GfwList to ' && _green $OUT_TYPE && printf ' ...\n'
|
||||||
|
_yellow '\nWARNING:\nThe following lines in GfwList contain regex, and might be ignored:\n\n'
|
||||||
|
cat $GFWLIST_FILE | grep -n '^/.*$'
|
||||||
|
_yellow "\nThis script will try to convert some of the regex rules. But you should know this may not be a equivalent conversion.\nIf there's regex rules which this script do not deal with, you should add the domain manually to the list.\n\n"
|
||||||
|
grep -vE $IGNORE_PATTERN $GFWLIST_FILE | $SED_ERES $HEAD_FILTER_PATTERN | $SED_ERES $TAIL_FILTER_PATTERN | grep -E $DOMAIN_PATTERN | $SED_ERES $HANDLE_WILDCARD_PATTERN > $DOMAIN_TEMP_FILE
|
||||||
|
|
||||||
|
printf 'google.com\ngoogle.ad\ngoogle.ae\ngoogle.com.af\ngoogle.com.ag\ngoogle.com.ai\ngoogle.al\ngoogle.am\ngoogle.co.ao\ngoogle.com.ar\ngoogle.as\ngoogle.at\ngoogle.com.au\ngoogle.az\ngoogle.ba\ngoogle.com.bd\ngoogle.be\ngoogle.bf\ngoogle.bg\ngoogle.com.bh\ngoogle.bi\ngoogle.bj\ngoogle.com.bn\ngoogle.com.bo\ngoogle.com.br\ngoogle.bs\ngoogle.bt\ngoogle.co.bw\ngoogle.by\ngoogle.com.bz\ngoogle.ca\ngoogle.cd\ngoogle.cf\ngoogle.cg\ngoogle.ch\ngoogle.ci\ngoogle.co.ck\ngoogle.cl\ngoogle.cm\ngoogle.cn\ngoogle.com.co\ngoogle.co.cr\ngoogle.com.cu\ngoogle.cv\ngoogle.com.cy\ngoogle.cz\ngoogle.de\ngoogle.dj\ngoogle.dk\ngoogle.dm\ngoogle.com.do\ngoogle.dz\ngoogle.com.ec\ngoogle.ee\ngoogle.com.eg\ngoogle.es\ngoogle.com.et\ngoogle.fi\ngoogle.com.fj\ngoogle.fm\ngoogle.fr\ngoogle.ga\ngoogle.ge\ngoogle.gg\ngoogle.com.gh\ngoogle.com.gi\ngoogle.gl\ngoogle.gm\ngoogle.gp\ngoogle.gr\ngoogle.com.gt\ngoogle.gy\ngoogle.com.hk\ngoogle.hn\ngoogle.hr\ngoogle.ht\ngoogle.hu\ngoogle.co.id\ngoogle.ie\ngoogle.co.il\ngoogle.im\ngoogle.co.in\ngoogle.iq\ngoogle.is\ngoogle.it\ngoogle.je\ngoogle.com.jm\ngoogle.jo\ngoogle.co.jp\ngoogle.co.ke\ngoogle.com.kh\ngoogle.ki\ngoogle.kg\ngoogle.co.kr\ngoogle.com.kw\ngoogle.kz\ngoogle.la\ngoogle.com.lb\ngoogle.li\ngoogle.lk\ngoogle.co.ls\ngoogle.lt\ngoogle.lu\ngoogle.lv\ngoogle.com.ly\ngoogle.co.ma\ngoogle.md\ngoogle.me\ngoogle.mg\ngoogle.mk\ngoogle.ml\ngoogle.com.mm\ngoogle.mn\ngoogle.ms\ngoogle.com.mt\ngoogle.mu\ngoogle.mv\ngoogle.mw\ngoogle.com.mx\ngoogle.com.my\ngoogle.co.mz\ngoogle.com.na\ngoogle.com.nf\ngoogle.com.ng\ngoogle.com.ni\ngoogle.ne\ngoogle.nl\ngoogle.no\ngoogle.com.np\ngoogle.nr\ngoogle.nu\ngoogle.co.nz\ngoogle.com.om\ngoogle.com.pa\ngoogle.com.pe\ngoogle.com.pg\ngoogle.com.ph\ngoogle.com.pk\ngoogle.pl\ngoogle.pn\ngoogle.com.pr\ngoogle.ps\ngoogle.pt\ngoogle.com.py\ngoogle.com.qa\ngoogle.ro\ngoogle.ru\ngoogle.rw\ngoogle.com.sa\ngoogle.com.sb\ngoogle.sc\ngoogle.se\ngoogle.com.sg\ngoogle.sh\ngoogle.si\ngoogle.sk\ngoogle.com.sl\ngoogle.sn\ngoogle.so\ngoogle.sm\ngoogle.sr\ngoogle.st\ngoogle.com.sv\ngoogle.td\ngoogle.tg\ngoogle.co.th\ngoogle.com.tj\ngoogle.tk\ngoogle.tl\ngoogle.tm\ngoogle.tn\ngoogle.to\ngoogle.com.tr\ngoogle.tt\ngoogle.com.tw\ngoogle.co.tz\ngoogle.com.ua\ngoogle.co.ug\ngoogle.co.uk\ngoogle.com.uy\ngoogle.co.uz\ngoogle.com.vc\ngoogle.co.ve\ngoogle.vg\ngoogle.co.vi\ngoogle.com.vn\ngoogle.vu\ngoogle.ws\ngoogle.rs\ngoogle.co.za\ngoogle.co.zm\ngoogle.co.zw\ngoogle.cat\n' >> $DOMAIN_TEMP_FILE
|
||||||
|
printf 'Google search domains... ' && _green 'Added\n'
|
||||||
|
|
||||||
|
# Add blogspot domains
|
||||||
|
printf 'blogspot.ca\nblogspot.co.uk\nblogspot.com\nblogspot.com.ar\nblogspot.com.au\nblogspot.com.br\nblogspot.com.by\nblogspot.com.co\nblogspot.com.cy\nblogspot.com.ee\nblogspot.com.eg\nblogspot.com.es\nblogspot.com.mt\nblogspot.com.ng\nblogspot.com.tr\nblogspot.com.uy\nblogspot.de\nblogspot.gr\nblogspot.in\nblogspot.mx\nblogspot.ch\nblogspot.fr\nblogspot.ie\nblogspot.it\nblogspot.pt\nblogspot.ro\nblogspot.sg\nblogspot.be\nblogspot.no\nblogspot.se\nblogspot.jp\nblogspot.in\nblogspot.ae\nblogspot.al\nblogspot.am\nblogspot.ba\nblogspot.bg\nblogspot.ch\nblogspot.cl\nblogspot.cz\nblogspot.dk\nblogspot.fi\nblogspot.gr\nblogspot.hk\nblogspot.hr\nblogspot.hu\nblogspot.ie\nblogspot.is\nblogspot.kr\nblogspot.li\nblogspot.lt\nblogspot.lu\nblogspot.md\nblogspot.mk\nblogspot.my\nblogspot.nl\nblogspot.no\nblogspot.pe\nblogspot.qa\nblogspot.ro\nblogspot.ru\nblogspot.se\nblogspot.sg\nblogspot.si\nblogspot.sk\nblogspot.sn\nblogspot.tw\nblogspot.ug\nblogspot.cat\n' >> $DOMAIN_TEMP_FILE
|
||||||
|
printf 'Blogspot domains... ' && _green 'Added\n'
|
||||||
|
|
||||||
|
# Add twimg.edgesuite.net
|
||||||
|
printf 'twimg.edgesuite.net\n' >> $DOMAIN_TEMP_FILE
|
||||||
|
printf 'twimg.edgesuite.net... ' && _green 'Added\n'
|
||||||
|
|
||||||
|
# Delete exclude domains
|
||||||
|
if [ ! -z $EXCLUDE_DOMAIN_FILE ]; then
|
||||||
|
for line in $(cat $EXCLUDE_DOMAIN_FILE)
|
||||||
|
do
|
||||||
|
cat $DOMAIN_TEMP_FILE | grep -vF -f $EXCLUDE_DOMAIN_FILE > $DOMAIN_FILE
|
||||||
|
done
|
||||||
|
printf 'Domains in exclude domain file '$EXCLUDE_DOMAIN_FILE'... ' && _green 'Deleted\n'
|
||||||
|
else
|
||||||
|
cat $DOMAIN_TEMP_FILE > $DOMAIN_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add extra domains
|
||||||
|
if [ ! -z $EXTRA_DOMAIN_FILE ]; then
|
||||||
|
cat $EXTRA_DOMAIN_FILE >> $DOMAIN_FILE
|
||||||
|
printf 'Extra domain file '$EXTRA_DOMAIN_FILE'... ' && _green 'Added\n'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $OUT_TYPE = 'DNSMASQ_RULES' ]; then
|
||||||
|
# Convert domains into dnsmasq rules
|
||||||
|
if [ $WITH_IPSET -eq 1 ]; then
|
||||||
|
_green 'Ipset rules included.'
|
||||||
|
sort -u $DOMAIN_FILE | $SED_ERES 's#(.+)#server=/\1/'$DNS_IP'\#'$DNS_PORT'\
|
||||||
|
ipset=/\1/'$IPSET_NAME'#g' > $CONF_TMP_FILE
|
||||||
|
else
|
||||||
|
_green 'Ipset rules not included.'
|
||||||
|
sort -u $DOMAIN_FILE | $SED_ERES 's#(.+)#server=/\1/'$DNS_IP'\#'$DNS_PORT'#g' > $CONF_TMP_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate output file
|
||||||
|
echo '# dnsmasq rules generated by gfwlist' > $OUT_TMP_FILE
|
||||||
|
echo "# Last Updated on $(date "+%Y-%m-%d %H:%M:%S")" >> $OUT_TMP_FILE
|
||||||
|
echo '# ' >> $OUT_TMP_FILE
|
||||||
|
cat $CONF_TMP_FILE >> $OUT_TMP_FILE
|
||||||
|
cp $OUT_TMP_FILE $OUT_FILE
|
||||||
|
else
|
||||||
|
sort -u $DOMAIN_FILE > $OUT_TMP_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp $OUT_TMP_FILE $OUT_FILE
|
||||||
|
printf '\nConverting GfwList to '$OUT_TYPE'... ' && _green 'Done\n\n'
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
clean_and_exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
usage 0
|
||||||
|
else
|
||||||
|
check_depends
|
||||||
|
get_args "$@"
|
||||||
|
_green '\nJob Started.\n\n'
|
||||||
|
process
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
59
dnsproxy/Makefile
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
#
|
||||||
|
# Copyright (C) 2021 ImmortalWrt.org
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=dnsproxy
|
||||||
|
PKG_VERSION:=0.42.2
|
||||||
|
PKG_RELEASE:=$(AUTORELEASE)
|
||||||
|
|
||||||
|
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||||
|
PKG_SOURCE_URL:=https://codeload.github.com/AdguardTeam/dnsproxy/tar.gz/v$(PKG_VERSION)?
|
||||||
|
PKG_HASH:=399a7a1f7d1afef85d8557bbe445541872bfe005e15c36e242f69b78fa94f1ca
|
||||||
|
|
||||||
|
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||||
|
PKG_LICENSE:=Apache-2.0
|
||||||
|
PKG_LICENSE_FILES:=LICENSE
|
||||||
|
|
||||||
|
PKG_BUILD_DEPENDS:=golang/host
|
||||||
|
PKG_BUILD_PARALLEL:=1
|
||||||
|
PKG_USE_MIPS16:=0
|
||||||
|
|
||||||
|
GO_PKG:=github.com/AdguardTeam/dnsproxy
|
||||||
|
GO_PKG_LDFLAGS_X:=main.VersionString=v$(PKG_VERSION)
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
include $(TOPDIR)/feeds/openmptcprouter/golang/golang-package.mk
|
||||||
|
|
||||||
|
define Package/dnsproxy
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
SUBMENU:=IP Addresses and Names
|
||||||
|
TITLE:=Simple DNS proxy with DoH, DoT, DoQ and DNSCrypt support
|
||||||
|
URL:=https://github.com/AdguardTeam/dnsproxy
|
||||||
|
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
|
||||||
|
USERID:=dnsproxy=411:dnsproxy=411
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/dnsproxy/description
|
||||||
|
A simple DNS proxy server that supports all existing DNS protocols including
|
||||||
|
DNS-over-TLS, DNS-over-HTTPS, DNSCrypt, and DNS-over-QUIC.Moreover, it can
|
||||||
|
work as a DNS-over-HTTPS, DNS-over-TLS or DNS-over-QUIC server.
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/dnsproxy/install
|
||||||
|
$(call GoPackage/Package/Install/Bin,$(1))
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/etc/config/
|
||||||
|
$(INSTALL_CONF) $(CURDIR)/files/dnsproxy.config $(1)/etc/config/dnsproxy
|
||||||
|
$(INSTALL_DIR) $(1)/etc/init.d/
|
||||||
|
$(INSTALL_BIN) $(CURDIR)/files/dnsproxy.init $(1)/etc/init.d/dnsproxy
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/dnsproxy/conffiles
|
||||||
|
/etc/config/dnsproxy
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call GoBinPackage,dnsproxy))
|
||||||
|
$(eval $(call BuildPackage,dnsproxy))
|
41
dnsproxy/files/dnsproxy.config
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
|
||||||
|
# For documents, please see https://github.com/AdguardTeam/dnsproxy#usage
|
||||||
|
|
||||||
|
config dnsproxy 'global'
|
||||||
|
option enabled '0'
|
||||||
|
option listen_addr '127.0.0.1'
|
||||||
|
option listen_port '5353'
|
||||||
|
option log_file ''
|
||||||
|
option all_servers '0'
|
||||||
|
option fastest_addr '0'
|
||||||
|
option insecure '0'
|
||||||
|
option ipv6_disabled '0'
|
||||||
|
option max_go_routines ''
|
||||||
|
option rate_limit ''
|
||||||
|
option refuse_any '0'
|
||||||
|
option udp_buf_size ''
|
||||||
|
option verbose '0'
|
||||||
|
|
||||||
|
config dnsproxy 'bogus_nxdomain'
|
||||||
|
list ip_addr ''
|
||||||
|
|
||||||
|
config dnsproxy 'cache'
|
||||||
|
option enabled '0'
|
||||||
|
option cache_optimistic '0'
|
||||||
|
option size '65535'
|
||||||
|
option min_ttl ''
|
||||||
|
option max_ttl ''
|
||||||
|
|
||||||
|
config dnsproxy 'dns64'
|
||||||
|
option enabled '0'
|
||||||
|
option dns64_prefix '64:ff9b::'
|
||||||
|
|
||||||
|
config dnsproxy 'edns'
|
||||||
|
option enabled '0'
|
||||||
|
option edns_addr ''
|
||||||
|
|
||||||
|
config dnsproxy 'servers'
|
||||||
|
list bootstrap 'tls://8.8.8.8'
|
||||||
|
list fallback 'tls://9.9.9.9'
|
||||||
|
list upstream 'tls://1.1.1.1'
|
||||||
|
|
115
dnsproxy/files/dnsproxy.init
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
# Copyright (C) 2021 Tianling Shen <cnsztl@immortalwrt.org>
|
||||||
|
|
||||||
|
USE_PROCD=1
|
||||||
|
START=90
|
||||||
|
|
||||||
|
CONF="dnsproxy"
|
||||||
|
PROG="/usr/bin/dnsproxy"
|
||||||
|
|
||||||
|
is_enabled() {
|
||||||
|
local enabled
|
||||||
|
config_get enabled "$1" "$2" "0"
|
||||||
|
if [ "$enabled" -eq "1" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
is_empty() {
|
||||||
|
local empty
|
||||||
|
config_get empty "$1" "$2" $3
|
||||||
|
if [ -z "$empty" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
append_param() {
|
||||||
|
procd_append_param command "$1" $2
|
||||||
|
}
|
||||||
|
|
||||||
|
append_param_arg() {
|
||||||
|
local value
|
||||||
|
config_get value "$1" "$2" $4
|
||||||
|
[ -n "$value" ] && append_param "$3" "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
append_param_bool() {
|
||||||
|
is_enabled "$1" "$2" && append_param "--${2//_/-}"
|
||||||
|
}
|
||||||
|
|
||||||
|
load_config_arg() {
|
||||||
|
append_param_bool "$1" "all_servers"
|
||||||
|
append_param_bool "$1" "fastest_addr"
|
||||||
|
append_param_bool "$1" "insecure"
|
||||||
|
append_param_bool "$1" "ipv6_disabled"
|
||||||
|
append_param_bool "$1" "refuse_any"
|
||||||
|
append_param_bool "$1" "verbose"
|
||||||
|
}
|
||||||
|
|
||||||
|
load_config_list() {
|
||||||
|
is_empty "bogus_nxdomain" "ip_addr" || config_list_foreach "bogus_nxdomain" "ip_addr" "append_param '--bogus-nxdomain'"
|
||||||
|
|
||||||
|
for i in "bootstrap" "fallback" "upstream"; do
|
||||||
|
is_empty "servers" "$i" || config_list_foreach "servers" "$i" "append_param '--$i'"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
load_config_param() {
|
||||||
|
append_param_arg "global" "listen_addr" "--listen" "127.0.0.1"
|
||||||
|
append_param_arg "global" "listen_port" "--port" "5353"
|
||||||
|
append_param_arg "global" "log_file" "--output"
|
||||||
|
append_param_arg "global" "max_go_routines" "--max-go-routines"
|
||||||
|
append_param_arg "global" "rate_limit" "--ratelimit"
|
||||||
|
append_param_arg "global" "udp_buf_size" "--udp-buf-size"
|
||||||
|
|
||||||
|
is_enabled "cache" "enabled" && {
|
||||||
|
append_param "--cache"
|
||||||
|
append_param_bool "cache" "cache_optimistic"
|
||||||
|
append_param_arg "cache" "size" "--cache-size"
|
||||||
|
append_param_arg "cache" "min_ttl" "--cache-min-ttl"
|
||||||
|
append_param_arg "cache" "max_ttl" "--cache-max-ttl"
|
||||||
|
}
|
||||||
|
|
||||||
|
is_enabled "dns64" "enabled" && {
|
||||||
|
append_param "--dns64"
|
||||||
|
append_param_arg "dns64" "dns64_prefix" "--dns64-prefix"
|
||||||
|
}
|
||||||
|
|
||||||
|
is_enabled "edns" "enabled" && {
|
||||||
|
append_param "--edns"
|
||||||
|
append_param_arg "edns" "edns_addr" "--edns-addr"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
config_load "$CONF"
|
||||||
|
|
||||||
|
is_enabled "global" "enabled" || exit 1
|
||||||
|
|
||||||
|
procd_open_instance "$CONF"
|
||||||
|
procd_set_param command "$PROG"
|
||||||
|
|
||||||
|
load_config_arg "global"
|
||||||
|
load_config_list
|
||||||
|
load_config_param
|
||||||
|
|
||||||
|
procd_set_param respawn
|
||||||
|
procd_set_param stdout 1
|
||||||
|
procd_set_param stderr 1
|
||||||
|
procd_set_param user dnsproxy
|
||||||
|
|
||||||
|
procd_close_instance
|
||||||
|
}
|
||||||
|
|
||||||
|
reload_service() {
|
||||||
|
stop
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
|
service_triggers() {
|
||||||
|
procd_add_reload_trigger "$CONF"
|
||||||
|
}
|
3
dnsproxy/test.sh
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
dnsproxy --version | grep "$PKG_VERSION"
|
109
fast-classifier/Makefile
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
|
# above copyright notice and this permission notice appear in all copies.
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
include $(INCLUDE_DIR)/kernel.mk
|
||||||
|
|
||||||
|
PKG_NAME:=fast-classifier
|
||||||
|
PKG_RELEASE:=3
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define KernelPackage/fast-classifier/Default
|
||||||
|
SECTION:=kernel
|
||||||
|
CATEGORY:=Kernel modules
|
||||||
|
SUBMENU:=Network Support
|
||||||
|
DEPENDS:=+kmod-ipt-conntrack +kmod-shortcut-fe
|
||||||
|
TITLE:=Kernel driver for FAST Classifier
|
||||||
|
FILES:=$(PKG_BUILD_DIR)/fast-classifier.ko
|
||||||
|
KCONFIG:= \
|
||||||
|
CONFIG_NF_CONNTRACK_EVENTS=y \
|
||||||
|
CONFIG_NF_CONNTRACK_CHAIN_EVENTS=y \
|
||||||
|
CONFIG_NF_CONNTRACK_MARK=y \
|
||||||
|
CONFIG_XFRM=y
|
||||||
|
CONFLICTS:=kmod-shortcut-fe-drv kmod-shortcut-fe-cm
|
||||||
|
endef
|
||||||
|
|
||||||
|
define KernelPackage/fast-classifier
|
||||||
|
$(call KernelPackage/fast-classifier/Default)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define KernelPackage/fast-classifier-noload
|
||||||
|
$(call KernelPackage/fast-classifier/Default)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define KernelPackage/fast-classifier/Default/description
|
||||||
|
FAST Classifier talks to SFE to make decisions about offloading connections
|
||||||
|
endef
|
||||||
|
|
||||||
|
define KernelPackage/fast-classifier/description
|
||||||
|
$(call KernelPackage/fast-classifier/Default/description)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define KernelPackage/fast-classifier-noload/description
|
||||||
|
$(call KernelPackage/fast-classifier/Default/description)
|
||||||
|
|
||||||
|
This package does not load fast-classifier at boot by default
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/fast-classifier-example
|
||||||
|
TITLE:=Example user space program for fast-classifier
|
||||||
|
DEPENDS:=+libnl +kmod-fast-classifier
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/fast-classifier-example/description
|
||||||
|
Example user space program that communicates with fast
|
||||||
|
classifier kernel module
|
||||||
|
endef
|
||||||
|
|
||||||
|
HAVE_ECM:=$(CONFIG_PACKAGE_kmod-qca-nss-ecm-premium)$(CONFIG_PACKAGE_kmod-qca-nss-ecm-noload)$(CONFIG_PACKAGE_kmod-qca-nss-ecm-premium-noload)$(CONFIG_PACKAGE_kmod-qca-nss-ecm-standard)
|
||||||
|
|
||||||
|
define Build/Compile/kmod
|
||||||
|
+$(MAKE) $(PKG_JOBS) -C "$(LINUX_DIR)" \
|
||||||
|
$(KERNEL_MAKE_FLAGS) \
|
||||||
|
$(PKG_MAKE_FLAGS) \
|
||||||
|
M="$(PKG_BUILD_DIR)" \
|
||||||
|
CONFIG_FAST_CLASSIFIER=m \
|
||||||
|
EXTRA_CFLAGS+="-DSFE_SUPPORT_IPV6" \
|
||||||
|
$(if $(HAVE_ECM),EXTRA_CFLAGS+="-DCONFIG_SFE_ECM" CONFIG_SFE_ECM=y,) \
|
||||||
|
modules
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile/example
|
||||||
|
$(TARGET_CC) -o $(PKG_BUILD_DIR)/userspace_fast_classifier \
|
||||||
|
-I $(PKG_BUILD_DIR) \
|
||||||
|
-I$(STAGING_DIR)/usr/include/libnl \
|
||||||
|
-I$(STAGING_DIR)/usr/include/libnl3 \
|
||||||
|
-lnl-genl-3 -lnl-3 \
|
||||||
|
$(PKG_BUILD_DIR)/nl_classifier_test.c
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
$(Build/Compile/kmod)
|
||||||
|
$(if $(CONFIG_PACKAGE_fast-classifier-example),$(Build/Compile/example))
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/InstallDev
|
||||||
|
$(INSTALL_DIR) $(1)/usr/include
|
||||||
|
$(CP) $(PKG_BUILD_DIR)/fast-classifier.h $(1)/usr/include/
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/fast-classifier-example/install
|
||||||
|
$(INSTALL_DIR) $(1)/sbin
|
||||||
|
$(CP) $(PKG_BUILD_DIR)/userspace_fast_classifier $(1)/sbin/
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call KernelPackage,fast-classifier))
|
||||||
|
#$(eval $(call KernelPackage,fast-classifier-noload))
|
||||||
|
#$(eval $(call BuildPackage,fast-classifier-example))
|
10
fast-classifier/src/Makefile
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
obj-$(CONFIG_FAST_CLASSIFIER) += fast-classifier.o
|
||||||
|
|
||||||
|
ifeq ($(SFE_SUPPORT_IPV6),)
|
||||||
|
SFE_SUPPORT_IPV6=y
|
||||||
|
endif
|
||||||
|
ccflags-$(SFE_SUPPORT_IPV6) += -DSFE_SUPPORT_IPV6
|
||||||
|
|
||||||
|
ccflags-y += -I$(obj)/../shortcut-fe
|
||||||
|
|
||||||
|
obj ?= .
|
2002
fast-classifier/src/fast-classifier.c
Normal file
57
fast-classifier/src/fast-classifier.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* User space header to send message to the fast classifier
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013,2016 The Linux Foundation. All rights reserved.
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
* any purpose with or without fee is hereby granted, provided that the
|
||||||
|
* above copyright notice and this permission notice appear in all copies.
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
|
||||||
|
#define FAST_CLASSIFIER_GENL_VERSION (1)
|
||||||
|
#define FAST_CLASSIFIER_GENL_NAME "FC"
|
||||||
|
#define FAST_CLASSIFIER_GENL_MCGRP "FC_MCGRP"
|
||||||
|
#define FAST_CLASSIFIER_GENL_HDRSIZE (0)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
FAST_CLASSIFIER_A_UNSPEC,
|
||||||
|
FAST_CLASSIFIER_A_TUPLE,
|
||||||
|
__FAST_CLASSIFIER_A_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FAST_CLASSIFIER_A_MAX (__FAST_CLASSIFIER_A_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
FAST_CLASSIFIER_C_UNSPEC,
|
||||||
|
FAST_CLASSIFIER_C_OFFLOAD,
|
||||||
|
FAST_CLASSIFIER_C_OFFLOADED,
|
||||||
|
FAST_CLASSIFIER_C_DONE,
|
||||||
|
__FAST_CLASSIFIER_C_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FAST_CLASSIFIER_C_MAX (__FAST_CLASSIFIER_C_MAX - 1)
|
||||||
|
|
||||||
|
struct fast_classifier_tuple {
|
||||||
|
unsigned short ethertype;
|
||||||
|
unsigned char proto;
|
||||||
|
union {
|
||||||
|
struct in_addr in;
|
||||||
|
struct in6_addr in6;
|
||||||
|
} src_saddr;
|
||||||
|
union {
|
||||||
|
struct in_addr in;
|
||||||
|
struct in6_addr in6;
|
||||||
|
} dst_saddr;
|
||||||
|
unsigned short sport;
|
||||||
|
unsigned short dport;
|
||||||
|
unsigned char smac[ETH_ALEN];
|
||||||
|
unsigned char dmac[ETH_ALEN];
|
||||||
|
};
|
281
fast-classifier/src/nl_classifier_test.c
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 The Linux Foundation. All rights reserved.
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
* any purpose with or without fee is hereby granted, provided that the
|
||||||
|
* above copyright notice and this permission notice appear in all copies.
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink/genl/genl.h>
|
||||||
|
#include <netlink/genl/ctrl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#define NL_CLASSIFIER_GENL_VERSION 1
|
||||||
|
#define NL_CLASSIFIER_GENL_FAMILY "FC"
|
||||||
|
#define NL_CLASSIFIER_GENL_GROUP "FC_MCGRP"
|
||||||
|
#define NL_CLASSIFIER_GENL_HDRSIZE 0
|
||||||
|
|
||||||
|
enum NL_CLASSIFIER_CMD {
|
||||||
|
NL_CLASSIFIER_CMD_UNSPEC,
|
||||||
|
NL_CLASSIFIER_CMD_ACCEL,
|
||||||
|
NL_CLASSIFIER_CMD_ACCEL_OK,
|
||||||
|
NL_CLASSIFIER_CMD_CONNECTION_CLOSED,
|
||||||
|
NL_CLASSIFIER_CMD_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum NL_CLASSIFIER_ATTR {
|
||||||
|
NL_CLASSIFIER_ATTR_UNSPEC,
|
||||||
|
NL_CLASSIFIER_ATTR_TUPLE,
|
||||||
|
NL_CLASSIFIER_ATTR_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
union nl_classifier_tuple_ip {
|
||||||
|
struct in_addr in;
|
||||||
|
struct in6_addr in6;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nl_classifier_tuple {
|
||||||
|
unsigned short af;
|
||||||
|
unsigned char proto;
|
||||||
|
union nl_classifier_tuple_ip src_ip;
|
||||||
|
union nl_classifier_tuple_ip dst_ip;
|
||||||
|
unsigned short sport;
|
||||||
|
unsigned short dport;
|
||||||
|
unsigned char smac[6];
|
||||||
|
unsigned char dmac[6];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nl_classifier_instance {
|
||||||
|
struct nl_sock *sock;
|
||||||
|
int family_id;
|
||||||
|
int group_id;
|
||||||
|
int stop;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nl_classifier_instance nl_cls_inst;
|
||||||
|
|
||||||
|
static struct nla_policy nl_classifier_genl_policy[(NL_CLASSIFIER_ATTR_MAX+1)] = {
|
||||||
|
[NL_CLASSIFIER_ATTR_TUPLE] = { .type = NLA_UNSPEC },
|
||||||
|
};
|
||||||
|
|
||||||
|
void nl_classifier_dump_nl_tuple(struct nl_classifier_tuple *tuple)
|
||||||
|
{
|
||||||
|
char ip_str[64];
|
||||||
|
|
||||||
|
printf("protocol = %s\n", (tuple->proto == IPPROTO_UDP) ? "udp" : ((tuple->proto == IPPROTO_TCP) ? "tcp" : "unknown"));
|
||||||
|
printf("source ip = %s\n", inet_ntop(tuple->af, &tuple->src_ip, ip_str, sizeof(ip_str)));
|
||||||
|
printf("destination ip = %s\n", inet_ntop(tuple->af, &tuple->dst_ip, ip_str, sizeof(ip_str)));
|
||||||
|
printf("source port = %d\n", ntohs(tuple->sport));
|
||||||
|
printf("destination port = %d\n", ntohs(tuple->dport));
|
||||||
|
}
|
||||||
|
|
||||||
|
int nl_classifier_msg_recv(struct nl_msg *msg, void *arg)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh = nlmsg_hdr(msg);
|
||||||
|
struct genlmsghdr *gnlh = nlmsg_data(nlh);
|
||||||
|
struct nlattr *attrs[(NL_CLASSIFIER_ATTR_MAX+1)];
|
||||||
|
|
||||||
|
genlmsg_parse(nlh, NL_CLASSIFIER_GENL_HDRSIZE, attrs, NL_CLASSIFIER_ATTR_MAX, nl_classifier_genl_policy);
|
||||||
|
|
||||||
|
switch (gnlh->cmd) {
|
||||||
|
case NL_CLASSIFIER_CMD_ACCEL_OK:
|
||||||
|
printf("Acceleration successful:\n");
|
||||||
|
nl_classifier_dump_nl_tuple(nla_data(attrs[NL_CLASSIFIER_ATTR_TUPLE]));
|
||||||
|
return NL_OK;
|
||||||
|
case NL_CLASSIFIER_CMD_CONNECTION_CLOSED:
|
||||||
|
printf("Connection is closed:\n");
|
||||||
|
nl_classifier_dump_nl_tuple(nla_data(attrs[NL_CLASSIFIER_ATTR_TUPLE]));
|
||||||
|
return NL_OK;
|
||||||
|
default:
|
||||||
|
printf("nl classifier received unknow message %d\n", gnlh->cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NL_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nl_classifier_offload(struct nl_classifier_instance *inst,
|
||||||
|
unsigned char proto, unsigned long *src_saddr,
|
||||||
|
unsigned long *dst_saddr, unsigned short sport,
|
||||||
|
unsigned short dport, int af)
|
||||||
|
{
|
||||||
|
struct nl_msg *msg;
|
||||||
|
int ret;
|
||||||
|
struct nl_classifier_tuple classifier_msg;
|
||||||
|
|
||||||
|
memset(&classifier_msg, 0, sizeof(classifier_msg));
|
||||||
|
classifier_msg.af = af;
|
||||||
|
classifier_msg.proto = proto;
|
||||||
|
memcpy(&classifier_msg.src_ip, src_saddr, (af == AF_INET ? 4 : 16));
|
||||||
|
memcpy(&classifier_msg.dst_ip, dst_saddr, (af == AF_INET ? 4 : 16));
|
||||||
|
classifier_msg.sport = sport;
|
||||||
|
classifier_msg.dport = dport;
|
||||||
|
|
||||||
|
msg = nlmsg_alloc();
|
||||||
|
if (!msg) {
|
||||||
|
printf("Unable to allocate message\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, inst->family_id,
|
||||||
|
NL_CLASSIFIER_GENL_HDRSIZE, NLM_F_REQUEST,
|
||||||
|
NL_CLASSIFIER_CMD_ACCEL, NL_CLASSIFIER_GENL_VERSION);
|
||||||
|
nla_put(msg, NL_CLASSIFIER_ATTR_TUPLE, sizeof(classifier_msg), &classifier_msg);
|
||||||
|
|
||||||
|
ret = nl_send_auto(inst->sock, msg);
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("send netlink message failed.\n");
|
||||||
|
nlmsg_free(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlmsg_free(msg);
|
||||||
|
printf("nl classifier offload connection successful\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int nl_classifier_init(struct nl_classifier_instance *inst)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
inst->sock = nl_socket_alloc();
|
||||||
|
if (!inst->sock) {
|
||||||
|
printf("Unable to allocation socket.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
genl_connect(inst->sock);
|
||||||
|
|
||||||
|
inst->family_id = genl_ctrl_resolve(inst->sock, NL_CLASSIFIER_GENL_FAMILY);
|
||||||
|
if (inst->family_id < 0) {
|
||||||
|
printf("Unable to resolve family %s\n", NL_CLASSIFIER_GENL_FAMILY);
|
||||||
|
goto init_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
inst->group_id = genl_ctrl_resolve_grp(inst->sock, NL_CLASSIFIER_GENL_FAMILY, NL_CLASSIFIER_GENL_GROUP);
|
||||||
|
if (inst->group_id < 0) {
|
||||||
|
printf("Unable to resolve mcast group %s\n", NL_CLASSIFIER_GENL_GROUP);
|
||||||
|
goto init_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = nl_socket_add_membership(inst->sock, inst->group_id);
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("Unable to add membership\n");
|
||||||
|
goto init_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
nl_socket_disable_seq_check(inst->sock);
|
||||||
|
nl_socket_modify_cb(inst->sock, NL_CB_VALID, NL_CB_CUSTOM, nl_classifier_msg_recv, NULL);
|
||||||
|
|
||||||
|
printf("nl classifier init successful\n");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
init_failed:
|
||||||
|
if (inst->sock) {
|
||||||
|
nl_close(inst->sock);
|
||||||
|
nl_socket_free(inst->sock);
|
||||||
|
inst->sock = NULL;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nl_classifier_exit(struct nl_classifier_instance *inst)
|
||||||
|
{
|
||||||
|
if (inst->sock) {
|
||||||
|
nl_close(inst->sock);
|
||||||
|
nl_socket_free(inst->sock);
|
||||||
|
inst->sock = NULL;
|
||||||
|
}
|
||||||
|
printf("nl classifier exit successful\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int nl_classifier_parse_arg(int argc, char *argv[], unsigned char *proto, unsigned long *src_saddr,
|
||||||
|
unsigned long *dst_saddr, unsigned short *sport, unsigned short *dport, int *af)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned short port;
|
||||||
|
|
||||||
|
if (argc < 7) {
|
||||||
|
printf("help: nl_classifier <v4|v6> <udp|tcp> <source ip> <destination ip> <source port> <destination port>\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == strncmp(argv[1], "v4", 2)) {
|
||||||
|
*af = AF_INET;
|
||||||
|
} else if (0 == strncmp(argv[1], "v6", 2)) {
|
||||||
|
*af = AF_INET6;
|
||||||
|
} else {
|
||||||
|
printf("Address family is not supported");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == strncmp(argv[2], "udp", 3)) {
|
||||||
|
*proto = IPPROTO_UDP;
|
||||||
|
} else if (0 == strncmp(argv[2], "tcp", 3)) {
|
||||||
|
*proto = IPPROTO_TCP;
|
||||||
|
} else {
|
||||||
|
printf("Protocol is not supported");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = inet_pton(*af, argv[3], src_saddr);
|
||||||
|
if (ret <= 0) {
|
||||||
|
printf("source ip has wrong format\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = inet_pton(*af, argv[4], dst_saddr);
|
||||||
|
if (ret <= 0) {
|
||||||
|
printf("destination ip has wrong format\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
port = strtol(argv[5], NULL, 0);
|
||||||
|
*sport = htons(port);
|
||||||
|
port = strtol(argv[6], NULL, 0);
|
||||||
|
*dport = htons(port);
|
||||||
|
|
||||||
|
printf("nl classifier parse arguments successful\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct nl_classifier_instance *inst = &nl_cls_inst;
|
||||||
|
unsigned char proto;
|
||||||
|
unsigned long src_addr[4];
|
||||||
|
unsigned long dst_addr[4];
|
||||||
|
unsigned short sport;
|
||||||
|
unsigned short dport;
|
||||||
|
int af;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = nl_classifier_parse_arg(argc, argv, &proto, src_addr, dst_addr, &sport, &dport, &af);
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("Failed to parse arguments\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = nl_classifier_init(inst);
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("Unable to init generic netlink\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
nl_classifier_offload(inst, proto, src_addr, dst_addr, sport, dport, af);
|
||||||
|
|
||||||
|
/* main loop to listen on message */
|
||||||
|
while (!inst->stop) {
|
||||||
|
nl_recvmsgs_default(inst->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
nl_classifier_exit(inst);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
232
fast-classifier/src/userspace_example.c
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013,2016 The Linux Foundation. All rights reserved.
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
* any purpose with or without fee is hereby granted, provided that the
|
||||||
|
* above copyright notice and this permission notice appear in all copies.
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink/genl/genl.h>
|
||||||
|
#include <netlink/genl/ctrl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <fast-classifier.h>
|
||||||
|
|
||||||
|
static struct nl_sock *sock;
|
||||||
|
static struct nl_sock *sock_event;
|
||||||
|
static int family;
|
||||||
|
static int grp_id;
|
||||||
|
|
||||||
|
static struct nla_policy fast_classifier_genl_policy[FAST_CLASSIFIER_A_MAX + 1] = {
|
||||||
|
[FAST_CLASSIFIER_A_TUPLE] = { .type = NLA_UNSPEC },
|
||||||
|
};
|
||||||
|
|
||||||
|
void dump_fc_tuple(struct fast_classifier_tuple *fc_msg)
|
||||||
|
{
|
||||||
|
char src_str[INET_ADDRSTRLEN];
|
||||||
|
char dst_str[INET_ADDRSTRLEN];
|
||||||
|
|
||||||
|
printf("TUPLE: %d, %s, %s, %d, %d"
|
||||||
|
" SMAC=%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
" DMAC=%02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||||
|
fc_msg->proto,
|
||||||
|
inet_ntop(AF_INET,
|
||||||
|
&fc_msg->src_saddr.in.s_addr,
|
||||||
|
src_str,
|
||||||
|
INET_ADDRSTRLEN),
|
||||||
|
inet_ntop(AF_INET,
|
||||||
|
&fc_msg->dst_saddr.in.s_addr,
|
||||||
|
dst_str,
|
||||||
|
INET_ADDRSTRLEN),
|
||||||
|
fc_msg->sport, fc_msg->dport,
|
||||||
|
fc_msg->smac[0], fc_msg->smac[1], fc_msg->smac[2],
|
||||||
|
fc_msg->smac[3], fc_msg->smac[4], fc_msg->smac[5],
|
||||||
|
fc_msg->dmac[0], fc_msg->dmac[1], fc_msg->dmac[2],
|
||||||
|
fc_msg->dmac[3], fc_msg->dmac[4], fc_msg->dmac[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_cb(struct nl_msg *msg, void *arg)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh = nlmsg_hdr(msg);
|
||||||
|
struct genlmsghdr *gnlh = nlmsg_data(nlh);
|
||||||
|
struct nlattr *attrs[FAST_CLASSIFIER_A_MAX];
|
||||||
|
|
||||||
|
genlmsg_parse(nlh, 0, attrs, FAST_CLASSIFIER_A_MAX, fast_classifier_genl_policy);
|
||||||
|
|
||||||
|
switch (gnlh->cmd) {
|
||||||
|
case FAST_CLASSIFIER_C_OFFLOADED:
|
||||||
|
printf("Got a offloaded message\n");
|
||||||
|
dump_fc_tuple(nla_data(attrs[FAST_CLASSIFIER_A_TUPLE]));
|
||||||
|
return NL_OK;
|
||||||
|
case FAST_CLASSIFIER_C_DONE:
|
||||||
|
printf("Got a done message\n");
|
||||||
|
dump_fc_tuple(nla_data(attrs[FAST_CLASSIFIER_A_TUPLE]));
|
||||||
|
return NL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NL_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fast_classifier_init(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
sock = nl_socket_alloc();
|
||||||
|
if (!sock) {
|
||||||
|
printf("Unable to allocation socket.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
genl_connect(sock);
|
||||||
|
|
||||||
|
sock_event = nl_socket_alloc();
|
||||||
|
if (!sock_event) {
|
||||||
|
nl_close(sock);
|
||||||
|
nl_socket_free(sock);
|
||||||
|
printf("Unable to allocation socket.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
genl_connect(sock_event);
|
||||||
|
|
||||||
|
family = genl_ctrl_resolve(sock, FAST_CLASSIFIER_GENL_NAME);
|
||||||
|
if (family < 0) {
|
||||||
|
nl_close(sock_event);
|
||||||
|
nl_close(sock);
|
||||||
|
nl_socket_free(sock);
|
||||||
|
nl_socket_free(sock_event);
|
||||||
|
printf("Unable to resolve family\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
grp_id = genl_ctrl_resolve_grp(sock, FAST_CLASSIFIER_GENL_NAME,
|
||||||
|
FAST_CLASSIFIER_GENL_MCGRP);
|
||||||
|
if (grp_id < 0) {
|
||||||
|
printf("Unable to resolve mcast group\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = nl_socket_add_membership(sock_event, grp_id);
|
||||||
|
if (err < 0) {
|
||||||
|
printf("Unable to add membership\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nl_socket_disable_seq_check(sock_event);
|
||||||
|
nl_socket_modify_cb(sock_event, NL_CB_VALID, NL_CB_CUSTOM, parse_cb, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fast_classifier_close(void)
|
||||||
|
{
|
||||||
|
nl_close(sock_event);
|
||||||
|
nl_close(sock);
|
||||||
|
nl_socket_free(sock_event);
|
||||||
|
nl_socket_free(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fast_classifier_ipv4_offload(unsigned char proto, unsigned long src_saddr,
|
||||||
|
unsigned long dst_saddr, unsigned short sport,
|
||||||
|
unsigned short dport)
|
||||||
|
{
|
||||||
|
struct nl_msg *msg;
|
||||||
|
int ret;
|
||||||
|
#ifdef DEBUG
|
||||||
|
char src_str[INET_ADDRSTRLEN];
|
||||||
|
char dst_str[INET_ADDRSTRLEN];
|
||||||
|
#endif
|
||||||
|
struct fast_classifier_tuple fc_msg;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("DEBUG: would offload: %d, %s, %s, %d, %d\n", proto,
|
||||||
|
inet_ntop(AF_INET, &src_saddr, src_str, INET_ADDRSTRLEN),
|
||||||
|
inet_ntop(AF_INET, &dst_saddr, dst_str, INET_ADDRSTRLEN),
|
||||||
|
sport, dport);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fc_msg.proto = proto;
|
||||||
|
fc_msg.src_saddr.in.s_addr = src_saddr;
|
||||||
|
fc_msg.dst_saddr.in.s_addr = dst_saddr;
|
||||||
|
fc_msg.sport = sport;
|
||||||
|
fc_msg.dport = dport;
|
||||||
|
fc_msg.smac[0] = 'a';
|
||||||
|
fc_msg.smac[1] = 'b';
|
||||||
|
fc_msg.smac[2] = 'c';
|
||||||
|
fc_msg.smac[3] = 'd';
|
||||||
|
fc_msg.smac[4] = 'e';
|
||||||
|
fc_msg.smac[5] = 'f';
|
||||||
|
fc_msg.dmac[0] = 'f';
|
||||||
|
fc_msg.dmac[1] = 'e';
|
||||||
|
fc_msg.dmac[2] = 'd';
|
||||||
|
fc_msg.dmac[3] = 'c';
|
||||||
|
fc_msg.dmac[4] = 'b';
|
||||||
|
fc_msg.dmac[5] = 'a';
|
||||||
|
|
||||||
|
if (fast_classifier_init() < 0) {
|
||||||
|
printf("Unable to init generic netlink\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = nlmsg_alloc();
|
||||||
|
if (!msg) {
|
||||||
|
nl_socket_free(sock);
|
||||||
|
printf("Unable to allocate message\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family,
|
||||||
|
FAST_CLASSIFIER_GENL_HDRSIZE, NLM_F_REQUEST,
|
||||||
|
FAST_CLASSIFIER_C_OFFLOAD, FAST_CLASSIFIER_GENL_VERSION);
|
||||||
|
nla_put(msg, 1, sizeof(fc_msg), &fc_msg);
|
||||||
|
|
||||||
|
ret = nl_send_auto_complete(sock, msg);
|
||||||
|
|
||||||
|
nlmsg_free(msg);
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("nlmsg_free failed");
|
||||||
|
nl_close(sock);
|
||||||
|
nl_socket_free(sock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = nl_wait_for_ack(sock);
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("wait for ack failed");
|
||||||
|
nl_close(sock);
|
||||||
|
nl_socket_free(sock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fast_classifier_listen_for_messages(void)
|
||||||
|
{
|
||||||
|
printf("waiting for netlink events\n");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
nl_recvmsgs_default(sock_event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (fast_classifier_init() < 0) {
|
||||||
|
printf("Unable to init generic netlink\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fast_classifier_ipv4_offload('a', 0, 0, 0, 0);
|
||||||
|
|
||||||
|
/* this never returns */
|
||||||
|
fast_classifier_listen_for_messages();
|
||||||
|
|
||||||
|
fast_classifier_close();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
19
luci-app-cpufreq/Makefile
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
# Copyright (C) 2016 Openwrt.org
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:=LuCI for CPU Freq Setting
|
||||||
|
LUCI_DEPENDS:=@(aarch64||arm)
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
|
||||||
|
PKG_NAME:=luci-app-cpufreq
|
||||||
|
PKG_VERSION:=1
|
||||||
|
PKG_RELEASE:=7
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
9
luci-app-cpufreq/luasrc/controller/cpufreq.lua
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module("luci.controller.cpufreq", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
if not nixio.fs.access("/etc/config/cpufreq") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
entry({"admin", "services", "cpufreq"}, cbi("cpufreq"), _("CPU Freq"), 900).dependent = true
|
||||||
|
end
|
60
luci-app-cpufreq/luasrc/model/cbi/cpufreq.lua
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
|
||||||
|
cpu_freqs = fs.readfile("/sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies") or "100000"
|
||||||
|
cpu_freqs = string.sub(cpu_freqs, 1, -3)
|
||||||
|
|
||||||
|
cpu_governors = fs.readfile("/sys/devices/system/cpu/cpufreq/policy0/scaling_available_governors") or "performance"
|
||||||
|
cpu_governors = string.sub(cpu_governors, 1, -3)
|
||||||
|
|
||||||
|
function string.split(input, delimiter)
|
||||||
|
input = tostring(input)
|
||||||
|
delimiter = tostring(delimiter)
|
||||||
|
if (delimiter=='') then return false end
|
||||||
|
local pos,arr = 0, {}
|
||||||
|
for st,sp in function() return string.find(input, delimiter, pos, true) end do
|
||||||
|
table.insert(arr, string.sub(input, pos, st - 1))
|
||||||
|
pos = sp + 1
|
||||||
|
end
|
||||||
|
table.insert(arr, string.sub(input, pos))
|
||||||
|
return arr
|
||||||
|
end
|
||||||
|
|
||||||
|
freq_array = string.split(cpu_freqs, " ")
|
||||||
|
governor_array = string.split(cpu_governors, " ")
|
||||||
|
|
||||||
|
mp = Map("cpufreq", translate("CPU Freq Settings"))
|
||||||
|
mp.description = translate("Set CPU Scaling Governor to Max Performance or Balance Mode")
|
||||||
|
|
||||||
|
s = mp:section(NamedSection, "cpufreq", "settings")
|
||||||
|
s.anonymouse = true
|
||||||
|
|
||||||
|
governor = s:option(ListValue, "governor", translate("CPU Scaling Governor"))
|
||||||
|
for _, e in ipairs(governor_array) do
|
||||||
|
if e ~= "" then governor:value(e, translate(e,string.upper(e))) end
|
||||||
|
end
|
||||||
|
|
||||||
|
minfreq = s:option(ListValue, "minifreq", translate("Min Idle CPU Freq"))
|
||||||
|
for _, e in ipairs(freq_array) do
|
||||||
|
if e ~= "" then minfreq:value(e) end
|
||||||
|
end
|
||||||
|
|
||||||
|
maxfreq = s:option(ListValue, "maxfreq", translate("Max Turbo Boost CPU Freq"))
|
||||||
|
for _, e in ipairs(freq_array) do
|
||||||
|
if e ~= "" then maxfreq:value(e) end
|
||||||
|
end
|
||||||
|
|
||||||
|
upthreshold = s:option(Value, "upthreshold", translate("CPU Switching Threshold"))
|
||||||
|
upthreshold.datatype="range(1,99)"
|
||||||
|
upthreshold.rmempty = false
|
||||||
|
upthreshold.description = translate("Kernel make a decision on whether it should increase the frequency (%)")
|
||||||
|
upthreshold.placeholder = 50
|
||||||
|
upthreshold.default = 50
|
||||||
|
|
||||||
|
factor = s:option(Value, "factor", translate("CPU Switching Sampling rate"))
|
||||||
|
factor.datatype="range(1,100000)"
|
||||||
|
factor.rmempty = false
|
||||||
|
factor.description = translate("The sampling rate determines how frequently the governor checks to tune the CPU (ms)")
|
||||||
|
factor.placeholder = 10
|
||||||
|
factor.default = 10
|
||||||
|
|
||||||
|
return mp
|
57
luci-app-cpufreq/po/zh-cn/cpufreq.po
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: \n"
|
||||||
|
"POT-Creation-Date: \n"
|
||||||
|
"PO-Revision-Date: \n"
|
||||||
|
"Last-Translator: dingpengyu <dingpengyu06@gmail.com>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Language: zh_CN\n"
|
||||||
|
"X-Generator: Poedit 2.3.1\n"
|
||||||
|
|
||||||
|
msgid "CPU Freq"
|
||||||
|
msgstr "CPU 性能优化调节"
|
||||||
|
|
||||||
|
msgid "CPU Freq Settings"
|
||||||
|
msgstr "CPU 性能优化调节设置"
|
||||||
|
|
||||||
|
msgid "Set CPU Scaling Governor to Max Performance or Balance Mode"
|
||||||
|
msgstr "设置路由器的 CPU 性能模式(高性能/均衡省电)"
|
||||||
|
|
||||||
|
msgid "CPU Scaling Governor"
|
||||||
|
msgstr "CPU 工作模式"
|
||||||
|
|
||||||
|
msgid "Ondemand Balance Mode"
|
||||||
|
msgstr "Ondemand 自动平衡模式"
|
||||||
|
|
||||||
|
msgid "Performance Mode"
|
||||||
|
msgstr "Performance 高性能模式(降低游戏转发延迟)"
|
||||||
|
|
||||||
|
msgid "ondemand"
|
||||||
|
msgstr "ondemand 自动平衡模式"
|
||||||
|
|
||||||
|
msgid "performance"
|
||||||
|
msgstr "performance 高性能模式(降低游戏转发延迟)"
|
||||||
|
|
||||||
|
msgid "CPU Freq from 48000 to 716000 (Khz)"
|
||||||
|
msgstr "CPU 频率范围为 48000 到 716000 (Khz)"
|
||||||
|
|
||||||
|
msgid "Min Idle CPU Freq"
|
||||||
|
msgstr "待机 CPU 最小频率"
|
||||||
|
|
||||||
|
msgid "Max Turbo Boost CPU Freq"
|
||||||
|
msgstr "最大 Turbo Boost CPU 频率"
|
||||||
|
|
||||||
|
msgid "CPU Switching Threshold"
|
||||||
|
msgstr "CPU 切换频率触发阈值"
|
||||||
|
|
||||||
|
msgid "Kernel make a decision on whether it should increase the frequency (%)"
|
||||||
|
msgstr "当 CPU 占用率超过 (%) 的情况下触发内核切换频率"
|
||||||
|
|
||||||
|
msgid "CPU Switching Sampling rate"
|
||||||
|
msgstr "CPU 切换周期"
|
||||||
|
|
||||||
|
msgid "The sampling rate determines how frequently the governor checks to tune the CPU (ms)"
|
||||||
|
msgstr "CPU 检查切换的周期 (ms) 。注意:过于频繁的切换频率会引起网络延迟抖动"
|
8
luci-app-cpufreq/root/etc/config/cpufreq
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
config settings 'cpufreq'
|
||||||
|
option maxfreq '716000'
|
||||||
|
option upthreshold '50'
|
||||||
|
option factor '10'
|
||||||
|
option minifreq '300000'
|
||||||
|
option governor 'ondemand'
|
||||||
|
|
30
luci-app-cpufreq/root/etc/init.d/cpufreq
Executable file
|
@ -0,0 +1,30 @@
|
||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
START=50
|
||||||
|
|
||||||
|
NAME=cpufreq
|
||||||
|
|
||||||
|
uci_get_by_type() {
|
||||||
|
local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null)
|
||||||
|
echo ${ret:=$3}
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
{
|
||||||
|
local min=$(cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies | awk -F ' ' '{print $1 }')
|
||||||
|
local max=$(cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies | awk -F ' ' '{print $NF }')
|
||||||
|
|
||||||
|
config_load cpufreq
|
||||||
|
local governor=$(uci_get_by_type settings governor schedutil)
|
||||||
|
local minifreq=$(uci_get_by_type settings minifreq $min)
|
||||||
|
local maxfreq=$(uci_get_by_type settings maxfreq $max)
|
||||||
|
local upthreshold=$(uci_get_by_type settings upthreshold 50)
|
||||||
|
local factor=$(uci_get_by_type settings factor 10)
|
||||||
|
|
||||||
|
echo $governor > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor
|
||||||
|
echo $minifreq > /sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq
|
||||||
|
echo $maxfreq > /sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq
|
||||||
|
if [ "$governor" == "ondemand" ]; then
|
||||||
|
echo $upthreshold > /sys/devices/system/cpu/cpufreq/ondemand/up_threshold
|
||||||
|
echo $factor > /sys/devices/system/cpu/cpufreq/ondemand/sampling_down_factor
|
||||||
|
fi
|
||||||
|
}
|
11
luci-app-cpufreq/root/etc/uci-defaults/luci-cpufreq
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
delete ucitrack.@cpufreq[-1]
|
||||||
|
add ucitrack cpufreq
|
||||||
|
set ucitrack.@cpufreq[-1].init=cpufreq
|
||||||
|
commit ucitrack
|
||||||
|
EOF
|
||||||
|
|
||||||
|
rm -f /tmp/luci-indexcache
|
||||||
|
exit 0
|
53
luci-app-diskman/Makefile
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=luci-app-diskman
|
||||||
|
PKG_VERSION:=v0.2.11
|
||||||
|
PKG_RELEASE:=2
|
||||||
|
|
||||||
|
PKG_MAINTAINER:=lisaac <lisaac.cn@gmail.com>
|
||||||
|
PKG_LICENSE:=AGPL-3.0
|
||||||
|
|
||||||
|
LUCI_TITLE:=Disk Manager interface for LuCI
|
||||||
|
LUCI_DEPENDS:=+blkid +e2fsprogs +parted +smartmontools \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_btrfs_progs:btrfs-progs \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_lsblk:lsblk \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_mdadm:mdadm \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_kmod_md_raid456:mdadm \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_kmod_md_raid456:kmod-md-raid456 \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_kmod_md_linears:mdadm \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_kmod_md_linears:kmod-md-linear
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/config
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_btrfs_progs
|
||||||
|
bool "Include btrfs-progs"
|
||||||
|
default y
|
||||||
|
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_lsblk
|
||||||
|
bool "Include lsblk"
|
||||||
|
default y
|
||||||
|
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_mdadm
|
||||||
|
bool "Include mdadm"
|
||||||
|
default n
|
||||||
|
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_kmod_md_raid456
|
||||||
|
depends on PACKAGE_$(PKG_NAME)_INCLUDE_mdadm
|
||||||
|
bool "Include kmod-md-raid456"
|
||||||
|
default n
|
||||||
|
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_kmod_md_linear
|
||||||
|
depends on PACKAGE_$(PKG_NAME)_INCLUDE_mdadm
|
||||||
|
bool "Include kmod-md-linear"
|
||||||
|
default n
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/postinst
|
||||||
|
#!/bin/sh
|
||||||
|
rm -fr /tmp/luci-indexcache /tmp/luci-modulecache
|
||||||
|
endef
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
155
luci-app-diskman/luasrc/controller/diskman.lua
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
|
||||||
|
]]--
|
||||||
|
|
||||||
|
require "luci.util"
|
||||||
|
module("luci.controller.diskman",package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
-- check all used executables in disk management are existed
|
||||||
|
local CMD = {"parted", "blkid", "smartctl"}
|
||||||
|
local executables_all_existed = true
|
||||||
|
for _, cmd in ipairs(CMD) do
|
||||||
|
local command = luci.sys.exec("/usr/bin/which " .. cmd)
|
||||||
|
if not command:match(cmd) then
|
||||||
|
executables_all_existed = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not executables_all_existed then return end
|
||||||
|
-- entry(path, target, title, order)
|
||||||
|
-- set leaf attr to true to pass argument throughe url (e.g. admin/system/disk/partition/sda)
|
||||||
|
entry({"admin", "system", "diskman"}, alias("admin", "system", "diskman", "disks"), _("Disk Man"), 55)
|
||||||
|
entry({"admin", "system", "diskman", "disks"}, form("diskman/disks"), nil).leaf = true
|
||||||
|
entry({"admin", "system", "diskman", "partition"}, form("diskman/partition"), nil).leaf = true
|
||||||
|
entry({"admin", "system", "diskman", "btrfs"}, form("diskman/btrfs"), nil).leaf = true
|
||||||
|
entry({"admin", "system", "diskman", "format_partition"}, call("format_partition"), nil).leaf = true
|
||||||
|
entry({"admin", "system", "diskman", "get_disk_info"}, call("get_disk_info"), nil).leaf = true
|
||||||
|
entry({"admin", "system", "diskman", "mk_p_table"}, call("mk_p_table"), nil).leaf = true
|
||||||
|
entry({"admin", "system", "diskman", "smartdetail"}, call("smart_detail"), nil).leaf = true
|
||||||
|
entry({"admin", "system", "diskman", "smartattr"}, call("smart_attr"), nil).leaf = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function format_partition()
|
||||||
|
local partation_name = luci.http.formvalue("partation_name")
|
||||||
|
local fs = luci.http.formvalue("file_system")
|
||||||
|
if not partation_name then
|
||||||
|
luci.http.status(500, "Partition NOT found!")
|
||||||
|
luci.http.write_json("Partition NOT found!")
|
||||||
|
return
|
||||||
|
elseif not nixio.fs.access("/dev/"..partation_name) then
|
||||||
|
luci.http.status(500, "Partition NOT found!")
|
||||||
|
luci.http.write_json("Partition NOT found!")
|
||||||
|
return
|
||||||
|
elseif not fs then
|
||||||
|
luci.http.status(500, "no file system")
|
||||||
|
luci.http.write_json("no file system")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local dm = require "luci.model.diskman"
|
||||||
|
code, msg = dm.format_partition(partation_name, fs)
|
||||||
|
luci.http.status(code, msg)
|
||||||
|
luci.http.write_json(msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_disk_info(dev)
|
||||||
|
if not dev then
|
||||||
|
luci.http.status(500, "no device")
|
||||||
|
luci.http.write_json("no device")
|
||||||
|
return
|
||||||
|
elseif not nixio.fs.access("/dev/"..dev) then
|
||||||
|
luci.http.status(500, "no device")
|
||||||
|
luci.http.write_json("no device")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local dm = require "luci.model.diskman"
|
||||||
|
local device_info = dm.get_disk_info(dev)
|
||||||
|
luci.http.status(200, "ok")
|
||||||
|
luci.http.prepare_content("application/json")
|
||||||
|
luci.http.write_json(device_info)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mk_p_table()
|
||||||
|
local p_table = luci.http.formvalue("p_table")
|
||||||
|
local dev = luci.http.formvalue("dev")
|
||||||
|
if not dev then
|
||||||
|
luci.http.status(500, "no device")
|
||||||
|
luci.http.write_json("no device")
|
||||||
|
return
|
||||||
|
elseif not nixio.fs.access("/dev/"..dev) then
|
||||||
|
luci.http.status(500, "no device")
|
||||||
|
luci.http.write_json("no device")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local dm = require "luci.model.diskman"
|
||||||
|
if p_table == "GPT" or p_table == "MBR" then
|
||||||
|
p_table = p_table == "MBR" and "msdos" or "gpt"
|
||||||
|
local res = luci.sys.call(dm.command.parted .. " -s /dev/" .. dev .. " mktable ".. p_table)
|
||||||
|
if res == 0 then
|
||||||
|
luci.http.status(200, "ok")
|
||||||
|
else
|
||||||
|
luci.http.status(500, "command exec error")
|
||||||
|
end
|
||||||
|
luci.http.prepare_content("application/json")
|
||||||
|
luci.http.write_json({code=res})
|
||||||
|
else
|
||||||
|
luci.http.status(404, "not support")
|
||||||
|
luci.http.prepare_content("application/json")
|
||||||
|
luci.http.write_json({code="1"})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function smart_detail(dev)
|
||||||
|
luci.template.render("diskman/smart_detail", {dev=dev})
|
||||||
|
end
|
||||||
|
|
||||||
|
function smart_attr(dev)
|
||||||
|
local dm = require "luci.model.diskman"
|
||||||
|
local cmd = io.popen(dm.command.smartctl .. " -H -A -i /dev/%s" % dev)
|
||||||
|
if cmd then
|
||||||
|
local attr = { }
|
||||||
|
if cmd:match("NVMe Version:")then
|
||||||
|
while true do
|
||||||
|
local ln = cmd:read("*l")
|
||||||
|
if not ln then
|
||||||
|
break
|
||||||
|
elseif ln:match("^(.-):%s+(.+)") then
|
||||||
|
local key, value = ln:match("^(.-):%s+(.+)")
|
||||||
|
attr[#attr+1]= {
|
||||||
|
key = key,
|
||||||
|
value = value
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
while true do
|
||||||
|
local ln = cmd:read("*l")
|
||||||
|
if not ln then
|
||||||
|
break
|
||||||
|
elseif ln:match("^.*%d+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+") then
|
||||||
|
local id,attrbute,flag,value,worst,thresh,type,updated,raw = ln:match("^%s*(%d+)%s+([%a%p]+)%s+(%w+)%s+(%d+)%s+(%d+)%s+(%d+)%s+([%a%p]+)%s+(%a+)%s+[%w%p]+%s+(.+)")
|
||||||
|
id= "%x" % id
|
||||||
|
if not id:match("^%w%w") then
|
||||||
|
id = "0%s" % id
|
||||||
|
end
|
||||||
|
attr[#attr+1]= {
|
||||||
|
id = id:upper(),
|
||||||
|
attrbute = attrbute,
|
||||||
|
flag = flag,
|
||||||
|
value = value,
|
||||||
|
worst = worst,
|
||||||
|
thresh = thresh,
|
||||||
|
type = type,
|
||||||
|
updated = updated,
|
||||||
|
raw = raw
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
cmd:close()
|
||||||
|
luci.http.prepare_content("application/json")
|
||||||
|
luci.http.write_json(attr)
|
||||||
|
end
|
||||||
|
end
|
210
luci-app-diskman/luasrc/model/cbi/diskman/btrfs.lua
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
|
||||||
|
]]--
|
||||||
|
|
||||||
|
require "luci.util"
|
||||||
|
require("luci.tools.webadmin")
|
||||||
|
local dm = require "luci.model.diskman"
|
||||||
|
local uuid = arg[1]
|
||||||
|
|
||||||
|
if not uuid then luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman")) end
|
||||||
|
|
||||||
|
-- mount subv=/ to tempfs
|
||||||
|
mount_point = "/tmp/.btrfs_tmp"
|
||||||
|
nixio.fs.mkdirr(mount_point)
|
||||||
|
luci.util.exec(dm.command.umount .. " "..mount_point .. " >/dev/null 2>&1")
|
||||||
|
luci.util.exec(dm.command.mount .. " -t btrfs -o subvol=/ UUID="..uuid.." "..mount_point)
|
||||||
|
|
||||||
|
m = SimpleForm("btrfs", translate("Btrfs"), translate("Manage Btrfs"))
|
||||||
|
m.template = "diskman/cbi/xsimpleform"
|
||||||
|
m.redirect = luci.dispatcher.build_url("admin/system/diskman")
|
||||||
|
m.submit = false
|
||||||
|
m.reset = false
|
||||||
|
|
||||||
|
-- info
|
||||||
|
local btrfs_info = dm.get_btrfs_info(mount_point)
|
||||||
|
local table_btrfs_info = m:section(Table, {btrfs_info}, translate("Btrfs Info"))
|
||||||
|
table_btrfs_info:option(DummyValue, "uuid", translate("UUID"))
|
||||||
|
table_btrfs_info:option(DummyValue, "members", translate("Members"))
|
||||||
|
table_btrfs_info:option(DummyValue, "data_raid_level", translate("Data"))
|
||||||
|
table_btrfs_info:option(DummyValue, "metadata_raid_lavel", translate("Metadata"))
|
||||||
|
table_btrfs_info:option(DummyValue, "size_formated", translate("Size"))
|
||||||
|
table_btrfs_info:option(DummyValue, "used_formated", translate("Used"))
|
||||||
|
table_btrfs_info:option(DummyValue, "free_formated", translate("Free Space"))
|
||||||
|
table_btrfs_info:option(DummyValue, "usage", translate("Usage"))
|
||||||
|
local v_btrfs_label = table_btrfs_info:option(Value, "label", translate("Label"))
|
||||||
|
local value_btrfs_label = ""
|
||||||
|
v_btrfs_label.write = function(self, section, value)
|
||||||
|
value_btrfs_label = value or ""
|
||||||
|
end
|
||||||
|
local btn_update_label = table_btrfs_info:option(Button, "_update_label")
|
||||||
|
btn_update_label.inputtitle = translate("Update")
|
||||||
|
btn_update_label.inputstyle = "edit"
|
||||||
|
btn_update_label.write = function(self, section, value)
|
||||||
|
local cmd = dm.command.btrfs .. " filesystem label " .. mount_point .. " " .. value_btrfs_label
|
||||||
|
local res = luci.util.exec(cmd)
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
|
||||||
|
end
|
||||||
|
-- subvolume
|
||||||
|
local subvolume_list = dm.get_btrfs_subv(mount_point)
|
||||||
|
subvolume_list["_"] = { ID = 0 }
|
||||||
|
table_subvolume = m:section(Table, subvolume_list, translate("SubVolumes"))
|
||||||
|
table_subvolume:option(DummyValue, "id", translate("ID"))
|
||||||
|
table_subvolume:option(DummyValue, "top_level", translate("Top Level"))
|
||||||
|
table_subvolume:option(DummyValue, "uuid", translate("UUID"))
|
||||||
|
table_subvolume:option(DummyValue, "otime", translate("Otime"))
|
||||||
|
table_subvolume:option(DummyValue, "snapshots", translate("Snapshots"))
|
||||||
|
local v_path = table_subvolume:option(Value, "path", translate("Path"))
|
||||||
|
v_path.forcewrite = true
|
||||||
|
v_path.render = function(self, section, scope)
|
||||||
|
if subvolume_list[section].ID == 0 then
|
||||||
|
self.template = "cbi/value"
|
||||||
|
self.placeholder = "/my_subvolume"
|
||||||
|
self.forcewrite = true
|
||||||
|
Value.render(self, section, scope)
|
||||||
|
else
|
||||||
|
self.template = "cbi/dvalue"
|
||||||
|
DummyValue.render(self, section, scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local value_path
|
||||||
|
v_path.write = function(self, section, value)
|
||||||
|
value_path = value
|
||||||
|
end
|
||||||
|
local btn_set_default = table_subvolume:option(Button, "_subv_set_default", translate("Set Default"))
|
||||||
|
btn_set_default.forcewrite = true
|
||||||
|
btn_set_default.inputstyle = "edit"
|
||||||
|
btn_set_default.template = "diskman/cbi/disabled_button"
|
||||||
|
btn_set_default.render = function(self, section, scope)
|
||||||
|
if subvolume_list[section].default_subvolume then
|
||||||
|
self.view_disabled = true
|
||||||
|
self.inputtitle = translate("Set Default")
|
||||||
|
elseif subvolume_list[section].ID == 0 then
|
||||||
|
self.template = "cbi/dvalue"
|
||||||
|
else
|
||||||
|
self.inputtitle = translate("Set Default")
|
||||||
|
self.view_disabled = false
|
||||||
|
end
|
||||||
|
Button.render(self, section, scope)
|
||||||
|
end
|
||||||
|
btn_set_default.write = function(self, section, value)
|
||||||
|
local cmd
|
||||||
|
if value == translate("Set Default") then
|
||||||
|
cmd = dm.command.btrfs .. " subvolume set-default " .. mount_point..subvolume_list[section].path
|
||||||
|
else
|
||||||
|
cmd = dm.command.btrfs .. " subvolume set-default " .. mount_point.."/"
|
||||||
|
end
|
||||||
|
local res = luci.util.exec(cmd.. " 2>&1")
|
||||||
|
if res and (res:match("ERR") or res:match("not enough arguments")) then
|
||||||
|
m.errmessage = res
|
||||||
|
else
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local btn_remove = table_subvolume:option(Button, "_subv_remove")
|
||||||
|
btn_remove.template = "diskman/cbi/disabled_button"
|
||||||
|
btn_remove.forcewrite = true
|
||||||
|
btn_remove.render = function(self, section, scope)
|
||||||
|
if subvolume_list[section].ID == 0 then
|
||||||
|
btn_remove.inputtitle = translate("Create")
|
||||||
|
btn_remove.inputstyle = "add"
|
||||||
|
self.view_disabled = false
|
||||||
|
elseif subvolume_list[section].path == "/" or subvolume_list[section].default_subvolume then
|
||||||
|
btn_remove.inputtitle = translate("Delete")
|
||||||
|
btn_remove.inputstyle = "remove"
|
||||||
|
self.view_disabled = true
|
||||||
|
else
|
||||||
|
btn_remove.inputtitle = translate("Delete")
|
||||||
|
btn_remove.inputstyle = "remove"
|
||||||
|
self.view_disabled = false
|
||||||
|
end
|
||||||
|
Button.render(self, section, scope)
|
||||||
|
end
|
||||||
|
|
||||||
|
btn_remove.write = function(self, section, value)
|
||||||
|
local cmd
|
||||||
|
if value == translate("Delete") then
|
||||||
|
cmd = dm.command.btrfs .. " subvolume delete " .. mount_point .. subvolume_list[section].path
|
||||||
|
elseif value == translate("Create") then
|
||||||
|
if value_path and value_path:match("^/") then
|
||||||
|
cmd = dm.command.btrfs .. " subvolume create " .. mount_point .. value_path
|
||||||
|
else
|
||||||
|
m.errmessage = translate("Please input Subvolume Path, Subvolume must start with '/'")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local res = luci.util.exec(cmd.. " 2>&1")
|
||||||
|
if res and (res:match("ERR") or res:match("not enough arguments")) then
|
||||||
|
m.errmessage = luci.util.pcdata(res)
|
||||||
|
else
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- snapshot
|
||||||
|
-- local snapshot_list = dm.get_btrfs_subv(mount_point, 1)
|
||||||
|
-- table_snapshot = m:section(Table, snapshot_list, translate("Snapshots"))
|
||||||
|
-- table_snapshot:option(DummyValue, "id", translate("ID"))
|
||||||
|
-- table_snapshot:option(DummyValue, "top_level", translate("Top Level"))
|
||||||
|
-- table_snapshot:option(DummyValue, "uuid", translate("UUID"))
|
||||||
|
-- table_snapshot:option(DummyValue, "otime", translate("Otime"))
|
||||||
|
-- table_snapshot:option(DummyValue, "path", translate("Path"))
|
||||||
|
-- local snp_remove = table_snapshot:option(Button, "_snp_remove")
|
||||||
|
-- snp_remove.inputtitle = translate("Delete")
|
||||||
|
-- snp_remove.inputstyle = "remove"
|
||||||
|
-- snp_remove.write = function(self, section, value)
|
||||||
|
-- local cmd = dm.command.btrfs .. " subvolume delete " .. mount_point .. snapshot_list[section].path
|
||||||
|
-- local res = luci.util.exec(cmd.. " 2>&1")
|
||||||
|
-- if res and (res:match("ERR") or res:match("not enough arguments")) then
|
||||||
|
-- m.errmessage = luci.util.pcdata(res)
|
||||||
|
-- else
|
||||||
|
-- luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- new snapshots
|
||||||
|
local s_snapshot = m:section(SimpleSection, translate("New Snapshot"))
|
||||||
|
local value_sorce, value_dest, value_readonly
|
||||||
|
local v_sorce = s_snapshot:option(Value, "_source", translate("Source Path"), translate("The source path for create the snapshot"))
|
||||||
|
v_sorce.placeholder = "/data"
|
||||||
|
v_sorce.forcewrite = true
|
||||||
|
v_sorce.write = function(self, section, value)
|
||||||
|
value_sorce = value
|
||||||
|
end
|
||||||
|
|
||||||
|
local v_readonly = s_snapshot:option(Flag, "_readonly", translate("Readonly"), translate("The path where you want to store the snapshot"))
|
||||||
|
v_readonly.forcewrite = true
|
||||||
|
v_readonly.rmempty = false
|
||||||
|
v_readonly.disabled = 0
|
||||||
|
v_readonly.enabled = 1
|
||||||
|
v_readonly.default = 1
|
||||||
|
v_readonly.write = function(self, section, value)
|
||||||
|
value_readonly = value
|
||||||
|
end
|
||||||
|
local v_dest = s_snapshot:option(Value, "_dest", translate("Destination Path (optional)"))
|
||||||
|
v_dest.forcewrite = true
|
||||||
|
v_dest.placeholder = "/.snapshot/202002051538"
|
||||||
|
v_dest.write = function(self, section, value)
|
||||||
|
value_dest = value
|
||||||
|
end
|
||||||
|
local btn_snp_create = s_snapshot:option(Button, "_snp_create")
|
||||||
|
btn_snp_create.title = " "
|
||||||
|
btn_snp_create.inputtitle = translate("New Snapshot")
|
||||||
|
btn_snp_create.inputstyle = "add"
|
||||||
|
btn_snp_create.write = function(self, section, value)
|
||||||
|
if value_sorce and value_sorce:match("^/") then
|
||||||
|
if not value_dest then value_dest = "/.snapshot"..value_sorce.."/"..os.date("%Y%m%d%H%M%S") end
|
||||||
|
nixio.fs.mkdirr(mount_point..value_dest:match("(.-)[^/]+$"))
|
||||||
|
local cmd = dm.command.btrfs .. " subvolume snapshot" .. (value_readonly == 1 and " -r " or " ") .. mount_point..value_sorce .. " " .. mount_point..value_dest
|
||||||
|
local res = luci.util.exec(cmd .. " 2>&1")
|
||||||
|
if res and (res:match("ERR") or res:match("not enough arguments")) then
|
||||||
|
m.errmessage = luci.util.pcdata(res)
|
||||||
|
else
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
m.errmessage = translate("Please input Source Path of snapshot, Source Path must start with '/'")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return m
|
327
luci-app-diskman/luasrc/model/cbi/diskman/disks.lua
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
|
||||||
|
]]--
|
||||||
|
|
||||||
|
require "luci.util"
|
||||||
|
require("luci.tools.webadmin")
|
||||||
|
local dm = require "luci.model.diskman"
|
||||||
|
|
||||||
|
-- Use (non-UCI) SimpleForm since we have no related config file
|
||||||
|
m = SimpleForm("diskman", translate("DiskMan"), translate("Manage Disks over LuCI."))
|
||||||
|
m.template = "diskman/cbi/xsimpleform"
|
||||||
|
m:append(Template("diskman/disk_info"))
|
||||||
|
-- disable submit and reset button
|
||||||
|
m.submit = false
|
||||||
|
m.reset = false
|
||||||
|
-- rescan disks
|
||||||
|
rescan = m:section(SimpleSection)
|
||||||
|
rescan_button = rescan:option(Button, "_rescan")
|
||||||
|
rescan_button.inputtitle= translate("Rescan Disks")
|
||||||
|
rescan_button.template = "diskman/cbi/inlinebutton"
|
||||||
|
rescan_button.inputstyle = "add"
|
||||||
|
rescan_button.forcewrite = true
|
||||||
|
rescan_button.write = function(self, section, value)
|
||||||
|
luci.util.exec("echo '- - -' | tee /sys/class/scsi_host/host*/scan > /dev/null")
|
||||||
|
if dm.command.mdadm then
|
||||||
|
luci.util.exec(dm.command.mdadm .. " --assemble --scan")
|
||||||
|
end
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- disks
|
||||||
|
local disks = dm.list_devices()
|
||||||
|
d = m:section(Table, disks, translate("Disks"))
|
||||||
|
d.config = "disk"
|
||||||
|
-- option(type, id(key of table), text)
|
||||||
|
d:option(DummyValue, "path", translate("Path"))
|
||||||
|
d:option(DummyValue, "model", translate("Model"))
|
||||||
|
d:option(DummyValue, "sn", translate("Serial Number"))
|
||||||
|
d:option(DummyValue, "size_formated", translate("Size"))
|
||||||
|
d:option(DummyValue, "temp", translate("Temp"))
|
||||||
|
-- d:option(DummyValue, "sec_size", translate("Sector Size "))
|
||||||
|
d:option(DummyValue, "p_table", translate("Partition Table"))
|
||||||
|
d:option(DummyValue, "sata_ver", translate("SATA Version"))
|
||||||
|
-- d:option(DummyValue, "rota_rate", translate("Rotation Rate"))
|
||||||
|
d:option(DummyValue, "health", translate("Health"))
|
||||||
|
d:option(DummyValue, "status", translate("Status"))
|
||||||
|
|
||||||
|
d.extedit = luci.dispatcher.build_url("admin/system/diskman/partition/%s")
|
||||||
|
|
||||||
|
-- raid devices
|
||||||
|
if dm.command.mdadm then
|
||||||
|
local raid_devices = dm.list_raid_devices()
|
||||||
|
-- raid_devices = diskmanager.getRAIDdevices()
|
||||||
|
if next(raid_devices) ~= nil then
|
||||||
|
local r = m:section(Table, raid_devices, translate("RAID Devices"))
|
||||||
|
r.config = "_raid"
|
||||||
|
r:option(DummyValue, "path", translate("Path"))
|
||||||
|
r:option(DummyValue, "level", translate("RAID mode"))
|
||||||
|
r:option(DummyValue, "size_formated", translate("Size"))
|
||||||
|
r:option(DummyValue, "p_table", translate("Partition Table"))
|
||||||
|
r:option(DummyValue, "status", translate("Status"))
|
||||||
|
r:option(DummyValue, "members_str", translate("Members"))
|
||||||
|
r:option(DummyValue, "active", translate("Active"))
|
||||||
|
r.extedit = luci.dispatcher.build_url("admin/system/diskman/partition/%s")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- btrfs devices
|
||||||
|
if dm.command.btrfs then
|
||||||
|
btrfs_devices = dm.list_btrfs_devices()
|
||||||
|
if next(btrfs_devices) ~= nil then
|
||||||
|
local table_btrfs = m:section(Table, btrfs_devices, translate("Btrfs"))
|
||||||
|
table_btrfs:option(DummyValue, "uuid", translate("UUID"))
|
||||||
|
table_btrfs:option(DummyValue, "label", translate("Label"))
|
||||||
|
table_btrfs:option(DummyValue, "members", translate("Members"))
|
||||||
|
-- sieze is error, since there is RAID
|
||||||
|
-- table_btrfs:option(DummyValue, "size_formated", translate("Size"))
|
||||||
|
table_btrfs:option(DummyValue, "used_formated", translate("Usage"))
|
||||||
|
table_btrfs.extedit = luci.dispatcher.build_url("admin/system/diskman/btrfs/%s")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- mount point
|
||||||
|
local mount_point = dm.get_mount_points()
|
||||||
|
local _mount_point = {}
|
||||||
|
table.insert( mount_point, { device = 0 } )
|
||||||
|
local table_mp = m:section(Table, mount_point, translate("Mount Point"))
|
||||||
|
local v_device = table_mp:option(Value, "device", translate("Device"))
|
||||||
|
v_device.render = function(self, section, scope)
|
||||||
|
if mount_point[section].device == 0 then
|
||||||
|
self.template = "cbi/value"
|
||||||
|
self.forcewrite = true
|
||||||
|
for dev, info in pairs(disks) do
|
||||||
|
for i, v in ipairs(info.partitions) do
|
||||||
|
self:value("/dev/".. v.name, "/dev/".. v.name .. " ".. v.size_formated)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Value.render(self, section, scope)
|
||||||
|
else
|
||||||
|
self.template = "cbi/dvalue"
|
||||||
|
DummyValue.render(self, section, scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
v_device.write = function(self, section, value)
|
||||||
|
_mount_point.device = value and value:gsub("%s+", "") or ""
|
||||||
|
end
|
||||||
|
local v_fs = table_mp:option(Value, "fs", translate("File System"))
|
||||||
|
v_fs.render = function(self, section, scope)
|
||||||
|
if mount_point[section].device == 0 then
|
||||||
|
self.template = "cbi/value"
|
||||||
|
self:value("auto", "auto")
|
||||||
|
self.default = "auto"
|
||||||
|
self.forcewrite = true
|
||||||
|
Value.render(self, section, scope)
|
||||||
|
else
|
||||||
|
self.template = "cbi/dvalue"
|
||||||
|
DummyValue.render(self, section, scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
v_fs.write = function(self, section, value)
|
||||||
|
_mount_point.fs = value and value:gsub("%s+", "") or ""
|
||||||
|
end
|
||||||
|
local v_mount_option = table_mp:option(Value, "mount_options", translate("Mount Options"))
|
||||||
|
v_mount_option.render = function(self, section, scope)
|
||||||
|
if mount_point[section].device == 0 then
|
||||||
|
self.template = "cbi/value"
|
||||||
|
self.placeholder = "rw,noauto"
|
||||||
|
self.forcewrite = true
|
||||||
|
Value.render(self, section, scope)
|
||||||
|
else
|
||||||
|
self.template = "cbi/dvalue"
|
||||||
|
local mp = mount_point[section].mount_options
|
||||||
|
mount_point[section].mount_options = nil
|
||||||
|
local length = 0
|
||||||
|
for k in mp:gmatch("([^,]+)") do
|
||||||
|
mount_point[section].mount_options = mount_point[section].mount_options and (mount_point[section].mount_options .. ",") or ""
|
||||||
|
if length > 20 then
|
||||||
|
mount_point[section].mount_options = mount_point[section].mount_options.. " <br>"
|
||||||
|
length = 0
|
||||||
|
end
|
||||||
|
mount_point[section].mount_options = mount_point[section].mount_options .. k
|
||||||
|
length = length + #k
|
||||||
|
end
|
||||||
|
self.rawhtml = true
|
||||||
|
-- mount_point[section].mount_options = #mount_point[section].mount_options > 50 and mount_point[section].mount_options:sub(1,50) .. "..." or mount_point[section].mount_options
|
||||||
|
DummyValue.render(self, section, scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
v_mount_option.write = function(self, section, value)
|
||||||
|
_mount_point.mount_options = value and value:gsub("%s+", "") or ""
|
||||||
|
end
|
||||||
|
local v_mount_point = table_mp:option(Value, "mount_point", translate("Mount Point"))
|
||||||
|
v_mount_point.render = function(self, section, scope)
|
||||||
|
if mount_point[section].device == 0 then
|
||||||
|
self.template = "cbi/value"
|
||||||
|
self.placeholder = "/media/diskX"
|
||||||
|
self.forcewrite = true
|
||||||
|
Value.render(self, section, scope)
|
||||||
|
else
|
||||||
|
self.template = "cbi/dvalue"
|
||||||
|
local new_mp = ""
|
||||||
|
local v_mp_d
|
||||||
|
for v_mp_d in self["section"]["data"][section]["mount_point"]:gmatch('[^/]+') do
|
||||||
|
if #v_mp_d > 12 then
|
||||||
|
new_mp = new_mp .. "/" .. v_mp_d:sub(1,7) .. ".." .. v_mp_d:sub(-4)
|
||||||
|
else
|
||||||
|
new_mp = new_mp .."/".. v_mp_d
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self["section"]["data"][section]["mount_point"] = '<span title="'..self["section"]["data"][section]["mount_point"] .. '" >'..new_mp..'</span>'
|
||||||
|
self.rawhtml = true
|
||||||
|
DummyValue.render(self, section, scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
v_mount_point.write = function(self, section, value)
|
||||||
|
_mount_point.mount_point = value
|
||||||
|
end
|
||||||
|
local btn_umount = table_mp:option(Button, "_mount", translate("Mount"))
|
||||||
|
btn_umount.forcewrite = true
|
||||||
|
btn_umount.render = function(self, section, scope)
|
||||||
|
if mount_point[section].device == 0 then
|
||||||
|
self.inputtitle = translate("Mount")
|
||||||
|
btn_umount.inputstyle = "add"
|
||||||
|
else
|
||||||
|
self.inputtitle = translate("Umount")
|
||||||
|
btn_umount.inputstyle = "remove"
|
||||||
|
end
|
||||||
|
Button.render(self, section, scope)
|
||||||
|
end
|
||||||
|
btn_umount.write = function(self, section, value)
|
||||||
|
local res
|
||||||
|
if value == translate("Mount") then
|
||||||
|
if not _mount_point.mount_point or not _mount_point.device then return end
|
||||||
|
luci.util.exec("mkdir -p ".. _mount_point.mount_point)
|
||||||
|
res = luci.util.exec(dm.command.mount .. " ".. _mount_point.device .. (_mount_point.fs and (" -t ".. _mount_point.fs )or "") .. (_mount_point.mount_options and (" -o " .. _mount_point.mount_options.. " ") or " ").._mount_point.mount_point .. " 2>&1")
|
||||||
|
elseif value == translate("Umount") then
|
||||||
|
res = luci.util.exec(dm.command.umount .. " "..mount_point[section].mount_point .. " 2>&1")
|
||||||
|
end
|
||||||
|
if res:match("^mount:") or res:match("^umount:") then
|
||||||
|
m.errmessage = luci.util.pcdata(res)
|
||||||
|
else
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if dm.command.mdadm or dm.command.btrfs then
|
||||||
|
local creation_section = m:section(TypedSection, "_creation")
|
||||||
|
creation_section.cfgsections=function()
|
||||||
|
return {translate("Creation")}
|
||||||
|
end
|
||||||
|
creation_section:tab("raid", translate("RAID"), translate("RAID Creation"))
|
||||||
|
creation_section:tab("btrfs", translate("Btrfs"), translate("Multiple Devices Btrfs Creation"))
|
||||||
|
|
||||||
|
-- raid functions
|
||||||
|
if dm.command.mdadm then
|
||||||
|
|
||||||
|
local rname, rmembers, rlevel
|
||||||
|
local r_name = creation_section:taboption("raid", Value, "_rname", translate("Raid Name"))
|
||||||
|
r_name.placeholder = "/dev/md0"
|
||||||
|
r_name.write = function(self, section, value)
|
||||||
|
rname = value
|
||||||
|
end
|
||||||
|
local r_level = creation_section:taboption("raid", ListValue, "_rlevel", translate("Raid Level"))
|
||||||
|
local valid_raid = luci.util.exec("lsmod | grep md_mod")
|
||||||
|
if valid_raid:match("linear") then
|
||||||
|
r_level:value("linear", "Linear")
|
||||||
|
end
|
||||||
|
if valid_raid:match("raid456") then
|
||||||
|
r_level:value("5", "Raid 5")
|
||||||
|
r_level:value("6", "Raid 6")
|
||||||
|
end
|
||||||
|
if valid_raid:match("raid1") then
|
||||||
|
r_level:value("1", "Raid 1")
|
||||||
|
end
|
||||||
|
if valid_raid:match("raid0") then
|
||||||
|
r_level:value("0", "Raid 0")
|
||||||
|
end
|
||||||
|
if valid_raid:match("raid10") then
|
||||||
|
r_level:value("10", "Raid 10")
|
||||||
|
end
|
||||||
|
r_level.write = function(self, section, value)
|
||||||
|
rlevel = value
|
||||||
|
end
|
||||||
|
local r_member = creation_section:taboption("raid", DynamicList, "_rmember", translate("Raid Member"))
|
||||||
|
for dev, info in pairs(disks) do
|
||||||
|
if not info.inuse and #info.partitions == 0 then
|
||||||
|
r_member:value(info.path, info.path.. " ".. info.size_formated)
|
||||||
|
end
|
||||||
|
for i, v in ipairs(info.partitions) do
|
||||||
|
if not v.inuse then
|
||||||
|
r_member:value("/dev/".. v.name, "/dev/".. v.name .. " ".. v.size_formated)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
r_member.write = function(self, section, value)
|
||||||
|
rmembers = value
|
||||||
|
end
|
||||||
|
local r_create = creation_section:taboption("raid", Button, "_rcreate")
|
||||||
|
r_create.render = function(self, section, scope)
|
||||||
|
self.title = " "
|
||||||
|
self.inputtitle = translate("Create Raid")
|
||||||
|
self.inputstyle = "add"
|
||||||
|
Button.render(self, section, scope)
|
||||||
|
end
|
||||||
|
r_create.write = function(self, section, value)
|
||||||
|
-- mdadm --create --verbose /dev/md0 --level=stripe --raid-devices=2 /dev/sdb6 /dev/sdc5
|
||||||
|
local res = dm.create_raid(rname, rlevel, rmembers)
|
||||||
|
if res and res:match("^ERR") then
|
||||||
|
m.errmessage = luci.util.pcdata(res)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
dm.gen_mdadm_config()
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- btrfs
|
||||||
|
if dm.command.btrfs then
|
||||||
|
local blabel, bmembers, blevel
|
||||||
|
local btrfs_label = creation_section:taboption("btrfs", Value, "_blabel", translate("Btrfs Label"))
|
||||||
|
btrfs_label.write = function(self, section, value)
|
||||||
|
blabel = value
|
||||||
|
end
|
||||||
|
local btrfs_level = creation_section:taboption("btrfs", ListValue, "_blevel", translate("Btrfs Raid Level"))
|
||||||
|
btrfs_level:value("single", "Single")
|
||||||
|
btrfs_level:value("raid0", "Raid 0")
|
||||||
|
btrfs_level:value("raid1", "Raid 1")
|
||||||
|
btrfs_level:value("raid10", "Raid 10")
|
||||||
|
btrfs_level.write = function(self, section, value)
|
||||||
|
blevel = value
|
||||||
|
end
|
||||||
|
|
||||||
|
local btrfs_member = creation_section:taboption("btrfs", DynamicList, "_bmember", translate("Btrfs Member"))
|
||||||
|
for dev, info in pairs(disks) do
|
||||||
|
if not info.inuse and #info.partitions == 0 then
|
||||||
|
btrfs_member:value(info.path, info.path.. " ".. info.size_formated)
|
||||||
|
end
|
||||||
|
for i, v in ipairs(info.partitions) do
|
||||||
|
if not v.inuse then
|
||||||
|
btrfs_member:value("/dev/".. v.name, "/dev/".. v.name .. " ".. v.size_formated)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
btrfs_member.write = function(self, section, value)
|
||||||
|
bmembers = value
|
||||||
|
end
|
||||||
|
local btrfs_create = creation_section:taboption("btrfs", Button, "_bcreate")
|
||||||
|
btrfs_create.render = function(self, section, scope)
|
||||||
|
self.title = " "
|
||||||
|
self.inputtitle = translate("Create Btrfs")
|
||||||
|
self.inputstyle = "add"
|
||||||
|
Button.render(self, section, scope)
|
||||||
|
end
|
||||||
|
btrfs_create.write = function(self, section, value)
|
||||||
|
-- mkfs.btrfs -L label -d blevel /dev/sda /dev/sdb
|
||||||
|
local res = dm.create_btrfs(blabel, blevel, bmembers)
|
||||||
|
if res and res:match("^ERR") then
|
||||||
|
m.errmessage = luci.util.pcdata(res)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return m
|
366
luci-app-diskman/luasrc/model/cbi/diskman/partition.lua
Normal file
|
@ -0,0 +1,366 @@
|
||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
|
||||||
|
]]--
|
||||||
|
|
||||||
|
require "luci.util"
|
||||||
|
require("luci.tools.webadmin")
|
||||||
|
local dm = require "luci.model.diskman"
|
||||||
|
local dev = arg[1]
|
||||||
|
|
||||||
|
if not dev then
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||||
|
elseif not nixio.fs.access("/dev/"..dev) then
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||||
|
end
|
||||||
|
|
||||||
|
m = SimpleForm("partition", translate("Partition Management"), translate("Partition Disk over LuCI."))
|
||||||
|
m.template = "diskman/cbi/xsimpleform"
|
||||||
|
m.redirect = luci.dispatcher.build_url("admin/system/diskman")
|
||||||
|
m:append(Template("diskman/partition_info"))
|
||||||
|
-- disable submit and reset button
|
||||||
|
m.submit = false
|
||||||
|
m.reset = false
|
||||||
|
|
||||||
|
local disk_info = dm.get_disk_info(dev, true)
|
||||||
|
local format_cmd = dm.get_format_cmd()
|
||||||
|
|
||||||
|
s = m:section(Table, {disk_info}, translate("Device Info"))
|
||||||
|
-- s:option(DummyValue, "key")
|
||||||
|
-- s:option(DummyValue, "value")
|
||||||
|
s:option(DummyValue, "path", translate("Path"))
|
||||||
|
s:option(DummyValue, "model", translate("Model"))
|
||||||
|
s:option(DummyValue, "sn", translate("Serial Number"))
|
||||||
|
s:option(DummyValue, "size_formated", translate("Size"))
|
||||||
|
s:option(DummyValue, "sec_size", translate("Sector Size"))
|
||||||
|
local dv_p_table = s:option(ListValue, "p_table", translate("Partition Table"))
|
||||||
|
dv_p_table.render = function(self, section, scope)
|
||||||
|
-- create table only if not used by raid and no partitions on disk
|
||||||
|
if not disk_info.p_table:match("Raid") and (#disk_info.partitions == 0 or (#disk_info.partitions == 1 and disk_info.partitions[1].number == -1) or (disk_info.p_table:match("LOOP") and not disk_info.partitions[1].inuse)) then
|
||||||
|
self:value(disk_info.p_table, disk_info.p_table)
|
||||||
|
self:value("GPT", "GPT")
|
||||||
|
self:value("MBR", "MBR")
|
||||||
|
self.default = disk_info.p_table
|
||||||
|
ListValue.render(self, section, scope)
|
||||||
|
else
|
||||||
|
self.template = "cbi/dvalue"
|
||||||
|
DummyValue.render(self, section, scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if disk_info.type:match("md") then
|
||||||
|
s:option(DummyValue, "level", translate("Level"))
|
||||||
|
s:option(DummyValue, "members_str", translate("Members"))
|
||||||
|
else
|
||||||
|
s:option(DummyValue, "temp", translate("Temp"))
|
||||||
|
s:option(DummyValue, "sata_ver", translate("SATA Version"))
|
||||||
|
s:option(DummyValue, "rota_rate", translate("Rotation Rate"))
|
||||||
|
end
|
||||||
|
s:option(DummyValue, "status", translate("Status"))
|
||||||
|
local btn_health = s:option(Button, "health", translate("Health"))
|
||||||
|
btn_health.render = function(self, section, scope)
|
||||||
|
if disk_info.health then
|
||||||
|
self.inputtitle = disk_info.health
|
||||||
|
if disk_info.health == "PASSED" then
|
||||||
|
self.inputstyle = "add"
|
||||||
|
else
|
||||||
|
self.inputstyle = "remove"
|
||||||
|
end
|
||||||
|
Button.render(self, section, scope)
|
||||||
|
else
|
||||||
|
self.template = "cbi/dvalue"
|
||||||
|
DummyValue.render(self, section, scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local btn_eject = s:option(Button, "_eject")
|
||||||
|
btn_eject.template = "diskman/cbi/disabled_button"
|
||||||
|
btn_eject.inputstyle = "remove"
|
||||||
|
btn_eject.render = function(self, section, scope)
|
||||||
|
for i, p in ipairs(disk_info.partitions) do
|
||||||
|
if p.mount_point ~= "-" then
|
||||||
|
self.view_disabled = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if disk_info.p_table:match("Raid") then
|
||||||
|
self.view_disabled = true
|
||||||
|
end
|
||||||
|
if disk_info.type:match("md") then
|
||||||
|
btn_eject.inputtitle = translate("Remove")
|
||||||
|
else
|
||||||
|
btn_eject.inputtitle = translate("Eject")
|
||||||
|
end
|
||||||
|
Button.render(self, section, scope)
|
||||||
|
end
|
||||||
|
btn_eject.forcewrite = true
|
||||||
|
btn_eject.write = function(self, section, value)
|
||||||
|
for i, p in ipairs(disk_info.partitions) do
|
||||||
|
if p.mount_point ~= "-" then
|
||||||
|
m.errmessage = p.name .. translate("is in use! please unmount it first!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if disk_info.type:match("md") then
|
||||||
|
luci.util.exec(dm.command.mdadm .. " --stop /dev/" .. dev)
|
||||||
|
luci.util.exec(dm.command.mdadm .. " --remove /dev/" .. dev)
|
||||||
|
for _, disk in ipairs(disk_info.members) do
|
||||||
|
luci.util.exec(dm.command.mdadm .. " --zero-superblock " .. disk)
|
||||||
|
end
|
||||||
|
dm.gen_mdadm_config()
|
||||||
|
else
|
||||||
|
luci.util.exec("echo 1 > /sys/block/" .. dev .. "/device/delete")
|
||||||
|
end
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||||
|
end
|
||||||
|
-- eject: echo 1 > /sys/block/(device)/device/delete
|
||||||
|
-- rescan: echo '- - -' | tee /sys/class/scsi_host/host*/scan > /dev/null
|
||||||
|
|
||||||
|
|
||||||
|
-- partitions info
|
||||||
|
if not disk_info.p_table:match("Raid") then
|
||||||
|
s_partition_table = m:section(Table, disk_info.partitions, translate("Partitions Info"), translate("Default 2048 sector alignment, support +size{b,k,m,g,t} in End Sector"))
|
||||||
|
|
||||||
|
-- s_partition_table:option(DummyValue, "number", translate("Number"))
|
||||||
|
s_partition_table:option(DummyValue, "name", translate("Name"))
|
||||||
|
local val_sec_start = s_partition_table:option(Value, "sec_start", translate("Start Sector"))
|
||||||
|
val_sec_start.render = function(self, section, scope)
|
||||||
|
-- could create new partition
|
||||||
|
if disk_info.partitions[section].number == -1 and disk_info.partitions[section].size > 1 * 1024 * 1024 then
|
||||||
|
self.template = "cbi/value"
|
||||||
|
Value.render(self, section, scope)
|
||||||
|
else
|
||||||
|
self.template = "cbi/dvalue"
|
||||||
|
DummyValue.render(self, section, scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local val_sec_end = s_partition_table:option(Value, "sec_end", translate("End Sector"))
|
||||||
|
val_sec_end.render = function(self, section, scope)
|
||||||
|
-- could create new partition
|
||||||
|
if disk_info.partitions[section].number == -1 and disk_info.partitions[section].size > 1 * 1024 * 1024 then
|
||||||
|
self.template = "cbi/value"
|
||||||
|
Value.render(self, section, scope)
|
||||||
|
else
|
||||||
|
self.template = "cbi/dvalue"
|
||||||
|
DummyValue.render(self, section, scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
val_sec_start.forcewrite = true
|
||||||
|
val_sec_start.write = function(self, section, value)
|
||||||
|
disk_info.partitions[section]._sec_start = value
|
||||||
|
end
|
||||||
|
val_sec_end.forcewrite = true
|
||||||
|
val_sec_end.write = function(self, section, value)
|
||||||
|
disk_info.partitions[section]._sec_end = value
|
||||||
|
end
|
||||||
|
s_partition_table:option(DummyValue, "size_formated", translate("Size"))
|
||||||
|
if disk_info.p_table == "MBR" then
|
||||||
|
s_partition_table:option(DummyValue, "type", translate("Type"))
|
||||||
|
end
|
||||||
|
s_partition_table:option(DummyValue, "used_formated", translate("Used"))
|
||||||
|
s_partition_table:option(DummyValue, "free_formated", translate("Free Space"))
|
||||||
|
s_partition_table:option(DummyValue, "usage", translate("Usage"))
|
||||||
|
local dv_mount_point = s_partition_table:option(DummyValue, "mount_point", translate("Mount Point"))
|
||||||
|
dv_mount_point.rawhtml = true
|
||||||
|
dv_mount_point.render = function(self, section, scope)
|
||||||
|
local new_mp = ""
|
||||||
|
local v_mp_d
|
||||||
|
for line in self["section"]["data"][section]["mount_point"]:gmatch("[^%s]+") do
|
||||||
|
if line == '-' then
|
||||||
|
new_mp = line
|
||||||
|
break
|
||||||
|
end
|
||||||
|
for v_mp_d in line:gmatch('[^/]+') do
|
||||||
|
if #v_mp_d > 12 then
|
||||||
|
new_mp = new_mp .. "/" .. v_mp_d:sub(1,7) .. ".." .. v_mp_d:sub(-4)
|
||||||
|
else
|
||||||
|
new_mp = new_mp .."/".. v_mp_d
|
||||||
|
end
|
||||||
|
end
|
||||||
|
new_mp = '<span title="'.. line .. '" >' ..new_mp ..'</span>' .. "<br/>"
|
||||||
|
end
|
||||||
|
self["section"]["data"][section]["mount_point"] = new_mp
|
||||||
|
DummyValue.render(self, section, scope)
|
||||||
|
end
|
||||||
|
local val_fs = s_partition_table:option(Value, "fs", translate("File System"))
|
||||||
|
val_fs.forcewrite = true
|
||||||
|
val_fs.partitions = disk_info.partitions
|
||||||
|
for k, v in pairs(format_cmd) do
|
||||||
|
val_fs.format_cmd = val_fs.format_cmd and (val_fs.format_cmd .. "," .. k) or k
|
||||||
|
end
|
||||||
|
|
||||||
|
val_fs.write = function(self, section, value)
|
||||||
|
disk_info.partitions[section]._fs = value
|
||||||
|
end
|
||||||
|
val_fs.render = function(self, section, scope)
|
||||||
|
-- use listvalue when partition not mounted
|
||||||
|
if disk_info.partitions[section].mount_point == "-" and disk_info.partitions[section].number ~= -1 and disk_info.partitions[section].type ~= "extended" then
|
||||||
|
self.template = "diskman/cbi/format_button"
|
||||||
|
self.inputstyle = "reset"
|
||||||
|
self.inputtitle = disk_info.partitions[section].fs == "raw" and translate("Format") or disk_info.partitions[section].fs
|
||||||
|
Button.render(self, section, scope)
|
||||||
|
-- self:reset_values()
|
||||||
|
-- self.keylist = {}
|
||||||
|
-- self.vallist = {}
|
||||||
|
-- for k, v in pairs(format_cmd) do
|
||||||
|
-- self:value(k,k)
|
||||||
|
-- end
|
||||||
|
-- self.default = disk_info.partitions[section].fs
|
||||||
|
else
|
||||||
|
-- self:reset_values()
|
||||||
|
-- self.keylist = {}
|
||||||
|
-- self.vallist = {}
|
||||||
|
self.template = "cbi/dvalue"
|
||||||
|
DummyValue.render(self, section, scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- btn_format = s_partition_table:option(Button, "_format")
|
||||||
|
-- btn_format.template = "diskman/cbi/format_button"
|
||||||
|
-- btn_format.partitions = disk_info.partitions
|
||||||
|
-- btn_format.render = function(self, section, scope)
|
||||||
|
-- if disk_info.partitions[section].mount_point == "-" and disk_info.partitions[section].number ~= -1 and disk_info.partitions[section].type ~= "extended" then
|
||||||
|
-- self.inputtitle = translate("Format")
|
||||||
|
-- self.template = "diskman/cbi/disabled_button"
|
||||||
|
-- self.view_disabled = false
|
||||||
|
-- self.inputstyle = "reset"
|
||||||
|
-- for k, v in pairs(format_cmd) do
|
||||||
|
-- self:depends("val_fs", "k")
|
||||||
|
-- end
|
||||||
|
-- -- elseif disk_info.partitions[section].mount_point ~= "-" and disk_info.partitions[section].number ~= -1 then
|
||||||
|
-- -- self.inputtitle = "Format"
|
||||||
|
-- -- self.template = "diskman/cbi/disabled_button"
|
||||||
|
-- -- self.view_disabled = true
|
||||||
|
-- -- self.inputstyle = "reset"
|
||||||
|
-- else
|
||||||
|
-- self.inputtitle = ""
|
||||||
|
-- self.template = "cbi/dvalue"
|
||||||
|
-- end
|
||||||
|
-- Button.render(self, section, scope)
|
||||||
|
-- end
|
||||||
|
-- btn_format.forcewrite = true
|
||||||
|
-- btn_format.write = function(self, section, value)
|
||||||
|
-- local partition_name = "/dev/".. disk_info.partitions[section].name
|
||||||
|
-- if not nixio.fs.access(partition_name) then
|
||||||
|
-- m.errmessage = translate("Partition NOT found!")
|
||||||
|
-- return
|
||||||
|
-- end
|
||||||
|
-- local fs = disk_info.partitions[section]._fs
|
||||||
|
-- if not format_cmd[fs] then
|
||||||
|
-- m.errmessage = translate("Filesystem NOT support!")
|
||||||
|
-- return
|
||||||
|
-- end
|
||||||
|
-- local cmd = format_cmd[fs].cmd .. " " .. format_cmd[fs].option .. " " .. partition_name
|
||||||
|
-- local res = luci.util.exec(cmd .. " 2>&1")
|
||||||
|
-- if res and res:lower():match("error+") then
|
||||||
|
-- m.errmessage = luci.util.pcdata(res)
|
||||||
|
-- else
|
||||||
|
-- luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/partition/" .. dev))
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
local btn_action = s_partition_table:option(Button, "_action")
|
||||||
|
btn_action.forcewrite = true
|
||||||
|
btn_action.template = "diskman/cbi/disabled_button"
|
||||||
|
btn_action.render = function(self, section, scope)
|
||||||
|
-- if partition is mounted or the size < 1mb, then disable the add action
|
||||||
|
if disk_info.partitions[section].mount_point ~= "-" or (disk_info.partitions[section].type ~= "extended" and disk_info.partitions[section].number == -1 and disk_info.partitions[section].size <= 1 * 1024 * 1024) then
|
||||||
|
self.view_disabled = true
|
||||||
|
-- self.inputtitle = ""
|
||||||
|
-- self.template = "cbi/dvalue"
|
||||||
|
elseif disk_info.partitions[section].type == "extended" and next(disk_info.partitions[section]["logicals"]) ~= nil then
|
||||||
|
self.view_disabled = true
|
||||||
|
else
|
||||||
|
-- self.template = "diskman/cbi/disabled_button"
|
||||||
|
self.view_disabled = false
|
||||||
|
end
|
||||||
|
if disk_info.partitions[section].number ~= -1 then
|
||||||
|
self.inputtitle = translate("Remove")
|
||||||
|
self.inputstyle = "remove"
|
||||||
|
else
|
||||||
|
self.inputtitle = translate("New")
|
||||||
|
self.inputstyle = "add"
|
||||||
|
end
|
||||||
|
Button.render(self, section, scope)
|
||||||
|
end
|
||||||
|
btn_action.write = function(self, section, value)
|
||||||
|
if value == translate("New") then
|
||||||
|
local start_sec = disk_info.partitions[section]._sec_start and tonumber(disk_info.partitions[section]._sec_start) or tonumber(disk_info.partitions[section].sec_start)
|
||||||
|
local end_sec = disk_info.partitions[section]._sec_end
|
||||||
|
|
||||||
|
if start_sec then
|
||||||
|
-- for sector alignment
|
||||||
|
local align = tonumber(disk_info.phy_sec) / tonumber(disk_info.logic_sec)
|
||||||
|
align = (align < 2048) and 2048
|
||||||
|
if start_sec < 2048 then
|
||||||
|
start_sec = "2048" .. "s"
|
||||||
|
elseif math.fmod( start_sec, align ) ~= 0 then
|
||||||
|
start_sec = tostring(start_sec + align - math.fmod( start_sec, align )) .. "s"
|
||||||
|
else
|
||||||
|
start_sec = start_sec .. "s"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
m.errmessage = translate("Invalid Start Sector!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- support +size format for End sector
|
||||||
|
local end_size, end_unit = end_sec:match("^+(%d-)([bkmgtsBKMGTS])$")
|
||||||
|
if tonumber(end_size) and end_unit then
|
||||||
|
local unit ={
|
||||||
|
B=1,
|
||||||
|
S=512,
|
||||||
|
K=1024,
|
||||||
|
M=1048576,
|
||||||
|
G=1073741824,
|
||||||
|
T=1099511627776
|
||||||
|
}
|
||||||
|
end_unit = end_unit:upper()
|
||||||
|
end_sec = tostring(tonumber(end_size) * unit[end_unit] / unit["S"] + tonumber(start_sec:sub(1,-2)) - 1 ) .. "s"
|
||||||
|
elseif tonumber(end_sec) then
|
||||||
|
end_sec = end_sec .. "s"
|
||||||
|
else
|
||||||
|
m.errmessage = translate("Invalid End Sector!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local part_type = "primary"
|
||||||
|
|
||||||
|
if disk_info.p_table == "MBR" and disk_info["extended_partition_index"] then
|
||||||
|
if tonumber(disk_info.partitions[disk_info["extended_partition_index"]].sec_start) <= tonumber(start_sec:sub(1,-2)) and tonumber(disk_info.partitions[disk_info["extended_partition_index"]].sec_end) >= tonumber(end_sec:sub(1,-2)) then
|
||||||
|
part_type = "logical"
|
||||||
|
if tonumber(start_sec:sub(1,-2)) - tonumber(disk_info.partitions[section].sec_start) < 2048 then
|
||||||
|
start_sec = tonumber(start_sec:sub(1,-2)) + 2048
|
||||||
|
start_sec = start_sec .."s"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif disk_info.p_table == "GPT" then
|
||||||
|
-- AUTOMATIC FIX GPT PARTITION TABLE
|
||||||
|
-- Not all of the space available to /dev/sdb appears to be used, you can fix the GPT to use all of the space (an extra 16123870 blocks) or continue with the current setting?
|
||||||
|
local cmd = ' printf "ok\nfix\n" | parted ---pretend-input-tty /dev/'.. dev ..' print'
|
||||||
|
luci.util.exec(cmd .. " 2>&1")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- partiton
|
||||||
|
local cmd = dm.command.parted .. " -s -a optimal /dev/" .. dev .. " mkpart " .. part_type .." " .. start_sec .. " " .. end_sec
|
||||||
|
local res = luci.util.exec(cmd .. " 2>&1")
|
||||||
|
if res and res:lower():match("error+") then
|
||||||
|
m.errmessage = luci.util.pcdata(res)
|
||||||
|
else
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/partition/" .. dev))
|
||||||
|
end
|
||||||
|
elseif value == translate("Remove") then
|
||||||
|
-- remove partition
|
||||||
|
local number = tostring(disk_info.partitions[section].number)
|
||||||
|
if (not number) or (number == "") then
|
||||||
|
m.errmessage = translate("Partition not exists!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local cmd = dm.command.parted .. " -s /dev/" .. dev .. " rm " .. number
|
||||||
|
local res = luci.util.exec(cmd .. " 2>&1")
|
||||||
|
if res and res:lower():match("error+") then
|
||||||
|
m.errmessage = luci.util.pcdata(res)
|
||||||
|
else
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/partition/" .. dev))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return m
|
738
luci-app-diskman/luasrc/model/diskman.lua
Normal file
|
@ -0,0 +1,738 @@
|
||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
|
||||||
|
]]--
|
||||||
|
|
||||||
|
require "luci.util"
|
||||||
|
local ver = require "luci.version"
|
||||||
|
|
||||||
|
local CMD = {"parted", "mdadm", "blkid", "smartctl", "df", "btrfs", "lsblk"}
|
||||||
|
|
||||||
|
local d = {command ={}}
|
||||||
|
for _, cmd in ipairs(CMD) do
|
||||||
|
local command = luci.sys.exec("/usr/bin/which " .. cmd)
|
||||||
|
d.command[cmd] = command:match("^.+"..cmd) or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
d.command.mount = nixio.fs.access("/usr/bin/mount") and "/usr/bin/mount" or "/bin/mount"
|
||||||
|
d.command.umount = nixio.fs.access("/usr/bin/umount") and "/usr/bin/umount" or "/bin/umount"
|
||||||
|
|
||||||
|
local proc_mounts = nixio.fs.readfile("/proc/mounts") or ""
|
||||||
|
local mounts = luci.util.exec(d.command.mount .. " 2>/dev/null") or ""
|
||||||
|
local swaps = nixio.fs.readfile("/proc/swaps") or ""
|
||||||
|
local df = luci.sys.exec(d.command.df .. " 2>/dev/null") or ""
|
||||||
|
|
||||||
|
function byte_format(byte)
|
||||||
|
local suff = {"B", "KB", "MB", "GB", "TB"}
|
||||||
|
for i=1, 5 do
|
||||||
|
if byte > 1024 and i < 5 then
|
||||||
|
byte = byte / 1024
|
||||||
|
else
|
||||||
|
return string.format("%.2f %s", byte, suff[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local get_smart_info = function(device)
|
||||||
|
local section
|
||||||
|
local smart_info = {}
|
||||||
|
for _, line in ipairs(luci.util.execl(d.command.smartctl .. " -H -A -i -n standby -f brief /dev/" .. device)) do
|
||||||
|
local attrib, val
|
||||||
|
if section == 1 then
|
||||||
|
attrib, val = line:match "^(.-):%s+(.+)"
|
||||||
|
elseif section == 2 and smart_info.nvme_ver then
|
||||||
|
attrib, val = line:match("^(.-):%s+(.+)")
|
||||||
|
if not smart_info.health then smart_info.health = line:match(".-overall%-health.-: (.+)") end
|
||||||
|
elseif section == 2 then
|
||||||
|
attrib, val = line:match("^([0-9 ]+)%s+[^ ]+%s+[POSRCK-]+%s+[0-9-]+%s+[0-9-]+%s+[0-9-]+%s+[0-9-]+%s+([0-9-]+)")
|
||||||
|
if not smart_info.health then smart_info.health = line:match(".-overall%-health.-: (.+)") end
|
||||||
|
else
|
||||||
|
attrib = line:match "^=== START OF (.*) SECTION ==="
|
||||||
|
if attrib and attrib:match("INFORMATION") then
|
||||||
|
section = 1
|
||||||
|
elseif attrib and attrib:match("SMART DATA") then
|
||||||
|
section = 2
|
||||||
|
elseif not smart_info.status then
|
||||||
|
val = line:match "^Device is in (.*) mode"
|
||||||
|
if val then smart_info.status = val end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not attrib then
|
||||||
|
if section ~= 2 then section = 0 end
|
||||||
|
elseif (attrib == "Power mode is") or
|
||||||
|
(attrib == "Power mode was") then
|
||||||
|
smart_info.status = val:match("(%S+)")
|
||||||
|
-- elseif attrib == "Sector Sizes" then
|
||||||
|
-- -- 512 bytes logical, 4096 bytes physical
|
||||||
|
-- smart_info.phy_sec = val:match "([0-9]*) bytes physical"
|
||||||
|
-- smart_info.logic_sec = val:match "([0-9]*) bytes logical"
|
||||||
|
-- elseif attrib == "Sector Size" then
|
||||||
|
-- -- 512 bytes logical/physical
|
||||||
|
-- smart_info.phy_sec = val:match "([0-9]*)"
|
||||||
|
-- smart_info.logic_sec = smart_info.phy_sec
|
||||||
|
elseif attrib == "Serial Number" then
|
||||||
|
smart_info.sn = val
|
||||||
|
elseif attrib == "194" or attrib == "Temperature" then
|
||||||
|
smart_info.temp = val:match("(%d+)") .. "°C"
|
||||||
|
elseif attrib == "Rotation Rate" then
|
||||||
|
smart_info.rota_rate = val
|
||||||
|
elseif attrib == "SATA Version is" then
|
||||||
|
smart_info.sata_ver = val
|
||||||
|
elseif attrib == "NVMe Version" then
|
||||||
|
smart_info.nvme_ver = val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return smart_info
|
||||||
|
end
|
||||||
|
|
||||||
|
local parse_parted_info = function(keys, line)
|
||||||
|
-- parse the output of parted command (machine parseable format)
|
||||||
|
-- /dev/sda:5860533168s:scsi:512:4096:gpt:ATA ST3000DM001-1ER1:;
|
||||||
|
-- 1:34s:2047s:2014s:free;
|
||||||
|
-- 1:2048s:1073743872s:1073741825s:ext4:primary:;
|
||||||
|
local result = {}
|
||||||
|
local values = {}
|
||||||
|
|
||||||
|
for value in line:gmatch("(.-)[:;]") do table.insert(values, value) end
|
||||||
|
for i = 1,#keys do
|
||||||
|
result[keys[i]] = values[i] or ""
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local is_raid_member = function(partition)
|
||||||
|
-- check if inuse as raid member
|
||||||
|
if nixio.fs.access("/proc/mdstat") then
|
||||||
|
for _, result in ipairs(luci.util.execl("grep md /proc/mdstat | sed 's/[][]//g'")) do
|
||||||
|
local md, buf
|
||||||
|
md, buf = result:match("(md.-):(.+)")
|
||||||
|
if buf:match(partition) then
|
||||||
|
return "Raid Member: ".. md
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local get_mount_point = function(partition)
|
||||||
|
local mount_point
|
||||||
|
for m in mounts:gmatch("/dev/"..partition.." on ([^ ]*)") do
|
||||||
|
mount_point = (mount_point and (mount_point .. " ") or "") .. m
|
||||||
|
end
|
||||||
|
if mount_point then return mount_point end
|
||||||
|
-- result = luci.sys.exec('cat /proc/mounts | awk \'{if($1=="/dev/'.. partition ..'") print $2}\'')
|
||||||
|
-- if result ~= "" then return result end
|
||||||
|
|
||||||
|
if swaps:match("\n/dev/" .. partition .."%s") then return "swap" end
|
||||||
|
-- result = luci.sys.exec("cat /proc/swaps | grep /dev/" .. partition)
|
||||||
|
-- if result ~= "" then return "swap" end
|
||||||
|
|
||||||
|
return is_raid_member(partition)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- return used, free, usage
|
||||||
|
local get_partition_usage = function(partition)
|
||||||
|
if not nixio.fs.access("/dev/"..partition) then return false end
|
||||||
|
local used, free, usage = df:match("\n/dev/" .. partition .. "%s+%d+%s+(%d+)%s+(%d+)%s+(%d+)%%%s-")
|
||||||
|
|
||||||
|
usage = usage and (usage .. "%") or "-"
|
||||||
|
used = used and (tonumber(used) * 1024) or 0
|
||||||
|
free = free and (tonumber(free) * 1024) or 0
|
||||||
|
|
||||||
|
return used, free, usage
|
||||||
|
end
|
||||||
|
|
||||||
|
local get_parted_info = function(device)
|
||||||
|
if not device then return end
|
||||||
|
local result = {partitions={}}
|
||||||
|
local DEVICE_INFO_KEYS = { "path", "size", "type", "logic_sec", "phy_sec", "p_table", "model", "flags" }
|
||||||
|
local PARTITION_INFO_KEYS = { "number", "sec_start", "sec_end", "size", "fs", "tag_name", "flags" }
|
||||||
|
local partition_temp
|
||||||
|
local partitions_temp = {}
|
||||||
|
local disk_temp
|
||||||
|
|
||||||
|
for line in luci.util.execi(d.command.parted .. " -s -m /dev/" .. device .. " unit s print free", "r") do
|
||||||
|
if line:find("^/dev/"..device..":.+") then
|
||||||
|
disk_temp = parse_parted_info(DEVICE_INFO_KEYS, line)
|
||||||
|
disk_temp.partitions = {}
|
||||||
|
if disk_temp["size"] then
|
||||||
|
local length = disk_temp["size"]:gsub("^(%d+)s$", "%1")
|
||||||
|
local newsize = tostring(tonumber(length)*tonumber(disk_temp["logic_sec"]))
|
||||||
|
disk_temp["size"] = newsize
|
||||||
|
end
|
||||||
|
if disk_temp["p_table"] == "msdos" then
|
||||||
|
disk_temp["p_table"] = "MBR"
|
||||||
|
else
|
||||||
|
disk_temp["p_table"] = disk_temp["p_table"]:upper()
|
||||||
|
end
|
||||||
|
elseif line:find("^%d-:.+") then
|
||||||
|
partition_temp = parse_parted_info(PARTITION_INFO_KEYS, line)
|
||||||
|
-- use human-readable form instead of sector number
|
||||||
|
if partition_temp["size"] then
|
||||||
|
local length = partition_temp["size"]:gsub("^(%d+)s$", "%1")
|
||||||
|
local newsize = (tonumber(length) * tonumber(disk_temp["logic_sec"]))
|
||||||
|
partition_temp["size"] = newsize
|
||||||
|
partition_temp["size_formated"] = byte_format(newsize)
|
||||||
|
end
|
||||||
|
partition_temp["number"] = tonumber(partition_temp["number"]) or -1
|
||||||
|
if partition_temp["fs"] == "free" then
|
||||||
|
partition_temp["number"] = -1
|
||||||
|
partition_temp["fs"] = "Free Space"
|
||||||
|
partition_temp["name"] = "-"
|
||||||
|
elseif device:match("sd") or device:match("sata") then
|
||||||
|
partition_temp["name"] = device..partition_temp["number"]
|
||||||
|
elseif device:match("mmcblk") or device:match("md") or device:match("nvme") then
|
||||||
|
partition_temp["name"] = device.."p"..partition_temp["number"]
|
||||||
|
end
|
||||||
|
if partition_temp["number"] > 0 and partition_temp["fs"] == "" and d.command.lsblk then
|
||||||
|
partition_temp["fs"] = luci.util.exec(d.command.lsblk .. " /dev/"..device.. tostring(partition_temp["number"]) .. " -no fstype"):match("([^%s]+)") or ""
|
||||||
|
end
|
||||||
|
partition_temp["fs"] = partition_temp["fs"] == "" and "raw" or partition_temp["fs"]
|
||||||
|
partition_temp["sec_start"] = partition_temp["sec_start"] and partition_temp["sec_start"]:sub(1,-2)
|
||||||
|
partition_temp["sec_end"] = partition_temp["sec_end"] and partition_temp["sec_end"]:sub(1,-2)
|
||||||
|
partition_temp["mount_point"] = partition_temp["name"]~="-" and get_mount_point(partition_temp["name"]) or "-"
|
||||||
|
if partition_temp["mount_point"]~="-" then
|
||||||
|
partition_temp["used"], partition_temp["free"], partition_temp["usage"] = get_partition_usage(partition_temp["name"])
|
||||||
|
partition_temp["used_formated"] = partition_temp["used"] and byte_format(partition_temp["used"]) or "-"
|
||||||
|
partition_temp["free_formated"] = partition_temp["free"] and byte_format(partition_temp["free"]) or "-"
|
||||||
|
else
|
||||||
|
partition_temp["used"], partition_temp["free"], partition_temp["usage"] = 0,0,"-"
|
||||||
|
partition_temp["used_formated"] = "-"
|
||||||
|
partition_temp["free_formated"] = "-"
|
||||||
|
end
|
||||||
|
-- if disk_temp["p_table"] == "MBR" and (partition_temp["number"] < 4) and (partition_temp["number"] > 0) then
|
||||||
|
-- local real_size_sec = tonumber(nixio.fs.readfile("/sys/block/"..device.."/"..partition_temp["name"].."/size")) * tonumber(disk_temp.phy_sec)
|
||||||
|
-- if real_size_sec ~= partition_temp["size"] then
|
||||||
|
-- disk_temp["extended_partition_index"] = partition_temp["number"]
|
||||||
|
-- partition_temp["type"] = "extended"
|
||||||
|
-- partition_temp["size"] = real_size_sec
|
||||||
|
-- partition_temp["fs"] = "-"
|
||||||
|
-- partition_temp["logicals"] = {}
|
||||||
|
-- else
|
||||||
|
-- partition_temp["type"] = "primary"
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
table.insert(partitions_temp, partition_temp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if disk_temp and disk_temp["p_table"] == "MBR" then
|
||||||
|
for i, p in ipairs(partitions_temp) do
|
||||||
|
if disk_temp["extended_partition_index"] and p["number"] > 4 then
|
||||||
|
if tonumber(p["sec_end"]) <= tonumber(partitions_temp[disk_temp["extended_partition_index"]]["sec_end"]) and tonumber(p["sec_start"]) >= tonumber(partitions_temp[disk_temp["extended_partition_index"]]["sec_start"]) then
|
||||||
|
p["type"] = "logical"
|
||||||
|
table.insert(partitions_temp[disk_temp["extended_partition_index"]]["logicals"], i)
|
||||||
|
end
|
||||||
|
elseif (p["number"] < 4) and (p["number"] > 0) then
|
||||||
|
local s = nixio.fs.readfile("/sys/block/"..device.."/"..p["name"].."/size")
|
||||||
|
if s then
|
||||||
|
local real_size_sec = tonumber(s) * tonumber(disk_temp.phy_sec)
|
||||||
|
-- if size not equal, it's an extended
|
||||||
|
if real_size_sec ~= p["size"] then
|
||||||
|
disk_temp["extended_partition_index"] = i
|
||||||
|
p["type"] = "extended"
|
||||||
|
p["size"] = real_size_sec
|
||||||
|
p["fs"] = "-"
|
||||||
|
p["logicals"] = {}
|
||||||
|
else
|
||||||
|
p["type"] = "primary"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- if not found in "/sys/block"
|
||||||
|
p["type"] = "primary"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result = disk_temp
|
||||||
|
result.partitions = partitions_temp
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local mddetail = function(mdpath)
|
||||||
|
local detail = {}
|
||||||
|
local path = mdpath:match("^/dev/md%d+$")
|
||||||
|
if path then
|
||||||
|
local mdadm = io.popen(d.command.mdadm .. " --detail "..path, "r")
|
||||||
|
for line in mdadm:lines() do
|
||||||
|
local key, value = line:match("^%s*(.+) : (.+)")
|
||||||
|
if key then
|
||||||
|
detail[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mdadm:close()
|
||||||
|
end
|
||||||
|
return detail
|
||||||
|
end
|
||||||
|
|
||||||
|
-- return {{device="", mount_points="", fs="", mount_options="", dump="", pass=""}..}
|
||||||
|
d.get_mount_points = function()
|
||||||
|
local mount
|
||||||
|
local res = {}
|
||||||
|
local h ={"device", "mount_point", "fs", "mount_options", "dump", "pass"}
|
||||||
|
for mount in proc_mounts:gmatch("[^\n]+") do
|
||||||
|
local device = mount:match("^([^%s]+)%s+.+")
|
||||||
|
-- only show /dev/xxx device
|
||||||
|
if device and device:match("/dev/") then
|
||||||
|
res[#res+1] = {}
|
||||||
|
local i = 0
|
||||||
|
for v in mount:gmatch("[^%s]+") do
|
||||||
|
i = i + 1
|
||||||
|
res[#res][h[i]] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
d.get_disk_info = function(device, wakeup)
|
||||||
|
--[[ return:
|
||||||
|
{
|
||||||
|
path, model, sn, size, size_mounted, flags, type, temp, p_table, logic_sec, phy_sec, sec_size, sata_ver, rota_rate, status, health,
|
||||||
|
partitions = {
|
||||||
|
1 = { number, name, sec_start, sec_end, size, size_mounted, fs, tag_name, type, flags, mount_point, usage, used, free, used_formated, free_formated},
|
||||||
|
2 = { number, name, sec_start, sec_end, size, size_mounted, fs, tag_name, type, flags, mount_point, usage, used, free, used_formated, free_formated},
|
||||||
|
...
|
||||||
|
}
|
||||||
|
--raid devices only
|
||||||
|
level, members, members_str
|
||||||
|
}
|
||||||
|
--]]
|
||||||
|
if not device then return end
|
||||||
|
local disk_info
|
||||||
|
local smart_info = get_smart_info(device)
|
||||||
|
|
||||||
|
-- check if divice is the member of raid
|
||||||
|
smart_info["p_table"] = is_raid_member(device..'0')
|
||||||
|
-- if status is not active(standby), only check smart_info.
|
||||||
|
-- if only weakup == true, weakup the disk and check parted_info.
|
||||||
|
if smart_info.status ~= "STANDBY" or wakeup or (smart_info["p_table"] and not smart_info["p_table"]:match("Raid")) or device:match("^md") then
|
||||||
|
disk_info = get_parted_info(device)
|
||||||
|
disk_info["sec_size"] = disk_info["logic_sec"] .. "/" .. disk_info["phy_sec"]
|
||||||
|
disk_info["size_formated"] = byte_format(tonumber(disk_info["size"]))
|
||||||
|
-- if status is standby, after get part info, the disk is weakuped, then get smart_info again for more informations
|
||||||
|
if smart_info.status ~= "ACTIVE" then smart_info = get_smart_info(device) end
|
||||||
|
else
|
||||||
|
disk_info = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(smart_info) do
|
||||||
|
disk_info[k] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
if disk_info.type and disk_info.type:match("md") then
|
||||||
|
local raid_info = d.list_raid_devices()[disk_info["path"]:match("/dev/(.+)")]
|
||||||
|
for k, v in pairs(raid_info) do
|
||||||
|
disk_info[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return disk_info
|
||||||
|
end
|
||||||
|
|
||||||
|
d.list_raid_devices = function()
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
|
||||||
|
local raid_devices = {}
|
||||||
|
if not fs.access("/proc/mdstat") then return raid_devices end
|
||||||
|
local mdstat = io.open("/proc/mdstat", "r")
|
||||||
|
for line in mdstat:lines() do
|
||||||
|
|
||||||
|
-- md1 : active raid1 sdb2[1] sda2[0]
|
||||||
|
-- md127 : active raid5 sdh1[6] sdg1[4] sdf1[3] sde1[2] sdd1[1] sdc1[0]
|
||||||
|
local device_info = {}
|
||||||
|
local mdpath, list = line:match("^(md%d+) : (.+)")
|
||||||
|
if mdpath then
|
||||||
|
local members = {}
|
||||||
|
for member in string.gmatch(list, "%S+") do
|
||||||
|
member_path = member:match("^(%S+)%[%d+%]")
|
||||||
|
if member_path then
|
||||||
|
member = '/dev/'..member_path
|
||||||
|
end
|
||||||
|
table.insert(members, member)
|
||||||
|
end
|
||||||
|
local active = table.remove(members, 1)
|
||||||
|
local level = "-"
|
||||||
|
if active == "active" then
|
||||||
|
level = table.remove(members, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local size = tonumber(fs.readfile(string.format("/sys/class/block/%s/size", mdpath)))
|
||||||
|
local ss = tonumber(fs.readfile(string.format("/sys/class/block/%s/queue/logical_block_size", mdpath)))
|
||||||
|
|
||||||
|
device_info["path"] = "/dev/"..mdpath
|
||||||
|
device_info["size"] = size*ss
|
||||||
|
device_info["size_formated"] = byte_format(size*ss)
|
||||||
|
device_info["active"] = active:upper()
|
||||||
|
device_info["level"] = level
|
||||||
|
device_info["members"] = members
|
||||||
|
device_info["members_str"] = table.concat(members, ", ")
|
||||||
|
|
||||||
|
-- Get more info from output of mdadm --detail
|
||||||
|
local detail = mddetail(device_info["path"])
|
||||||
|
device_info["status"] = detail["State"]:upper()
|
||||||
|
|
||||||
|
raid_devices[mdpath] = device_info
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mdstat:close()
|
||||||
|
|
||||||
|
return raid_devices
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Collect Devices information
|
||||||
|
--[[ return:
|
||||||
|
{
|
||||||
|
sda={
|
||||||
|
path, model, inuse, size_formated,
|
||||||
|
partitions={
|
||||||
|
{ name, inuse, size_formated }
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
..
|
||||||
|
}
|
||||||
|
--]]
|
||||||
|
d.list_devices = function()
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
|
||||||
|
-- get all device names (sdX and mmcblkX)
|
||||||
|
local target_devnames = {}
|
||||||
|
for dev in fs.dir("/dev") do
|
||||||
|
if dev:match("^sd[a-z]$")
|
||||||
|
or dev:match("^mmcblk%d+$")
|
||||||
|
or dev:match("^sata[a-z]$")
|
||||||
|
or dev:match("^nvme%d+n%d+$")
|
||||||
|
then
|
||||||
|
table.insert(target_devnames, dev)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local devices = {}
|
||||||
|
for i, bname in pairs(target_devnames) do
|
||||||
|
local device_info = {}
|
||||||
|
local device = "/dev/" .. bname
|
||||||
|
local size = tonumber(fs.readfile(string.format("/sys/class/block/%s/size", bname)) or "0")
|
||||||
|
local ss = tonumber(fs.readfile(string.format("/sys/class/block/%s/queue/logical_block_size", bname)) or "0")
|
||||||
|
local model = fs.readfile(string.format("/sys/class/block/%s/device/model", bname))
|
||||||
|
local partitions = {}
|
||||||
|
for part in nixio.fs.glob("/sys/block/" .. bname .."/" .. bname .. "*") do
|
||||||
|
local pname = nixio.fs.basename(part)
|
||||||
|
local psize = byte_format(tonumber(nixio.fs.readfile(part .. "/size"))*ss)
|
||||||
|
local mount_point = get_mount_point(pname)
|
||||||
|
if mount_point then device_info["inuse"] = true end
|
||||||
|
table.insert(partitions, {name = pname, size_formated = psize, inuse = mount_point})
|
||||||
|
end
|
||||||
|
|
||||||
|
device_info["path"] = device
|
||||||
|
device_info["size_formated"] = byte_format(size*ss)
|
||||||
|
device_info["model"] = model
|
||||||
|
device_info["partitions"] = partitions
|
||||||
|
-- true or false
|
||||||
|
device_info["inuse"] = device_info["inuse"] or get_mount_point(bname)
|
||||||
|
|
||||||
|
local udevinfo = {}
|
||||||
|
if luci.sys.exec("which udevadm") ~= "" then
|
||||||
|
local udevadm = io.popen("udevadm info --query=property --name="..device)
|
||||||
|
for attr in udevadm:lines() do
|
||||||
|
local k, v = attr:match("(%S+)=(%S+)")
|
||||||
|
udevinfo[k] = v
|
||||||
|
end
|
||||||
|
udevadm:close()
|
||||||
|
|
||||||
|
device_info["info"] = udevinfo
|
||||||
|
if udevinfo["ID_MODEL"] then device_info["model"] = udevinfo["ID_MODEL"] end
|
||||||
|
end
|
||||||
|
devices[bname] = device_info
|
||||||
|
end
|
||||||
|
-- luci.util.perror(luci.util.serialize_json(devices))
|
||||||
|
return devices
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get formart cmd
|
||||||
|
d.get_format_cmd = function()
|
||||||
|
local AVAILABLE_FMTS = {
|
||||||
|
ext2 = { cmd = "mkfs.ext2", option = "-F -E lazy_itable_init=1" },
|
||||||
|
ext3 = { cmd = "mkfs.ext3", option = "-F -E lazy_itable_init=1" },
|
||||||
|
ext4 = { cmd = "mkfs.ext4", option = "-F -E lazy_itable_init=1" },
|
||||||
|
fat32 = { cmd = "mkfs.vfat", option = "-F" },
|
||||||
|
exfat = { cmd = "mkexfat", option = "-f" },
|
||||||
|
hfsplus = { cmd = "mkhfs", option = "-f" },
|
||||||
|
ntfs = { cmd = "mkntfs", option = "-f" },
|
||||||
|
swap = { cmd = "mkswap", option = "" },
|
||||||
|
btrfs = { cmd = "mkfs.btrfs", option = "-f" }
|
||||||
|
}
|
||||||
|
result = {}
|
||||||
|
for fmt, obj in pairs(AVAILABLE_FMTS) do
|
||||||
|
local cmd = luci.sys.exec("/usr/bin/which " .. obj["cmd"])
|
||||||
|
if cmd:match(obj["cmd"]) then
|
||||||
|
result[fmt] = { cmd = cmd:match("^.+"..obj["cmd"]) ,option = obj["option"] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
d.create_raid = function(rname, rlevel, rmembers)
|
||||||
|
local mb = {}
|
||||||
|
for _, v in ipairs(rmembers) do
|
||||||
|
mb[v]=v
|
||||||
|
end
|
||||||
|
rmembers = {}
|
||||||
|
for _, v in pairs(mb) do
|
||||||
|
table.insert(rmembers, v)
|
||||||
|
end
|
||||||
|
if type(rname) == "string" then
|
||||||
|
if rname:match("^md%d-%s+") then
|
||||||
|
rname = "/dev/"..rname:match("^(md%d-)%s+")
|
||||||
|
elseif rname:match("^/dev/md%d-%s+") then
|
||||||
|
rname = "/dev/"..rname:match("^(/dev/md%d-)%s+")
|
||||||
|
elseif not rname:match("/") then
|
||||||
|
rname = "/dev/md/".. rname
|
||||||
|
else
|
||||||
|
return "ERR: Invalid raid name"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local mdnum = 0
|
||||||
|
for num=1,127 do
|
||||||
|
local md = io.open("/dev/md"..tostring(num), "r")
|
||||||
|
if md == nil then
|
||||||
|
mdnum = num
|
||||||
|
break
|
||||||
|
else
|
||||||
|
io.close(md)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if mdnum == 0 then return "ERR: Cannot find proper md number" end
|
||||||
|
rname = "/dev/md"..mdnum
|
||||||
|
end
|
||||||
|
|
||||||
|
if rlevel == "5" or rlevel == "6" then
|
||||||
|
if #rmembers < 3 then return "ERR: Not enough members" end
|
||||||
|
end
|
||||||
|
if rlevel == "10" then
|
||||||
|
if #rmembers < 4 then return "ERR: Not enough members" end
|
||||||
|
end
|
||||||
|
if #rmembers < 2 then return "ERR: Not enough members" end
|
||||||
|
local cmd = d.command.mdadm .. " --create "..rname.." --run --assume-clean --homehost=any --level=" .. rlevel .. " --raid-devices=" .. #rmembers .. " " .. table.concat(rmembers, " ")
|
||||||
|
local res = luci.util.exec(cmd)
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
d.gen_mdadm_config = function()
|
||||||
|
if not nixio.fs.access("/etc/config/mdadm") then return end
|
||||||
|
local uci = require "luci.model.uci"
|
||||||
|
local x = uci.cursor()
|
||||||
|
-- delete all array sections
|
||||||
|
x:foreach("mdadm", "array", function(s) x:delete("mdadm",s[".name"]) end)
|
||||||
|
local cmd = d.command.mdadm .. " -D -s"
|
||||||
|
--ARRAY /dev/md1 metadata=1.2 name=any:1 UUID=f998ae14:37621b27:5c49e850:051f6813
|
||||||
|
--ARRAY /dev/md3 metadata=1.2 name=any:3 UUID=c068c141:4b4232ca:f48cbf96:67d42feb
|
||||||
|
for _, v in ipairs(luci.util.execl(cmd)) do
|
||||||
|
local device, uuid = v:match("^ARRAY%s-([^%s]+)%s-[^%s]-%s-[^%s]-%s-UUID=([^%s]+)%s-")
|
||||||
|
if device and uuid then
|
||||||
|
local section_name = x:add("mdadm", "array")
|
||||||
|
x:set("mdadm", section_name, "device", device)
|
||||||
|
x:set("mdadm", section_name, "uuid", uuid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
x:commit("mdadm")
|
||||||
|
-- enable mdadm
|
||||||
|
luci.util.exec("/etc/init.d/mdadm enable")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- list btrfs filesystem device
|
||||||
|
-- {uuid={uuid, label, members, size, used}...}
|
||||||
|
d.list_btrfs_devices = function()
|
||||||
|
local btrfs_device = {}
|
||||||
|
if not d.command.btrfs then return btrfs_device end
|
||||||
|
local line, _uuid
|
||||||
|
for _, line in ipairs(luci.util.execl(d.command.btrfs .. " filesystem show -d --raw"))
|
||||||
|
do
|
||||||
|
local label, uuid = line:match("^Label:%s+([^%s]+)%s+uuid:%s+([^%s]+)")
|
||||||
|
if label and uuid then
|
||||||
|
_uuid = uuid
|
||||||
|
local _label = label:match("^'([^']+)'")
|
||||||
|
btrfs_device[_uuid] = {label = _label or label, uuid = uuid}
|
||||||
|
-- table.insert(btrfs_device, {label = label, uuid = uuid})
|
||||||
|
end
|
||||||
|
local used = line:match("Total devices[%w%s]+used%s+(%d+)$")
|
||||||
|
if used then
|
||||||
|
btrfs_device[_uuid]["used"] = tonumber(used)
|
||||||
|
btrfs_device[_uuid]["used_formated"] = byte_format(tonumber(used))
|
||||||
|
end
|
||||||
|
local size, device = line:match("devid[%w.%s]+size%s+(%d+)[%w.%s]+path%s+([^%s]+)$")
|
||||||
|
if size and device then
|
||||||
|
btrfs_device[_uuid]["size"] = btrfs_device[_uuid]["size"] and btrfs_device[_uuid]["size"] + tonumber(size) or tonumber(size)
|
||||||
|
btrfs_device[_uuid]["size_formated"] = byte_format(btrfs_device[_uuid]["size"])
|
||||||
|
btrfs_device[_uuid]["members"] = btrfs_device[_uuid]["members"] and btrfs_device[_uuid]["members"]..", "..device or device
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return btrfs_device
|
||||||
|
end
|
||||||
|
|
||||||
|
d.create_btrfs = function(blabel, blevel, bmembers)
|
||||||
|
-- mkfs.btrfs -L label -d blevel /dev/sda /dev/sdb
|
||||||
|
if not d.command.btrfs or type(bmembers) ~= "table" or next(bmembers) == nil then return "ERR no btrfs support or no members" end
|
||||||
|
local label = blabel and " -L " .. blabel or ""
|
||||||
|
local cmd = "mkfs.btrfs -f " .. label .. " -d " .. blevel .. " " .. table.concat(bmembers, " ")
|
||||||
|
return luci.util.exec(cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get btrfs info
|
||||||
|
-- {uuid, label, members, data_raid_level,metadata_raid_lavel, size, used, size_formated, used_formated, free, free_formated, usage}
|
||||||
|
d.get_btrfs_info = function(m_point)
|
||||||
|
local btrfs_info = {}
|
||||||
|
if not m_point or not d.command.btrfs then return btrfs_info end
|
||||||
|
local cmd = d.command.btrfs .. " filesystem show --raw " .. m_point
|
||||||
|
local _, line, uuid, _label, members
|
||||||
|
for _, line in ipairs(luci.util.execl(cmd)) do
|
||||||
|
if not uuid and not _label then
|
||||||
|
_label, uuid = line:match("^Label:%s+([^%s]+)%s+uuid:%s+([^s]+)")
|
||||||
|
else
|
||||||
|
local mb = line:match("%s+devid.+path%s+([^%s]+)")
|
||||||
|
if mb then
|
||||||
|
members = members and (members .. ", ".. mb) or mb
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not _label or not uuid then return btrfs_info end
|
||||||
|
local label = _label:match("^'([^']+)'")
|
||||||
|
cmd = d.command.btrfs .. " filesystem usage -b " .. m_point
|
||||||
|
local used, free, data_raid_level, metadata_raid_lavel
|
||||||
|
for _, line in ipairs(luci.util.execl(cmd)) do
|
||||||
|
if not used then
|
||||||
|
used = line:match("^%s+Used:%s+(%d+)")
|
||||||
|
elseif not free then
|
||||||
|
free = line:match("^%s+Free %(estimated%):%s+(%d+)")
|
||||||
|
elseif not data_raid_level then
|
||||||
|
data_raid_level = line:match("^Data,%s-(%w+)")
|
||||||
|
elseif not metadata_raid_lavel then
|
||||||
|
metadata_raid_lavel = line:match("^Metadata,%s-(%w+)")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if used and free and data_raid_level and metadata_raid_lavel then
|
||||||
|
used = tonumber(used)
|
||||||
|
free = tonumber(free)
|
||||||
|
btrfs_info = {
|
||||||
|
uuid = uuid,
|
||||||
|
label = label,
|
||||||
|
data_raid_level = data_raid_level,
|
||||||
|
metadata_raid_lavel = metadata_raid_lavel,
|
||||||
|
used = used,
|
||||||
|
free = free,
|
||||||
|
size = used + free,
|
||||||
|
size_formated = byte_format(used + free),
|
||||||
|
used_formated = byte_format(used),
|
||||||
|
free_formated = byte_format(free),
|
||||||
|
members = members,
|
||||||
|
usage = string.format("%.2f",(used / (free+used) * 100)) .. "%"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return btrfs_info
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get btrfs subvolume
|
||||||
|
-- {id={id, gen, top_level, path, snapshots, otime, default_subvolume}...}
|
||||||
|
d.get_btrfs_subv = function(m_point, snapshot)
|
||||||
|
local subvolume = {}
|
||||||
|
if not m_point or not d.command.btrfs then return subvolume end
|
||||||
|
|
||||||
|
-- get default subvolume
|
||||||
|
local cmd = d.command.btrfs .. " subvolume get-default " .. m_point
|
||||||
|
local res = luci.util.exec(cmd)
|
||||||
|
local default_subvolume_id = res:match("^ID%s+([^%s]+)")
|
||||||
|
|
||||||
|
-- get the root subvolume
|
||||||
|
if not snapshot then
|
||||||
|
local _, line, section_snap, _uuid, _otime, _id, _snap
|
||||||
|
cmd = d.command.btrfs .. " subvolume show ".. m_point
|
||||||
|
for _, line in ipairs(luci.util.execl(cmd)) do
|
||||||
|
if not section_snap then
|
||||||
|
if not _uuid then
|
||||||
|
_uuid = line:match("^%s-UUID:%s+([^%s]+)")
|
||||||
|
elseif not _otime then
|
||||||
|
_otime = line:match("^%s+Creation time:%s+(.+)")
|
||||||
|
elseif not _id then
|
||||||
|
_id = line:match("^%s+Subvolume ID:%s+([^%s]+)")
|
||||||
|
elseif line:match("^%s+(Snapshot%(s%):)") then
|
||||||
|
section_snap = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local snapshot = line:match("^%s+(.+)")
|
||||||
|
if snapshot then
|
||||||
|
_snap = _snap and (_snap ..", /".. snapshot) or ("/"..snapshot)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if _uuid and _otime and _id then
|
||||||
|
subvolume["0".._id] = {id = _id , uuid = _uuid, otime = _otime, snapshots = _snap, path = "/"}
|
||||||
|
if default_subvolume_id == _id then
|
||||||
|
subvolume["0".._id].default_subvolume = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get subvolume of btrfs
|
||||||
|
cmd = d.command.btrfs .. " subvolume list -gcu" .. (snapshot and "s " or " ") .. m_point
|
||||||
|
for _, line in ipairs(luci.util.execl(cmd)) do
|
||||||
|
-- ID 259 gen 11 top level 258 uuid 26ae0c59-199a-cc4d-bd58-644eb4f65d33 path 1a/2b'
|
||||||
|
local id, gen, top_level, uuid, path, otime, otime2
|
||||||
|
if snapshot then
|
||||||
|
id, gen, top_level, otime, otime2, uuid, path = line:match("^ID%s+([^%s]+)%s+gen%s+([^%s]+)%s+cgen.-top level%s+([^%s]+)%s+otime%s+([^%s]+)%s+([^%s]+)%s+uuid%s+([^%s]+)%s+path%s+([^%s]+)%s-$")
|
||||||
|
else
|
||||||
|
id, gen, top_level, uuid, path = line:match("^ID%s+([^%s]+)%s+gen%s+([^%s]+)%s+cgen.-top level%s+([^%s]+)%s+uuid%s+([^%s]+)%s+path%s+([^%s]+)%s-$")
|
||||||
|
end
|
||||||
|
if id and gen and top_level and uuid and path then
|
||||||
|
subvolume[id] = {id = id, gen = gen, top_level = top_level, otime = (otime and otime or "") .." ".. (otime2 and otime2 or ""), uuid = uuid, path = '/'.. path}
|
||||||
|
if not snapshot then
|
||||||
|
-- use btrfs subv show to get snapshots
|
||||||
|
local show_cmd = d.command.btrfs .. " subvolume show "..m_point.."/"..path
|
||||||
|
local __, line_show, section_snap
|
||||||
|
for __, line_show in ipairs(luci.util.execl(show_cmd)) do
|
||||||
|
if not section_snap then
|
||||||
|
local create_time = line_show:match("^%s+Creation time:%s+(.+)")
|
||||||
|
if create_time then
|
||||||
|
subvolume[id]["otime"] = create_time
|
||||||
|
elseif line_show:match("^%s+(Snapshot%(s%):)") then
|
||||||
|
section_snap = "true"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local snapshot = line_show:match("^%s+(.+)")
|
||||||
|
subvolume[id]["snapshots"] = subvolume[id]["snapshots"] and (subvolume[id]["snapshots"] .. ", /".. snapshot) or ("/"..snapshot)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if subvolume[default_subvolume_id] then
|
||||||
|
subvolume[default_subvolume_id].default_subvolume = 1
|
||||||
|
end
|
||||||
|
-- if m_point == "/tmp/.btrfs_tmp" then
|
||||||
|
-- luci.util.exec("umount " .. m_point)
|
||||||
|
-- end
|
||||||
|
return subvolume
|
||||||
|
end
|
||||||
|
|
||||||
|
d.format_partition = function(partition, fs)
|
||||||
|
local partition_name = "/dev/".. partition
|
||||||
|
if not nixio.fs.access(partition_name) then
|
||||||
|
return 500, "Partition NOT found!"
|
||||||
|
end
|
||||||
|
|
||||||
|
local format_cmd = d.get_format_cmd()
|
||||||
|
if not format_cmd[fs] then
|
||||||
|
return 500, "Filesystem NOT support!"
|
||||||
|
end
|
||||||
|
local cmd = format_cmd[fs].cmd .. " " .. format_cmd[fs].option .. " " .. partition_name
|
||||||
|
local res = luci.util.exec(cmd .. " 2>&1")
|
||||||
|
if res and res:lower():match("error+") then
|
||||||
|
return 500, res
|
||||||
|
else
|
||||||
|
return 200, "OK"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return d
|
|
@ -0,0 +1,7 @@
|
||||||
|
<%+cbi/valueheader%>
|
||||||
|
<% if self:cfgvalue(section) ~= false then %>
|
||||||
|
<input class="cbi-button cbi-button-<%=self.inputstyle or "button" %>" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> <% if self.view_disabled then %> disabled <% end %>/>
|
||||||
|
<% else %>
|
||||||
|
-
|
||||||
|
<% end %>
|
||||||
|
<%+cbi/valuefooter%>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<%+cbi/valueheader%>
|
||||||
|
<% if self:cfgvalue(section) ~= false then %>
|
||||||
|
<input class="cbi-button cbi-button-<%=self.inputstyle or "button" %>" onclick="event.preventDefault();partition_format('<%=self.partitions[section].name%>', '<%=self.format_cmd%>', '<%=self.inputtitle%>');" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> <% if self.view_disabled then %> disabled <% end %>/>
|
||||||
|
<% else %>
|
||||||
|
-
|
||||||
|
<% end %>
|
||||||
|
<%+cbi/valuefooter%>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<div style="display: inline-block;">
|
||||||
|
<% if self:cfgvalue(section) ~= false then %>
|
||||||
|
<input class="cbi-button cbi-button-<%=self.inputstyle or "button" %>" type="submit"" <% if self.disable then %>disabled <% end %><%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> />
|
||||||
|
<% else %>
|
||||||
|
-
|
||||||
|
<% end %>
|
||||||
|
</div>
|
37
luci-app-diskman/luasrc/view/diskman/cbi/xnullsection.htm
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<div class="cbi-section" id="cbi-<%=self.config%>-section">
|
||||||
|
<% if self.title and #self.title > 0 then -%>
|
||||||
|
<legend><%=self.title%></legend>
|
||||||
|
<%- end %>
|
||||||
|
<% if self.description and #self.description > 0 then -%>
|
||||||
|
<div class="cbi-section-descr"><%=self.description%></div>
|
||||||
|
<%- end %>
|
||||||
|
<div class="cbi-section-node">
|
||||||
|
<div id="cbi-<%=self.config%>-<%=tostring(self):sub(8)%>">
|
||||||
|
<% self:render_children(1, scope or {}) %>
|
||||||
|
</div>
|
||||||
|
<% if self.error and self.error[1] then -%>
|
||||||
|
<div class="cbi-section-error">
|
||||||
|
<ul><% for _, e in ipairs(self.error[1]) do -%>
|
||||||
|
<li>
|
||||||
|
<%- if e == "invalid" then -%>
|
||||||
|
<%:One or more fields contain invalid values!%>
|
||||||
|
<%- elseif e == "missing" then -%>
|
||||||
|
<%:One or more required fields have no value!%>
|
||||||
|
<%- else -%>
|
||||||
|
<%=pcdata(e)%>
|
||||||
|
<%- end -%>
|
||||||
|
</li>
|
||||||
|
<%- end %></ul>
|
||||||
|
</div>
|
||||||
|
<%- end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<%-
|
||||||
|
if type(self.hidden) == "table" then
|
||||||
|
for k, v in pairs(self.hidden) do
|
||||||
|
-%>
|
||||||
|
<input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" />
|
||||||
|
<%-
|
||||||
|
end
|
||||||
|
end
|
||||||
|
%>
|
88
luci-app-diskman/luasrc/view/diskman/cbi/xsimpleform.htm
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<% if not self.embedded then %>
|
||||||
|
<form method="post" enctype="multipart/form-data" action="<%=REQUEST_URI%>"<%=
|
||||||
|
attr("data-strings", luci.util.serialize_json({
|
||||||
|
label = {
|
||||||
|
choose = translate('-- Please choose --'),
|
||||||
|
custom = translate('-- custom --'),
|
||||||
|
},
|
||||||
|
path = {
|
||||||
|
resource = resource,
|
||||||
|
browser = url("admin/filebrowser")
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
%>>
|
||||||
|
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
|
||||||
|
<input type="hidden" name="token" value="<%=token%>" />
|
||||||
|
<input type="hidden" name="cbi.submit" value="1" /><%
|
||||||
|
end
|
||||||
|
|
||||||
|
%><div class="cbi-map" id="cbi-<%=self.config%>"><%
|
||||||
|
|
||||||
|
if self.title and #self.title > 0 then
|
||||||
|
%><h2 name="content"><%=self.title%></h2><%
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.description and #self.description > 0 then
|
||||||
|
%><div class="cbi-map-descr"><%=self.description%></div><%
|
||||||
|
end
|
||||||
|
|
||||||
|
self:render_children()
|
||||||
|
|
||||||
|
%></div><%
|
||||||
|
|
||||||
|
if self.message then
|
||||||
|
%><div class="alert-message notice"><%=self.message%></div><%
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.errmessage then
|
||||||
|
%><div class="alert-message warning"><%=self.errmessage%></div><%
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.embedded then
|
||||||
|
if type(self.hidden) == "table" then
|
||||||
|
local k, v
|
||||||
|
for k, v in pairs(self.hidden) do
|
||||||
|
%><input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" /><%
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local display_back = (self.redirect)
|
||||||
|
local display_cancel = (self.cancel ~= false and self.on_cancel)
|
||||||
|
local display_skip = (self.flow and self.flow.skip)
|
||||||
|
local display_submit = (self.submit ~= false)
|
||||||
|
local display_reset = (self.reset ~= false)
|
||||||
|
|
||||||
|
if display_back or display_cancel or display_skip or display_submit or display_reset then
|
||||||
|
%><div class="cbi-page-actions"><%
|
||||||
|
|
||||||
|
if display_back then
|
||||||
|
%><input class="cbi-button cbi-button-link" type="button" value="<%:Back to Overview%>" onclick="location.href='<%=pcdata(self.redirect)%>'" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_cancel then
|
||||||
|
local label = pcdata(self.cancel or translate("Cancel"))
|
||||||
|
%><input class="cbi-button cbi-button-link" type="button" value="<%=label%>" onclick="cbi_submit(this, 'cbi.cancel')" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_skip then
|
||||||
|
%><input class="cbi-button cbi-button-neutral" type="button" value="<%:Skip%>" onclick="cbi_submit(this, 'cbi.skip')" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_submit then
|
||||||
|
local label = pcdata(self.submit or translate("Submit"))
|
||||||
|
%><input class="cbi-button cbi-button-save" type="submit" value="<%=label%>" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_reset then
|
||||||
|
local label = pcdata(self.reset or translate("Reset"))
|
||||||
|
%><input class="cbi-button cbi-button-reset" type="reset" value="<%=label%>" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
%></div><%
|
||||||
|
end
|
||||||
|
|
||||||
|
%></form><%
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
|
||||||
|
<script type="text/javascript">cbi_init();</script>
|
108
luci-app-diskman/luasrc/view/diskman/disk_info.htm
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.onload = function () {
|
||||||
|
//disk partition info
|
||||||
|
let p_colors = ["#c0c0ff", "#fbbd00", "#e97c30", "#a0e0a0", "#e0c0ff"]
|
||||||
|
let lines = document.querySelectorAll('[id^=cbi-disk-]')
|
||||||
|
lines.forEach((item) => {
|
||||||
|
let dev = item.id.match(/cbi-disk-(.*)/)[1]
|
||||||
|
if (dev == "table") { return }
|
||||||
|
XHR.get('<%=luci.dispatcher.build_url("admin/system/diskman/get_disk_info")%>/' + dev, null, (x, disk_info) => {
|
||||||
|
// handle disk info
|
||||||
|
item.childNodes.forEach((cell) => {
|
||||||
|
if (cell && cell.attributes) {
|
||||||
|
if (cell.getAttribute("data-name") == "sn" || cell.childNodes[1] && cell.childNodes[1].id.match(/sn/)) {
|
||||||
|
cell.innerText = disk_info.sn || "-"
|
||||||
|
} else if (cell.getAttribute("data-name") == "temp" || cell.childNodes[1] && cell.childNodes[1].id.match(/temp/)) {
|
||||||
|
cell.innerText = disk_info.temp || "-"
|
||||||
|
} else if (cell.getAttribute("data-name") == "p_table" || cell.childNodes[1] && cell.childNodes[1].id.match(/p_table/)) {
|
||||||
|
cell.innerText = disk_info.p_table || "-"
|
||||||
|
} else if (cell.getAttribute("data-name") == "sata_ver" || cell.childNodes[1] && cell.childNodes[1].id.match(/sata_ver/)) {
|
||||||
|
cell.innerText = disk_info.sata_ver || "-"
|
||||||
|
} else if (cell.getAttribute("data-name") == "health" || cell.childNodes[1] && cell.childNodes[1].id.match(/health/)) {
|
||||||
|
cell.innerText = disk_info.health || "-"
|
||||||
|
} else if (cell.getAttribute("data-name") == "status" || cell.childNodes[1] && cell.childNodes[1].id.match(/status/)) {
|
||||||
|
cell.innerText = disk_info.status || "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// handle partitons info
|
||||||
|
if (disk_info.partitions && disk_info.partitions.length > 0) {
|
||||||
|
let partitons_div
|
||||||
|
if (item.nodeName == "TR") {
|
||||||
|
partitons_div = '<tr width="100%" style="white-space:nowrap;"><td style="margin:0px; padding:0px; border:0px; white-space:nowrap;" colspan="15">'
|
||||||
|
} else if (item.nodeName == "DIV") {
|
||||||
|
partitons_div = '<div class="tr cbi-section-table-row cbi-rowstyle-1"><div style="white-space:nowrap; position:absolute; width:100%">'
|
||||||
|
}
|
||||||
|
let expand = 0
|
||||||
|
let need_expand = 0
|
||||||
|
disk_info.partitions.forEach((part) => {
|
||||||
|
let p = part.size / disk_info.size * 100
|
||||||
|
if (p <= 8) {
|
||||||
|
expand += 8
|
||||||
|
need_expand += p
|
||||||
|
part.part_percent = 8
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let n = 0
|
||||||
|
disk_info.partitions.forEach((part) => {
|
||||||
|
let p = part.size / disk_info.size * 100
|
||||||
|
if (p > 8) {
|
||||||
|
part.part_percent = p * (100 - expand) / (100 - need_expand)
|
||||||
|
}
|
||||||
|
let part_percent = part.part_percent + '%'
|
||||||
|
let p_color = p_colors[n++]
|
||||||
|
if (n > 4) { n = 0 }
|
||||||
|
let inline_txt = (part.name != '-' && part.name || '') + ' ' + (part.fs != 'Free Space' && part.fs || '') + ' ' + part.size_formated + ' ' + (part.useage != '-' && part.useage || '')
|
||||||
|
let partiton_div = '<div title="' + inline_txt + '" style="color: #525F7F; display:inline-block; text-align:center;background-color:' + p_color + '; width:' + part_percent + '">' + inline_txt + '</div>'
|
||||||
|
partitons_div += partiton_div
|
||||||
|
})
|
||||||
|
if (item.nodeName == "TR") {
|
||||||
|
partitons_div += '</td></tr>'
|
||||||
|
} else if (item.nodeName == "DIV") {
|
||||||
|
partitons_div += '</div><div> </div></div>'
|
||||||
|
}
|
||||||
|
item.insertAdjacentHTML('afterend', partitons_div);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
//raid table
|
||||||
|
lines = document.querySelectorAll('[id^=cbi-_raid-]')
|
||||||
|
lines.forEach((item) => {
|
||||||
|
let dev = item.id.match(/cbi-_raid-(.*)/)[1]
|
||||||
|
if (dev == "table") { return }
|
||||||
|
console.log(dev)
|
||||||
|
XHR.get('<%=luci.dispatcher.build_url("admin/system/diskman/get_disk_info")%>/' + dev, null, (x, disk_info) => {
|
||||||
|
// handle raid info
|
||||||
|
item.childNodes.forEach((cell) => {
|
||||||
|
if (cell && cell.attributes) {
|
||||||
|
if (cell.getAttribute("data-name") == "p_table" || cell.childNodes[1] && cell.childNodes[1].id.match(/p_table/)) {
|
||||||
|
cell.innerText = disk_info.p_table || "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// handle partitons info
|
||||||
|
let partitons_div
|
||||||
|
if (item.nodeName == "TR") {
|
||||||
|
partitons_div = '<tr width="100%" style="white-space:nowrap;"><td style="margin:0px; padding:0px; border:0px; white-space:nowrap;" colspan="15">'
|
||||||
|
} else if (item.nodeName == "DIV") {
|
||||||
|
partitons_div = '<div class="tr cbi-section-table-row cbi-rowstyle-1"><div style="white-space:nowrap; position:absolute; width:100%">'
|
||||||
|
}
|
||||||
|
let n = 0
|
||||||
|
disk_info.partitions.forEach((part) => {
|
||||||
|
let part_percent = part.size / disk_info.size * 100 + '%'
|
||||||
|
let p_color = p_colors[n++]
|
||||||
|
if (n > 4) { n = 0 }
|
||||||
|
let inline_txt = (part.name != '-' && part.name || '') + ' ' + (part.fs != 'Free Space' && part.fs || '') + ' ' + part.size_formated + ' ' + (part.useage != '-' && part.useage || '')
|
||||||
|
let partiton_div = '<div title="' + inline_txt + '" style="display:inline-block; text-align:center;background-color:' + p_color + '; width:' + part_percent + '">' + inline_txt + '</div>'
|
||||||
|
partitons_div += partiton_div
|
||||||
|
})
|
||||||
|
if (item.nodeName == "TR") {
|
||||||
|
partitons_div += '</td></tr>'
|
||||||
|
} else if (item.nodeName == "DIV") {
|
||||||
|
partitons_div += '</div><div> </div></div>'
|
||||||
|
}
|
||||||
|
item.insertAdjacentHTML('afterend', partitons_div);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
129
luci-app-diskman/luasrc/view/diskman/partition_info.htm
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<style type="text/css">
|
||||||
|
#dialog_format {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
display: none;
|
||||||
|
z-index: 20000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dialog_format .dialog_box {
|
||||||
|
position: relative;
|
||||||
|
background: rgba(255, 255, 255);
|
||||||
|
top: 35%;
|
||||||
|
width: 40%;
|
||||||
|
min-width: 20em;
|
||||||
|
margin: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height:auto;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dialog_format .dialog_line {
|
||||||
|
margin-top: .5em;
|
||||||
|
margin-bottom: .5em;
|
||||||
|
margin-left: 2em;
|
||||||
|
margin-right: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dialog_format .dialog_box>h4,
|
||||||
|
#dialog_format .dialog_box>p,
|
||||||
|
#dialog_format .dialog_box>div {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dialog_format .dialog_box>img {
|
||||||
|
margin-right: 1em;
|
||||||
|
flex-basis: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dialog-format-active {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dialog-format-active #dialog_format {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="text/javascript">//<![CDATA[
|
||||||
|
function show_detail(dev, e) {
|
||||||
|
e.preventDefault()
|
||||||
|
window.open('<%=luci.dispatcher.build_url("admin/system/diskman/smartdetail")%>/' + dev,
|
||||||
|
'newwindow', 'height=480,width=800,top=100,left=200,toolbar=no,menubar=no,scrollbars=yes, resizable=no,location=no, status=no')
|
||||||
|
}
|
||||||
|
window.onload = function () {
|
||||||
|
// handle partition table
|
||||||
|
const btn_p_table = document.getElementById("widget.cbid.table.1.p_table") || document.getElementById("cbid.table.1.p_table")
|
||||||
|
const btn_p_table_raw_index = btn_p_table.selectedIndex
|
||||||
|
const val_name = document.getElementById("cbi-table-1-path").innerText.split('/').pop()
|
||||||
|
btn_p_table.onchange = function () {
|
||||||
|
let btn_p_table_index = btn_p_table.selectedIndex
|
||||||
|
if (btn_p_table_index != btn_p_table_raw_index) {
|
||||||
|
if (confirm("<%:Warnning !! \nTHIS WILL OVERWRITE EXISTING PARTITIONS!! \nModify the partition table?%>")) {
|
||||||
|
let p_table = btn_p_table.options[btn_p_table_index].value
|
||||||
|
XHR.get('<%=luci.dispatcher.build_url("admin/system/diskman/mk_p_table")%>', { dev: val_name, p_table: p_table }, (x, res) => {
|
||||||
|
if (res.code == 0) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// handle smartinfo
|
||||||
|
const url = location.href.split('/')
|
||||||
|
const dev = url[url.length - 1]
|
||||||
|
const btn_smart_detail = document.getElementById("cbi-table-1-health")
|
||||||
|
btn_smart_detail.children[0].onclick = show_detail.bind(this, dev)
|
||||||
|
}
|
||||||
|
function close_dialog() {
|
||||||
|
document.body.classList.remove('dialog-format-active')
|
||||||
|
document.documentElement.style.overflowY = 'scroll'
|
||||||
|
}
|
||||||
|
function do_format(partation_name){
|
||||||
|
let fs = document.getElementById("filesystem_list").value
|
||||||
|
let status = document.getElementById("format-status")
|
||||||
|
if(!fs) {
|
||||||
|
status.innerHTML = "<%:Please select file system!%>"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status.innerHTML = "<%:Formatting..%>"
|
||||||
|
let b = document.getElementById('btn_format')
|
||||||
|
b.disabled = true
|
||||||
|
let xhr = new XHR()
|
||||||
|
xhr.post('<%=luci.dispatcher.build_url("admin/system/diskman/format_partition")%>', { partation_name: partation_name, file_system: fs }, (x, res) => {
|
||||||
|
if (x.status == 200) {
|
||||||
|
status.innerHTML = x.statusText
|
||||||
|
location.reload();
|
||||||
|
}else{
|
||||||
|
status.innerHTML = x.statusText
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear_text(){
|
||||||
|
let s = document.getElementById('format-status')
|
||||||
|
s.innerHTML = ""
|
||||||
|
let b = document.getElementById('btn_format')
|
||||||
|
b.disabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function partition_format(partition_name, format_cmd, current_fs){
|
||||||
|
let list = ''
|
||||||
|
format_cmd.split(",").forEach(e => {
|
||||||
|
list = list + '<option value="'+e+'">'+e+'</option>'
|
||||||
|
});
|
||||||
|
document.getElementById('dialog_format') || document.body.insertAdjacentHTML("beforeend", '<div id="dialog_format"><div class="dialog_box"><div class="dialog_line"></div><div class="dialog_line"><span><%:Format partation:%> <b>'+partition_name+'</b></span><br><span id="format-status" style="color: red;"></span></div><div class="dialog_line"><select id="filesystem_list" class="cbi-input-select" onchange="clear_text()">'+list+'</select></div><div class="dialog_line" style="text-align: right;"><input type="button" class="cbi-button cbi-button-apply" id="btn_format" type="submit" value="<%:Format%>" onclick="do_format(`'+partition_name+'`)" /> <input type="button"class="cbi-button cbi-button-reset" type="reset" value="<%:Cancel%>" onclick="close_dialog()" /></div><div class="dialog_line"></div></div></div>>')
|
||||||
|
document.body.classList.add('dialog-format-active')
|
||||||
|
document.documentElement.style.overflowY = 'hidden'
|
||||||
|
let fs_list = document.getElementById("filesystem_list")
|
||||||
|
fs_list.value = current_fs
|
||||||
|
}
|
||||||
|
</script>
|
79
luci-app-diskman/luasrc/view/diskman/smart_detail.htm
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>S.M.A.R.T detail of <%=dev%></title>
|
||||||
|
<link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" />
|
||||||
|
<script type="text/javascript">//<![CDATA[
|
||||||
|
let formData = new FormData()
|
||||||
|
let xhr = new XMLHttpRequest()
|
||||||
|
xhr.open("GET", '<%=luci.dispatcher.build_url("admin", "system", "diskman", "smartattr", dev)%>', true)
|
||||||
|
xhr.onload = function () {
|
||||||
|
let st = JSON.parse(xhr.responseText)
|
||||||
|
let tb = document.getElementById('smart_attr_table');
|
||||||
|
if (st && tb) {
|
||||||
|
/* clear all rows */
|
||||||
|
while (tb.rows.length > 1)
|
||||||
|
tb.deleteRow(1);
|
||||||
|
|
||||||
|
for (var i = 0; i < st.length; i++) {
|
||||||
|
var tr = tb.insertRow(-1);
|
||||||
|
tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
|
||||||
|
var td = null
|
||||||
|
<% if dev: match("nvme") then %>
|
||||||
|
tr.insertCell(-1).innerHTML = st[i].key;
|
||||||
|
tr.insertCell(-1).innerHTML = st[i].value;
|
||||||
|
<% else %>
|
||||||
|
tr.insertCell(-1).innerHTML = st[i].id;
|
||||||
|
tr.insertCell(-1).innerHTML = st[i].attrbute;
|
||||||
|
tr.insertCell(-1).innerHTML = st[i].flag;
|
||||||
|
tr.insertCell(-1).innerHTML = st[i].value;
|
||||||
|
tr.insertCell(-1).innerHTML = st[i].worst;
|
||||||
|
tr.insertCell(-1).innerHTML = st[i].thresh;
|
||||||
|
tr.insertCell(-1).innerHTML = st[i].type;
|
||||||
|
tr.insertCell(-1).innerHTML = st[i].updated;
|
||||||
|
tr.insertCell(-1).innerHTML = st[i].raw;
|
||||||
|
if ((st[i].id == '05' || st[i].id == 'C5') && st[i].raw != '0') {
|
||||||
|
tr.style.cssText = "background-color:red !important;";
|
||||||
|
}
|
||||||
|
<% end %>
|
||||||
|
}
|
||||||
|
if (tb.rows.length == 1) {
|
||||||
|
var tr = tb.insertRow(-1);
|
||||||
|
tr.className = 'cbi-section-table-row';
|
||||||
|
var td = tr.insertCell(-1);
|
||||||
|
td.colSpan = 4;
|
||||||
|
td.innerHTML = '<em><br /><%:No Attrbute to display.%></em>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.send(formData)
|
||||||
|
//]]></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="maincontainer">
|
||||||
|
<fieldset class="cbi-section">
|
||||||
|
<legend><%:S.M.A.R.T Attrbutes%>: /dev/<%=dev%></legend>
|
||||||
|
<table class="cbi-section-table" id="smart_attr_table">
|
||||||
|
<tr class="cbi-section-table-titles">
|
||||||
|
<% if dev:match("nvme") then %>
|
||||||
|
<!-- <th class="cbi-section-table-cell"><%:KEY%></th>
|
||||||
|
<th class="cbi-section-table-cell"><%:VALUE%></th> -->
|
||||||
|
<% else %>
|
||||||
|
<th class="cbi-section-table-cell"><%:ID%></th>
|
||||||
|
<th class="cbi-section-table-cell"><%:Attrbute%></th>
|
||||||
|
<th class="cbi-section-table-cell"><%:Flag%></th>
|
||||||
|
<th class="cbi-section-table-cell"><%:Value%></th>
|
||||||
|
<th class="cbi-section-table-cell"><%:Worst%></th>
|
||||||
|
<th class="cbi-section-table-cell"><%:Thresh%></th>
|
||||||
|
<th class="cbi-section-table-cell"><%:Type%></th>
|
||||||
|
<th class="cbi-section-table-cell"><%:Updated%></th>
|
||||||
|
<th class="cbi-section-table-cell"><%:Raw%></th>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
<tr class="cbi-section-table-row">
|
||||||
|
<td colspan="4"><em><br /><%:Collecting data...%></em></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
239
luci-app-diskman/po/zh-cn/diskman.po
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr "Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
|
||||||
|
msgid "DiskMan"
|
||||||
|
msgstr "DiskMan 磁盘管理"
|
||||||
|
|
||||||
|
msgid "Manage Disks over LuCI."
|
||||||
|
msgstr "通过 LuCI 管理磁盘"
|
||||||
|
|
||||||
|
msgid "Rescan Disks"
|
||||||
|
msgstr "重新扫描磁盘"
|
||||||
|
|
||||||
|
msgid "Disks"
|
||||||
|
msgstr "磁盘"
|
||||||
|
|
||||||
|
msgid "Path"
|
||||||
|
msgstr "路径"
|
||||||
|
|
||||||
|
msgid "Serial Number"
|
||||||
|
msgstr "序列号"
|
||||||
|
|
||||||
|
msgid "Temp"
|
||||||
|
msgstr "温度"
|
||||||
|
|
||||||
|
msgid "Partition Table"
|
||||||
|
msgstr "分区表"
|
||||||
|
|
||||||
|
msgid "SATA Version"
|
||||||
|
msgstr "SATA 版本"
|
||||||
|
|
||||||
|
msgid "Health"
|
||||||
|
msgstr "健康"
|
||||||
|
|
||||||
|
msgid "File System"
|
||||||
|
msgstr "文件系统"
|
||||||
|
|
||||||
|
msgid "Mount Options"
|
||||||
|
msgstr "挂载选项"
|
||||||
|
|
||||||
|
msgid "Mount"
|
||||||
|
msgstr "挂载"
|
||||||
|
|
||||||
|
msgid "Umount"
|
||||||
|
msgstr "卸载"
|
||||||
|
|
||||||
|
msgid "Eject"
|
||||||
|
msgstr "弹出"
|
||||||
|
|
||||||
|
msgid "New"
|
||||||
|
msgstr "创建"
|
||||||
|
|
||||||
|
msgid "Remove"
|
||||||
|
msgstr "移除"
|
||||||
|
|
||||||
|
msgid "Format"
|
||||||
|
msgstr "格式化"
|
||||||
|
|
||||||
|
msgid "Start Sector"
|
||||||
|
msgstr "起始扇区"
|
||||||
|
|
||||||
|
msgid "End Sector"
|
||||||
|
msgstr "中止扇区"
|
||||||
|
|
||||||
|
msgid "Usage"
|
||||||
|
msgstr "用量"
|
||||||
|
|
||||||
|
msgid "Used"
|
||||||
|
msgstr "已使用"
|
||||||
|
|
||||||
|
msgid "Free Space"
|
||||||
|
msgstr "空闲空间"
|
||||||
|
|
||||||
|
msgid "Model"
|
||||||
|
msgstr "型号"
|
||||||
|
|
||||||
|
msgid "Size"
|
||||||
|
msgstr "容量"
|
||||||
|
|
||||||
|
msgid "Status"
|
||||||
|
msgstr "状态"
|
||||||
|
|
||||||
|
msgid "Mount Point"
|
||||||
|
msgstr "挂载点"
|
||||||
|
|
||||||
|
msgid "Sector Size"
|
||||||
|
msgstr "扇区/物理扇区大小"
|
||||||
|
|
||||||
|
msgid "Rotation Rate"
|
||||||
|
msgstr "转速"
|
||||||
|
|
||||||
|
msgid "RAID Devices"
|
||||||
|
msgstr "RAID 设备"
|
||||||
|
|
||||||
|
msgid "RAID mode"
|
||||||
|
msgstr "RAID 模式"
|
||||||
|
|
||||||
|
msgid "Members"
|
||||||
|
msgstr "成员"
|
||||||
|
|
||||||
|
msgid "Active"
|
||||||
|
msgstr "活动"
|
||||||
|
|
||||||
|
msgid "RAID Creation"
|
||||||
|
msgstr "RAID 创建"
|
||||||
|
|
||||||
|
msgid "Raid Name"
|
||||||
|
msgstr "RAID 名称"
|
||||||
|
|
||||||
|
msgid "Raid Level"
|
||||||
|
msgstr "RAID 级别"
|
||||||
|
|
||||||
|
msgid "Raid Member"
|
||||||
|
msgstr "磁盘阵列成员"
|
||||||
|
|
||||||
|
msgid "Create Raid"
|
||||||
|
msgstr "创建 RAID"
|
||||||
|
|
||||||
|
msgid "Partition Management"
|
||||||
|
msgstr "分区管理"
|
||||||
|
|
||||||
|
msgid "Partition Disk over LuCI."
|
||||||
|
msgstr "通过LuCI分区磁盘。"
|
||||||
|
|
||||||
|
msgid "Device Info"
|
||||||
|
msgstr "设备信息"
|
||||||
|
|
||||||
|
msgid "Disk Man"
|
||||||
|
msgstr "磁盘管理"
|
||||||
|
|
||||||
|
msgid "Partitions Info"
|
||||||
|
msgstr "分区信息"
|
||||||
|
|
||||||
|
msgid "Default 2048 sector alignment, support +size{b,k,m,g,t} in End Sector"
|
||||||
|
msgstr "默认2048扇区对齐,【中止扇区】支持 +容量{b,k,m,g,t} 格式,例:+500m +10g +1t"
|
||||||
|
|
||||||
|
msgid "Multiple Devices Btrfs Creation"
|
||||||
|
msgstr "Btrfs 阵列创建"
|
||||||
|
|
||||||
|
msgid "Label"
|
||||||
|
msgstr "卷标"
|
||||||
|
|
||||||
|
msgid "Btrfs Label"
|
||||||
|
msgstr "Btrfs 卷标"
|
||||||
|
|
||||||
|
msgid "Btrfs Raid Level"
|
||||||
|
msgstr "Btrfs Raid 级别"
|
||||||
|
|
||||||
|
msgid "Btrfs Member"
|
||||||
|
msgstr "Btrfs 整列成员"
|
||||||
|
|
||||||
|
msgid "Create Btrfs"
|
||||||
|
msgstr "创建 Btrfs"
|
||||||
|
|
||||||
|
msgid "New Snapshot"
|
||||||
|
msgstr "新建快照"
|
||||||
|
|
||||||
|
msgid "SubVolumes"
|
||||||
|
msgstr "子卷"
|
||||||
|
|
||||||
|
msgid "Top Level"
|
||||||
|
msgstr "父ID"
|
||||||
|
|
||||||
|
msgid "Manage Btrfs"
|
||||||
|
msgstr "Btrfs 管理"
|
||||||
|
|
||||||
|
msgid "Otime"
|
||||||
|
msgstr "创建时间"
|
||||||
|
|
||||||
|
msgid "Snapshots"
|
||||||
|
msgstr "快照"
|
||||||
|
|
||||||
|
msgid "Set Default"
|
||||||
|
msgstr "默认子卷"
|
||||||
|
|
||||||
|
msgid "Source Path"
|
||||||
|
msgstr "源目录"
|
||||||
|
|
||||||
|
msgid "Readonly"
|
||||||
|
msgstr "只读"
|
||||||
|
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "删除"
|
||||||
|
|
||||||
|
msgid "Create"
|
||||||
|
msgstr "创建"
|
||||||
|
|
||||||
|
msgid "Destination Path (optional)"
|
||||||
|
msgstr "目标目录(可选)"
|
||||||
|
|
||||||
|
msgid "Metadata"
|
||||||
|
msgstr "元数据"
|
||||||
|
|
||||||
|
msgid "Data"
|
||||||
|
msgstr "数据"
|
||||||
|
|
||||||
|
msgid "Btrfs Info"
|
||||||
|
msgstr "Btrfs 信息"
|
||||||
|
|
||||||
|
msgid "The source path for create the snapshot"
|
||||||
|
msgstr "创建快照的源数据目录"
|
||||||
|
|
||||||
|
msgid "The path where you want to store the snapshot"
|
||||||
|
msgstr "存放快照数据目录"
|
||||||
|
|
||||||
|
msgid "Please input Source Path of snapshot, Source Path must start with '/'"
|
||||||
|
msgstr "请输入快照源路径,源路径必须以'/'开头"
|
||||||
|
|
||||||
|
msgid "Please input Subvolume Path, Subvolume must start with '/'"
|
||||||
|
msgstr "请输入子卷路径,子卷路径必须以'/'开头"
|
||||||
|
|
||||||
|
msgid "is in use! please unmount it first!"
|
||||||
|
msgstr "正在被使用!请先卸载!"
|
||||||
|
|
||||||
|
msgid "Partition NOT found!"
|
||||||
|
msgstr "分区未找到!"
|
||||||
|
|
||||||
|
msgid "Filesystem NOT support!"
|
||||||
|
msgstr "文件系统不支持!"
|
||||||
|
|
||||||
|
msgid "Invalid Start Sector!"
|
||||||
|
msgstr "无效的起始扇区!"
|
||||||
|
|
||||||
|
msgid "Invalid End Sector"
|
||||||
|
msgstr "无效的终止扇区!"
|
||||||
|
|
||||||
|
msgid "Partition not exists!"
|
||||||
|
msgstr "分区不存在!"
|
||||||
|
|
||||||
|
msgid "Creation"
|
||||||
|
msgstr "创建"
|
||||||
|
|
||||||
|
msgid "Please select file system!"
|
||||||
|
msgstr "请选择文件系统!"
|
||||||
|
|
||||||
|
msgid "Format partation:"
|
||||||
|
msgstr "格式化分区:"
|
||||||
|
|
||||||
|
msgid "Warnning !! \nTHIS WILL OVERWRITE EXISTING PARTITIONS!! \nModify the partition table?"
|
||||||
|
msgstr "警告!!\n此操作会覆盖现有分区\n确定修改分区表?"
|
18
luci-app-netdata/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# 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 Netdata
|
||||||
|
LUCI_DEPENDS:=+netdata
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
|
||||||
|
PKG_NAME:=luci-app-netdata
|
||||||
|
PKG_VERSION:=1.0
|
||||||
|
PKG_RELEASE:=3
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
6
luci-app-netdata/luasrc/controller/netdata.lua
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module("luci.controller.netdata", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
|
||||||
|
entry({"admin", "status", "netdata"}, template("netdata/netdata"), _("NetData"), 10).leaf = true
|
||||||
|
end
|
9
luci-app-netdata/luasrc/view/netdata/netdata.htm
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<%+header%>
|
||||||
|
<div class="cbi-map">
|
||||||
|
<h2 name="content"><%=translate("NetData")%></h2>
|
||||||
|
<iframe id="netdata" style="width: 100%; min-height: 1200px; border: none; border-radius: 3px;"></iframe>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.getElementById("netdata").src = "http://" + window.location.hostname + ":19999";
|
||||||
|
</script>
|
||||||
|
<%+footer%>
|
5
luci-app-netdata/po/zh-cn/netdata.po
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||||
|
|
||||||
|
msgid "NetData"
|
||||||
|
msgstr "实时监控"
|
72
luci-app-turboacc/Makefile
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# SPDX-Identifier-License: GPL-3.0-only
|
||||||
|
#
|
||||||
|
# Copyright (C) 2018 Lean <coolsnowwolf@gmail.com>
|
||||||
|
# Copyright (C) 2019-2021 ImmortalWrt.org
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=luci-app-turboacc
|
||||||
|
PKG_RELEASE:=$(COMMITCOUNT)
|
||||||
|
|
||||||
|
PKG_LICENSE:=GPL-3.0-only
|
||||||
|
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||||
|
|
||||||
|
PKG_CONFIG_DEPENDS:= \
|
||||||
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_BBR_CCA \
|
||||||
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_DNSFORWARDER \
|
||||||
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_DNSPROXY \
|
||||||
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_OFFLOADING \
|
||||||
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_SHORTCUT_FE \
|
||||||
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_SHORTCUT_FE_DRV
|
||||||
|
|
||||||
|
LUCI_TITLE:=LuCI support for Flow Offload / Shortcut-FE
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
LUCI_DEPENDS:=+pdnsd-alt \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_BBR_CCA:kmod-tcp-bbr \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_DNSFORWARDER:dnsforwarder \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_DNSPROXY:dnsproxy \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_OFFLOADING:kmod-ipt-offload \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_SHORTCUT_FE:kmod-shortcut-fe-cm \
|
||||||
|
+PACKAGE_$(PKG_NAME)_INCLUDE_SHORTCUT_FE_DRV:kmod-shortcut-fe-drv
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/config
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_OFFLOADING
|
||||||
|
bool "Include Flow Offload"
|
||||||
|
depends on (PACKAGE_$(PKG_NAME)_INCLUDE_SHORTCUT_FE=n && PACKAGE_$(PKG_NAME)_INCLUDE_SHORTCUT_FE_DRV=n)
|
||||||
|
default y if TARGET_ramips
|
||||||
|
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_SHORTCUT_FE_DRV
|
||||||
|
bool "Include Shortcut-FE for ECM"
|
||||||
|
depends on PACKAGE_$(PKG_NAME)_INCLUDE_SHORTCUT_FE=n
|
||||||
|
default y if (TARGET_ipq60xxx||TARGET_ipq806x||TARGET_ipq807x)
|
||||||
|
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_SHORTCUT_FE
|
||||||
|
bool "Include Shortcut-FE"
|
||||||
|
default y if !(TARGET_ipq60xxx||TARGET_ipq806x||TARGET_ipq807x||TARGET_ramips)
|
||||||
|
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_BBR_CCA
|
||||||
|
bool "Include BBR CCA"
|
||||||
|
default y
|
||||||
|
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_DNSFORWARDER
|
||||||
|
bool "Include DNSForwarder"
|
||||||
|
default n
|
||||||
|
|
||||||
|
config PACKAGE_$(PKG_NAME)_INCLUDE_DNSPROXY
|
||||||
|
bool "Include DNSProxy"
|
||||||
|
default n
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/preinst
|
||||||
|
#!/bin/sh
|
||||||
|
sed -i 's/s:option(Flag, "fullcone", translate("Enable FullCone NAT"))/-- e:option(Flag,"fullcone",translate("Enable FullCone NAT"))/g' $${IPKG_INSTROOT}/usr/lib/lua/luci/model/cbi/firewall/zones.lua
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/postrm
|
||||||
|
#!/bin/sh
|
||||||
|
sed -i 's/-- s:option(Flag, "fullcone", translate("Enable FullCone NAT"))/s:option(Flag, "fullcone", translate("Enable FullCone NAT"))/g' $${IPKG_INSTROOT}/usr/lib/lua/luci/model/cbi/firewall/zones.lua
|
||||||
|
endef
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
39
luci-app-turboacc/luasrc/controller/turboacc.lua
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
module("luci.controller.turboacc", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
if not nixio.fs.access("/etc/config/turboacc") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local page
|
||||||
|
page = entry({"admin", "network", "turboacc"}, cbi("turboacc"), _("Turbo ACC Center"), 1000)
|
||||||
|
page.i18n = "turboacc"
|
||||||
|
page.dependent = true
|
||||||
|
|
||||||
|
entry({"admin", "network", "turboacc", "status"}, call("action_status"))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fastpath_status()
|
||||||
|
return luci.sys.call("/etc/init.d/turboacc check_status fastpath") == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bbr_status()
|
||||||
|
return luci.sys.call("/etc/init.d/turboacc check_status bbr") == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fullconenat_status()
|
||||||
|
return luci.sys.call("/etc/init.d/turboacc check_status fullconenat") == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dnscaching_status()
|
||||||
|
return luci.sys.call("/etc/init.d/turboacc check_status dns") == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function action_status()
|
||||||
|
luci.http.prepare_content("application/json")
|
||||||
|
luci.http.write_json({
|
||||||
|
fastpath_state = fastpath_status(),
|
||||||
|
fullconenat_state = fullconenat_status(),
|
||||||
|
bbr_state = bbr_status(),
|
||||||
|
dnscaching_state = dnscaching_status()
|
||||||
|
})
|
||||||
|
end
|
67
luci-app-turboacc/luasrc/model/cbi/turboacc.lua
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
local kernel_version = luci.sys.exec("echo -n $(uname -r)")
|
||||||
|
|
||||||
|
m = Map("turboacc")
|
||||||
|
m.title = translate("Turbo ACC Acceleration Settings")
|
||||||
|
m.description = translate("Opensource Flow Offloading driver (Fast Path or Hardware NAT)")
|
||||||
|
|
||||||
|
m:append(Template("turboacc/turboacc_status"))
|
||||||
|
|
||||||
|
s = m:section(TypedSection, "turboacc", "")
|
||||||
|
s.addremove = false
|
||||||
|
s.anonymous = true
|
||||||
|
|
||||||
|
if nixio.fs.access("/lib/modules/" .. kernel_version .. "/xt_FLOWOFFLOAD.ko") then
|
||||||
|
sw_flow = s:option(Flag, "sw_flow", translate("Software flow offloading"))
|
||||||
|
sw_flow.default = 0
|
||||||
|
sw_flow.description = translate("Software based offloading for routing/NAT")
|
||||||
|
sw_flow:depends("sfe_flow", 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
if luci.sys.call("cat /etc/openwrt_release | grep -q mt762") == 0 then
|
||||||
|
hw_flow = s:option(Flag, "hw_flow", translate("Hardware flow offloading"))
|
||||||
|
hw_flow.default = 0
|
||||||
|
hw_flow.description = translate("Requires hardware NAT support. Implemented at least for mt762x")
|
||||||
|
hw_flow:depends("sw_flow", 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
if nixio.fs.access("/lib/modules/" .. kernel_version .. "/shortcut-fe-cm.ko") then
|
||||||
|
sfe_flow = s:option(Flag, "sfe_flow", translate("Shortcut-FE flow offloading"))
|
||||||
|
sfe_flow.default = 0
|
||||||
|
sfe_flow.description = translate("Shortcut-FE based offloading for routing/NAT")
|
||||||
|
sfe_flow:depends("sw_flow", 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
if nixio.fs.access("/lib/modules/" .. kernel_version .. "/tcp_bbr.ko") then
|
||||||
|
bbr_cca = s:option(Flag, "bbr_cca", translate("BBR CCA"))
|
||||||
|
bbr_cca.default = 0
|
||||||
|
bbr_cca.description = translate("Using BBR CCA can improve TCP network performance effectively")
|
||||||
|
end
|
||||||
|
|
||||||
|
if nixio.fs.access("/lib/modules/" .. kernel_version .. "/xt_FULLCONENAT.ko") then
|
||||||
|
fullcone_nat = s:option(Flag, "fullcone_nat", translate("FullCone NAT"))
|
||||||
|
fullcone_nat.default = 0
|
||||||
|
fullcone_nat.description = translate("Using FullCone NAT can improve gaming performance effectively")
|
||||||
|
end
|
||||||
|
|
||||||
|
dns_caching = s:option(Flag, "dns_caching", translate("DNS Caching"))
|
||||||
|
dns_caching.default = 0
|
||||||
|
dns_caching.rmempty = false
|
||||||
|
dns_caching.description = translate("Enable DNS Caching and anti ISP DNS pollution")
|
||||||
|
|
||||||
|
dns_caching_mode = s:option(ListValue, "dns_caching_mode", translate("Resolve DNS Mode"), translate("DNS Program"))
|
||||||
|
dns_caching_mode:value("1", translate("Using PDNSD to query and cache"))
|
||||||
|
if nixio.fs.access("/usr/bin/dnsforwarder") then
|
||||||
|
dns_caching_mode:value("2", translate("Using DNSForwarder to query and cache"))
|
||||||
|
end
|
||||||
|
if nixio.fs.access("/usr/bin/dnsproxy") then
|
||||||
|
dns_caching_mode:value("3", translate("Using DNSProxy to query and cache"))
|
||||||
|
end
|
||||||
|
dns_caching_mode.default = 1
|
||||||
|
dns_caching_mode:depends("dns_caching", 1)
|
||||||
|
|
||||||
|
dns_caching_dns = s:option(Value, "dns_caching_dns", translate("Upsteam DNS Server"))
|
||||||
|
dns_caching_dns.default = "114.114.114.114,114.114.115.115,223.5.5.5,223.6.6.6,180.76.76.76,119.29.29.29,119.28.28.28,1.2.4.8,210.2.4.8"
|
||||||
|
dns_caching_dns.description = translate("Muitiple DNS server can saperate with ','")
|
||||||
|
dns_caching_dns:depends("dns_caching", 1)
|
||||||
|
|
||||||
|
return m
|
25
luci-app-turboacc/luasrc/view/turboacc/turboacc_status.htm
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<fieldset class="cbi-section">
|
||||||
|
<legend><%:Running Status%></legend>
|
||||||
|
<table width="100%" cellspacing="10" id="_turboacc_status_table">
|
||||||
|
<tr><td width="33%"><%:Flow Offloading%></td><td id="_fastpath_state"><em><%:Collecting data...%></em></td></tr>
|
||||||
|
<tr><td width="33%"><%:FullCone NAT%></td><td id="_fullconenat_state"><em><%:Collecting data...%></em></td></tr>
|
||||||
|
<tr><td width="33%"><%:BBR CCA%></td><td id="_bbr_state"><em><%:Collecting data...%></em></td></tr>
|
||||||
|
<tr><td width="33%"><%:DNS Caching%></td><td id="_dnscaching_state"><em><%:Collecting data...%></em></td></tr>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<script type="text/javascript">//<![CDATA[
|
||||||
|
var fastpath_state = document.getElementById('_fastpath_state');
|
||||||
|
var bbr_state = document.getElementById('_bbr_state');
|
||||||
|
var fullconenat_state = document.getElementById('_fullconenat_state');
|
||||||
|
var dnscaching_state = document.getElementById('_dnscaching_state');
|
||||||
|
XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "network", "turboacc", "status")%>', null, function(x, status) {
|
||||||
|
if ( x && x.status == 200 ) {
|
||||||
|
fastpath_state.innerHTML = status.fastpath_state ? '<em><b><font color=green><%=luci.sys.exec("/etc/init.d/turboacc check_status fastpath")%></font></b></em>' : '<em><b><font color=red><%:NOT RUNNING%></font></b></em>';
|
||||||
|
fullconenat_state.innerHTML = status.fullconenat_state ? '<em><b><font color=green><%:RUNNING%></font></b></em>' : '<em><b><font color=red><%:NOT RUNNING%></font></b></em>';
|
||||||
|
bbr_state.innerHTML = status.bbr_state ? '<em><b><font color=green><%:RUNNING%></font></b></em>' : '<em><b><font color=red><%:NOT RUNNING%></font></b></em>';
|
||||||
|
dnscaching_state.innerHTML = status.dnscaching_state ? '<em><b><font color=green><%:RUNNING%></font></b></em>' : '<em><b><font color=red><%:NOT RUNNING%></font></b></em>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//]]>
|
||||||
|
//]]></script>
|
99
luci-app-turboacc/po/zh-cn/turboacc.po
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
msgid "Turbo ACC Center"
|
||||||
|
msgstr "Turbo ACC 网络加速"
|
||||||
|
|
||||||
|
msgid "Turbo ACC Acceleration Settings"
|
||||||
|
msgstr "Turbo ACC 网络加速设置"
|
||||||
|
|
||||||
|
msgid "Linux Flow Offload Forwarding Engine Settings"
|
||||||
|
msgstr "Linux Flow Offload Forwarding 转发加速引擎设置"
|
||||||
|
|
||||||
|
msgid "Opensource Flow Offloading driver (Fast Path or Hardware NAT)"
|
||||||
|
msgstr "开源流量分载驱动 (支持 Fast Path 或者 硬件 NAT)"
|
||||||
|
|
||||||
|
msgid "Software flow offloading"
|
||||||
|
msgstr "软件流量分载"
|
||||||
|
|
||||||
|
msgid "Software based offloading for routing/NAT"
|
||||||
|
msgstr "基于软件的 Routing/NAT 分载"
|
||||||
|
|
||||||
|
msgid "Hardware flow offloading"
|
||||||
|
msgstr "硬件流量分载"
|
||||||
|
|
||||||
|
msgid "Requires hardware NAT support. Implemented at least for mt762x"
|
||||||
|
msgstr "需要硬件 NAT 支持。目前 mt762x 已实现"
|
||||||
|
|
||||||
|
msgid "Shortcut-FE flow offloading"
|
||||||
|
msgstr "Shortcut-FE 流量分载"
|
||||||
|
|
||||||
|
msgid "Shortcut-FE based offloading for routing/NAT"
|
||||||
|
msgstr "基于 Shortcut-FE 的 Routing/NAT 分载"
|
||||||
|
|
||||||
|
msgid "Bridge Acceleration"
|
||||||
|
msgstr "桥接加速"
|
||||||
|
|
||||||
|
msgid "Enable Bridge Acceleration (may be functional conflict with bridge-mode VPN server)"
|
||||||
|
msgstr "启用桥接加速 (可能会和路由器上桥接模式的VPN服务器冲突)"
|
||||||
|
|
||||||
|
msgid "IPv6 Acceleration"
|
||||||
|
msgstr "IPv6 加速"
|
||||||
|
|
||||||
|
msgid "Enable IPv6 Acceleration"
|
||||||
|
msgstr "启用 IPv6 加速"
|
||||||
|
|
||||||
|
msgid "BBR CCA"
|
||||||
|
msgstr "BBR 拥塞控制算法"
|
||||||
|
|
||||||
|
msgid "Using BBR CCA can improve TCP network performance effectively"
|
||||||
|
msgstr "使用 BBR 拥塞控制算法可以有效提升 TCP 网络性能"
|
||||||
|
|
||||||
|
msgid "FullCone NAT"
|
||||||
|
msgstr "全锥形 NAT"
|
||||||
|
|
||||||
|
msgid "Using FullCone NAT can improve gaming performance effectively"
|
||||||
|
msgstr "使用全锥形 NAT 可以有效提升游戏体验"
|
||||||
|
|
||||||
|
msgid "DNS Caching"
|
||||||
|
msgstr "DNS 缓存"
|
||||||
|
|
||||||
|
msgid "Enable DNS Caching and anti ISP DNS pollution"
|
||||||
|
msgstr "启用 DNS 多线程查询、缓存,并防止 ISP 的 DNS 广告和域名劫持"
|
||||||
|
|
||||||
|
msgid "Resolve DNS Mode"
|
||||||
|
msgstr "DNS 解析方式"
|
||||||
|
|
||||||
|
msgid "DNS Program"
|
||||||
|
msgstr "DNS 解析程序"
|
||||||
|
|
||||||
|
msgid "Using PDNSD to query and cache"
|
||||||
|
msgstr "使用 PDNSD 解析"
|
||||||
|
|
||||||
|
|
||||||
|
msgid "Using DNSForwarder to query and cache"
|
||||||
|
msgstr "使用 DNSForwarder 解析"
|
||||||
|
|
||||||
|
msgid "Using DNSProxy to query and cache"
|
||||||
|
msgstr "使用 DNSProxy 解析"
|
||||||
|
|
||||||
|
msgid "Upsteam DNS Server"
|
||||||
|
msgstr "上游 DNS 服务器"
|
||||||
|
|
||||||
|
msgid "Muitiple DNS server can saperate with ','"
|
||||||
|
msgstr "多个上游 DNS 服务器请用 ',' 分隔(注意用英文逗号)"
|
||||||
|
|
||||||
|
msgid "Running Status"
|
||||||
|
msgstr "运行状态"
|
||||||
|
|
||||||
|
msgid "Flow Offloading"
|
||||||
|
msgstr "流量分载"
|
||||||
|
|
||||||
|
msgid "BBR CCA"
|
||||||
|
msgstr "BBR 拥塞控制算法"
|
||||||
|
|
||||||
|
msgid "FullCone NAT"
|
||||||
|
msgstr "全锥型 NAT"
|
||||||
|
|
||||||
|
msgid "DNS Caching"
|
||||||
|
msgstr "DNS 缓存"
|
||||||
|
|
||||||
|
msgid "Open Web Interface"
|
||||||
|
msgstr "打开 Web 界面"
|
10
luci-app-turboacc/root/etc/config/turboacc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
config turboacc 'config'
|
||||||
|
option sw_flow '1'
|
||||||
|
option hw_flow '1'
|
||||||
|
option sfe_flow '1'
|
||||||
|
option fullcone_nat '1'
|
||||||
|
option bbr_cca '0'
|
||||||
|
option dns_caching '0'
|
||||||
|
option dns_caching_mode '1'
|
||||||
|
option dns_caching_dns '114.114.114.114,114.114.115.115,223.5.5.5,223.6.6.6,180.76.76.76,119.29.29.29,119.28.28.28,1.2.4.8,210.2.4.8'
|
364
luci-app-turboacc/root/etc/init.d/turboacc
Executable file
|
@ -0,0 +1,364 @@
|
||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
# Copyright (C) 2018 Lean <coolsnowwolf@gmail.com>
|
||||||
|
# Copyright (C) 2019-2021 Tianling Shen <cnsztl@immortalwrt.org>
|
||||||
|
|
||||||
|
START=90
|
||||||
|
STOP=10
|
||||||
|
|
||||||
|
EXTRA_COMMANDS="check_status"
|
||||||
|
EXTRA_HELP=" check_status Check running status of utils"
|
||||||
|
|
||||||
|
restart_utils="true"
|
||||||
|
|
||||||
|
PS="/bin/busybox ps"
|
||||||
|
|
||||||
|
inital_conf(){
|
||||||
|
config_load "turboacc"
|
||||||
|
config_get "sw_flow" "config" "sw_flow" "0"
|
||||||
|
config_get "hw_flow" "config" "hw_flow" "0"
|
||||||
|
config_get "sfe_flow" "config" "sfe_flow" "0"
|
||||||
|
config_get "bbr_cca" "config" "bbr_cca" "0"
|
||||||
|
config_get "fullcone_nat" "config" "fullcone_nat" "0"
|
||||||
|
config_get "dns_caching" "config" "dns_caching" "0"
|
||||||
|
config_get "dns_caching_mode" "config" "dns_caching_mode" "0"
|
||||||
|
config_get "dns_caching_dns" "config" "dns_caching_dns"
|
||||||
|
|
||||||
|
[ ! -e "/lib/modules/$(uname -r)/xt_FLOWOFFLOAD.ko" ] && { sw_flow="0"; hw_flow="0"; }
|
||||||
|
[ ! -e "/lib/modules/$(uname -r)/shortcut-fe-cm.ko" ] && sfe_flow="0"
|
||||||
|
[ ! -e "/lib/modules/$(uname -r)/tcp_bbr.ko" ] && bbr_cca="0"
|
||||||
|
[ ! -e "/lib/modules/$(uname -r)/xt_FULLCONENAT.ko" ] && fullcone_nat="0"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_pdnsd() {
|
||||||
|
[ -d "/var/run/dnscache" ] || mkdir -p "/var/run/dnscache"
|
||||||
|
cat > "/var/run/dnscache/dnscache.conf" <<EOF
|
||||||
|
global {
|
||||||
|
perm_cache=1024; # dns缓存大小,单位KB,建议不要写的太大
|
||||||
|
cache_dir="/var/dnscache"; # 缓存文件的位置
|
||||||
|
pid_file = /var/run/dnscache.pid;
|
||||||
|
server_ip = 127.0.0.1; # pdnsd监听的网卡,0.0.0.0是全部网卡
|
||||||
|
server_port=5333; # pdnsd监听的端口,不要和别的服务冲突即可
|
||||||
|
status_ctl = on;
|
||||||
|
paranoid=on; # 二次请求模式,如果请求主DNS服务器返回的是垃圾地址,就向备用服务器请求
|
||||||
|
query_method=udp_only;
|
||||||
|
neg_domain_pol = off;
|
||||||
|
par_queries = 400; # 最多同时请求数
|
||||||
|
min_ttl = 1h; # DNS结果最短缓存时间
|
||||||
|
max_ttl = 1w; # DNS结果最长缓存时间
|
||||||
|
timeout = 10; # DNS请求超时时间,单位秒
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
label = "routine";
|
||||||
|
ip = ${dns_caching_dns}; # 这里为主要上级 dns 的 ip 地址,建议填写一个当地最快的DNS地址
|
||||||
|
timeout = 5; # DNS请求超时时间
|
||||||
|
reject = 74.125.127.102, # 以下是脏IP,也就是DNS污染一般会返回的结果,如果收到如下DNS结果会触发二次请求(TCP协议一般不会碰到脏IP)
|
||||||
|
74.125.155.102,
|
||||||
|
74.125.39.102,
|
||||||
|
74.125.39.113,
|
||||||
|
209.85.229.138,
|
||||||
|
128.121.126.139,
|
||||||
|
159.106.121.75,
|
||||||
|
169.132.13.103,
|
||||||
|
192.67.198.6,
|
||||||
|
202.106.1.2,
|
||||||
|
202.181.7.85,
|
||||||
|
203.161.230.171,
|
||||||
|
203.98.7.65,
|
||||||
|
207.12.88.98,
|
||||||
|
208.56.31.43,
|
||||||
|
209.145.54.50,
|
||||||
|
209.220.30.174,
|
||||||
|
209.36.73.33,
|
||||||
|
211.94.66.147,
|
||||||
|
213.169.251.35,
|
||||||
|
216.221.188.182,
|
||||||
|
216.234.179.13,
|
||||||
|
243.185.187.39,
|
||||||
|
37.61.54.158,
|
||||||
|
4.36.66.178,
|
||||||
|
46.82.174.68,
|
||||||
|
59.24.3.173,
|
||||||
|
64.33.88.161,
|
||||||
|
64.33.99.47,
|
||||||
|
64.66.163.251,
|
||||||
|
65.104.202.252,
|
||||||
|
65.160.219.113,
|
||||||
|
66.45.252.237,
|
||||||
|
69.55.52.253,
|
||||||
|
72.14.205.104,
|
||||||
|
72.14.205.99,
|
||||||
|
78.16.49.15,
|
||||||
|
8.7.198.45,
|
||||||
|
93.46.8.89,
|
||||||
|
37.61.54.158,
|
||||||
|
243.185.187.39,
|
||||||
|
190.93.247.4,
|
||||||
|
190.93.246.4,
|
||||||
|
190.93.245.4,
|
||||||
|
190.93.244.4,
|
||||||
|
65.49.2.178,
|
||||||
|
189.163.17.5,
|
||||||
|
23.89.5.60,
|
||||||
|
49.2.123.56,
|
||||||
|
54.76.135.1,
|
||||||
|
77.4.7.92,
|
||||||
|
118.5.49.6,
|
||||||
|
159.24.3.173,
|
||||||
|
188.5.4.96,
|
||||||
|
197.4.4.12,
|
||||||
|
220.250.64.24,
|
||||||
|
243.185.187.30,
|
||||||
|
249.129.46.48,
|
||||||
|
253.157.14.165;
|
||||||
|
reject_policy = fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
label = "special"; # 这个随便写
|
||||||
|
ip = 117.50.10.10,52.80.52.52,119.29.29.29; # 这里为备用DNS服务器的 ip 地址
|
||||||
|
port = 5353; # 推荐使用53以外的端口(DNS服务器必须支持
|
||||||
|
proxy_only = on;
|
||||||
|
timeout = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
source {
|
||||||
|
owner=localhost;
|
||||||
|
// serve_aliases=on;
|
||||||
|
file="/etc/hosts";
|
||||||
|
}
|
||||||
|
|
||||||
|
rr {
|
||||||
|
name=localhost;
|
||||||
|
reverse=on;
|
||||||
|
a=127.0.0.1;
|
||||||
|
owner=localhost;
|
||||||
|
soa=localhost,root.localhost,42,86400,900,86400,86400;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ ! -f "/var/dnscache/pdnsd.cache" ]; then
|
||||||
|
mkdir -p "/var/dnscache"
|
||||||
|
echo -ne "pd13\000\000\000\000" > "/var/dnscache/pdnsd.cache"
|
||||||
|
chown -R nobody.nogroup "/var/dnscache"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -d "/var/sbin" ] || mkdir -p "/var/sbin"
|
||||||
|
cp -a "/usr/sbin/pdnsd" "/var/sbin/dnscache"
|
||||||
|
/var/sbin/dnscache -c "/var/run/dnscache/dnscache.conf" > "/var/log/dnscache.file" 2>&1 &
|
||||||
|
echo "PDNSD: Start DNS Caching"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_dnsforwarder() {
|
||||||
|
mkdir -p "/var/run/dnscache"
|
||||||
|
cat > "/var/run/dnscache/dnscache.conf" <<EOF
|
||||||
|
LogOn false
|
||||||
|
LogFileThresholdLength 102400
|
||||||
|
LogFileFolder /var/run/dnscache
|
||||||
|
UDPLocal 127.0.0.1:5333
|
||||||
|
UDPGroup ${dns_caching_dns} * on
|
||||||
|
GroupFile
|
||||||
|
BlockIP 243.185.187.39,46.82.174.68,37.61.54.158,93.46.8.89,59.24.3.173,203.98.7.65,8.7.198.45,78.16.49.15,159.106.121.75,69.63.187.12,31.13.76.8,31.13.64.49
|
||||||
|
IPSubstituting
|
||||||
|
BlockNegativeResponse false
|
||||||
|
Hosts
|
||||||
|
HostsUpdateInterval 18000
|
||||||
|
HostsDownloadPath
|
||||||
|
HostsScript
|
||||||
|
HostsRetryInterval 30
|
||||||
|
AppendHosts
|
||||||
|
BlockIpv6WhenIpv4Exists false
|
||||||
|
UseCache true
|
||||||
|
CacheSize 1048576
|
||||||
|
MemoryCache true
|
||||||
|
CacheFile
|
||||||
|
IgnoreTTL false
|
||||||
|
OverrideTTL -1
|
||||||
|
MultipleTTL 1
|
||||||
|
ReloadCache false
|
||||||
|
OverwriteCache false
|
||||||
|
DisabledType
|
||||||
|
DisabledDomain
|
||||||
|
DisabledList
|
||||||
|
DomainStatistic false
|
||||||
|
DomainStatisticTempletFile
|
||||||
|
StatisticUpdateInterval 29
|
||||||
|
EOF
|
||||||
|
|
||||||
|
[ -d "/var/sbin" ] || mkdir -p "/var/sbin"
|
||||||
|
cp -a "/usr/bin/dnsforwarder" "/var/sbin/dnscache"
|
||||||
|
/var/sbin/dnscache -f "/var/run/dnscache/dnscache.conf" > "/var/log/dnscache.file" 2>&1 &
|
||||||
|
echo "DnsForwarder: Start DNS Caching"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
start_dnsproxy() {
|
||||||
|
[ -d "/var/run/dnscache" ] || mkdir -p "/var/run/dnscache"
|
||||||
|
echo -e "${dns_caching_dns//,/\\n}" > "/var/run/dnscache/dnscache.conf"
|
||||||
|
|
||||||
|
[ -d "/var/sbin" ] || mkdir -p "/var/sbin"
|
||||||
|
cp -a "/usr/bin/dnsproxy" "/var/sbin/dnscache"
|
||||||
|
/var/sbin/dnscache -l "127.0.0.1" -p "5333" -b "tls://9.9.9.9" -f "tls://8.8.8.8" -u "/var/run/dnscache/dnscache.conf" --all-servers --cache --cache-min-ttl=3600 > "/var/log/dnscache.file" 2>&1 &
|
||||||
|
echo "DNSProxy: Start DNS Caching"
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_dnscache() {
|
||||||
|
$PS -w | grep dnscache | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
|
||||||
|
$PS -w | grep dnscache-while.sh | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
|
||||||
|
killall -q -9 dnscache
|
||||||
|
rm -rf "/var/dnscache" "/var/run/dnscache"
|
||||||
|
echo "Stop DNS Caching"
|
||||||
|
}
|
||||||
|
|
||||||
|
change_dns() {
|
||||||
|
uci -q delete dhcp.@dnsmasq[0].server
|
||||||
|
uci add_list dhcp.@dnsmasq[0].server="127.0.0.1#5333"
|
||||||
|
uci set dhcp.@dnsmasq[0].noresolv="1"
|
||||||
|
uci commit dhcp
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
revert_dns() {
|
||||||
|
uci -q del_list dhcp.@dnsmasq[0].server="127.0.0.1#5333"
|
||||||
|
uci set dhcp.@dnsmasq[0].resolvfile="/tmp/resolv.conf.d/resolv.conf.auto"
|
||||||
|
uci set dhcp.@dnsmasq[0].noresolv="0"
|
||||||
|
uci commit dhcp
|
||||||
|
}
|
||||||
|
|
||||||
|
start(){
|
||||||
|
inital_conf
|
||||||
|
|
||||||
|
uci set firewall.@defaults[0].flow_offloading="${sw_flow}"
|
||||||
|
uci set firewall.@defaults[0].flow_offloading_hw="${hw_flow}"
|
||||||
|
uci set firewall.@defaults[0].fullcone="${fullcone_nat}"
|
||||||
|
uci commit firewall
|
||||||
|
|
||||||
|
[ "${sw_flow}" -ne "1" ] && {
|
||||||
|
[ "${sfe_flow}" -eq "1" ] && {
|
||||||
|
[ "$(have_ecm_init)" = "0" ] && {
|
||||||
|
/etc/init.d/shortcut-fe enabled || /etc/init.d/shortcut-fe enable
|
||||||
|
/etc/init.d/shortcut-fe start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[ "${sfe_flow}" -eq "0" ] && [ -e "/etc/init.d/shortcut-fe" ] && [ "$(have_ecm_init)" = "0" ] && \
|
||||||
|
/etc/init.d/shortcut-fe enabled && {
|
||||||
|
/etc/init.d/shortcut-fe stop 2>"/dev/null"
|
||||||
|
/etc/init.d/shortcut-fe disable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "${bbr_cca}" -eq "1" ]; then
|
||||||
|
sysctl -w net.ipv4.tcp_congestion_control="bbr"
|
||||||
|
else
|
||||||
|
sysctl -w net.ipv4.tcp_congestion_control="cubic"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${dns_caching}" -eq "1" ]; then
|
||||||
|
stop_dnscache
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
rm -f "/var/log/dnscache.file"
|
||||||
|
if [ "${dns_caching_mode}" = "1" ]; then
|
||||||
|
start_pdnsd
|
||||||
|
elif [ "${dns_caching_mode}" = "2" ]; then
|
||||||
|
start_dnsforwarder
|
||||||
|
elif [ "${dns_caching_mode}" = "3" ]; then
|
||||||
|
start_dnsproxy
|
||||||
|
fi
|
||||||
|
|
||||||
|
change_dns
|
||||||
|
/usr/share/dnscache/dnscache-while.sh > "/var/log/dnscache.file" 2>&1 &
|
||||||
|
else
|
||||||
|
stop_dnscache
|
||||||
|
revert_dns
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${restart_utils}" = "true" ]; then
|
||||||
|
/etc/init.d/dnsmasq restart >"/dev/null" 2>&1 && echo "DNSMASQ change"
|
||||||
|
/etc/init.d/firewall restart >"/dev/null" 2>&1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop(){
|
||||||
|
inital_conf
|
||||||
|
|
||||||
|
uci set firewall.@defaults[0].flow_offloading="${sw_flow}"
|
||||||
|
uci set firewall.@defaults[0].flow_offloading_hw="${hw_flow}"
|
||||||
|
uci set firewall.@defaults[0].fullcone="${fullcone_nat}"
|
||||||
|
uci commit firewall
|
||||||
|
|
||||||
|
[ "${sfe_flow}" -ne "1" ] && {
|
||||||
|
[ -e "/etc/init.d/shortcut-fe" ] && [ "$(have_ecm_init)" = "0" ] && {
|
||||||
|
/etc/init.d/shortcut-fe stop 2>"/dev/null"
|
||||||
|
/etc/init.d/shortcut-fe enabled && /etc/init.d/shortcut-fe disable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_dnscache
|
||||||
|
revert_dns
|
||||||
|
|
||||||
|
if [ "${restart_utils}" = "true" ]; then
|
||||||
|
/etc/init.d/dnsmasq restart >"/dev/null" 2>&1 && echo "DNSMASQ revert"
|
||||||
|
/etc/init.d/firewall restart >"/dev/null" 2>&1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
restart(){
|
||||||
|
restart_utils="false"
|
||||||
|
stop
|
||||||
|
start
|
||||||
|
/etc/init.d/dnsmasq restart >"/dev/null" 2>&1 && echo "DNSMASQ restart"
|
||||||
|
/etc/init.d/firewall restart >"/dev/null" 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
have_ecm_init() {
|
||||||
|
[ -e "/etc/init.d/qca-nss-ecm" ] && echo 1 && return
|
||||||
|
echo 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ecm_mode(){
|
||||||
|
config_load "ecm"
|
||||||
|
config_get front_end global acceleration_engine
|
||||||
|
case $front_end in
|
||||||
|
auto | nss | sfe | hybrid)
|
||||||
|
[ -d /sys/kernel/debug/ecm/ecm_nss_ipv4 ] && echo 'NSS: Enabled ' || echo 'NSS: Disabled '
|
||||||
|
[ -d /sys/kernel/debug/ecm/ecm_sfe_ipv4 ] && echo 'SFE: Enabled' || echo 'SFE: Disabled'
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo 'Unknown'
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
check_status(){
|
||||||
|
case "$1" in
|
||||||
|
"fastpath")
|
||||||
|
if [ "$(cat "/sys/module/xt_FLOWOFFLOAD/refcnt" 2>"/dev/null" || echo 0)" -ne "0" ]; then
|
||||||
|
echo -n "Flow Offloading"
|
||||||
|
exit 0
|
||||||
|
elif lsmod | grep -q "ecm"; then
|
||||||
|
echo -n "QCA-ECM Engine: "$(ecm_mode)
|
||||||
|
exit 0
|
||||||
|
elif lsmod | grep -q "shortcut_fe_cm"; then
|
||||||
|
echo -n "Shortcut-FE"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"fullconenat")
|
||||||
|
[ "$(cat "/sys/module/xt_FULLCONENAT/refcnt" 2>"/dev/null" || echo 0)" -ne "0" ] && \
|
||||||
|
exit 0 || exit 1
|
||||||
|
;;
|
||||||
|
"bbr")
|
||||||
|
[ "x$(cat "/proc/sys/net/ipv4/tcp_congestion_control" 2>"/dev/null")" = "xbbr" ] && \
|
||||||
|
exit 0 || exit 1
|
||||||
|
;;
|
||||||
|
"dns")
|
||||||
|
pgrep "dnscache" >"/dev/null" && exit 0 || exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
10
luci-app-turboacc/root/etc/uci-defaults/luci-turboacc
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
delete ucitrack.@turboacc[-1]
|
||||||
|
add ucitrack turboacc
|
||||||
|
set ucitrack.@turboacc[-1].init=turboacc
|
||||||
|
commit ucitrack
|
||||||
|
EOF
|
||||||
|
|
||||||
|
exit 0
|
38
luci-app-turboacc/root/usr/share/dnscache/dnscache-while.sh
Executable file
|
@ -0,0 +1,38 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
sleeptime=60
|
||||||
|
|
||||||
|
dnsconf="/var/run/dnscache/dnscache.conf"
|
||||||
|
dnsprogram="/var/sbin/dnscache"
|
||||||
|
logfile="/var/log/dnscache.file"
|
||||||
|
|
||||||
|
dns_caching="$(uci -q get turboacc.config.dns_caching)"
|
||||||
|
dns_caching_mode="$(uci -q get turboacc.config.dns_caching_mode)"
|
||||||
|
|
||||||
|
clean_log() {
|
||||||
|
logrow="$(grep -c "" "${logfile}")"
|
||||||
|
[ "${logrow}" -lt "500" ] || echo "${curtime} Log 条数超限,清空处理!" > "${logfile}"
|
||||||
|
}
|
||||||
|
|
||||||
|
while [ "${dns_caching}" -eq "1" ];
|
||||||
|
do
|
||||||
|
curtime="$(date "+%H:%M:%S")"
|
||||||
|
|
||||||
|
clean_log
|
||||||
|
|
||||||
|
if pidof dnscache > "/dev/null"; then
|
||||||
|
echo -e "${curtime} online!" >> "${logfile}"
|
||||||
|
else
|
||||||
|
if [ "${dns_caching_mode}" = "1" ]; then
|
||||||
|
${dnsprogram} -c "${dnsconf}" > "${logfile}" 2>&1 &
|
||||||
|
elif [ "${dns_caching_mode}" = "2" ]; then
|
||||||
|
${dnsprogram} -f "${dnsconf}" > "${logfile}" 2>&1 &
|
||||||
|
elif [ "${dns_caching_mode}" = "3" ]; then
|
||||||
|
${dnsprogram} -o "${logfile}" -l "127.0.0.1" -p "5333" -b "tls://9.9.9.9" -f "tls://8.8.8.8" -u "${dnsconf}" --all-servers --cache --cache-min-ttl=3600 > "${logfile}" 2>&1 &
|
||||||
|
fi
|
||||||
|
echo "${curtime} 重启服务!" >> ${logfile}
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "${sleeptime}"
|
||||||
|
continue
|
||||||
|
done
|
|
@ -9,6 +9,8 @@ include $(TOPDIR)/rules.mk
|
||||||
LUCI_TITLE:=LuCI for Zerotier
|
LUCI_TITLE:=LuCI for Zerotier
|
||||||
LUCI_DEPENDS:=+zerotier
|
LUCI_DEPENDS:=+zerotier
|
||||||
LUCI_PKGARCH:=all
|
LUCI_PKGARCH:=all
|
||||||
|
|
||||||
|
PKG_NAME:=luci-app-zerotier
|
||||||
PKG_VERSION:=1.0
|
PKG_VERSION:=1.0
|
||||||
PKG_RELEASE:=20
|
PKG_RELEASE:=20
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
module("luci.controller.zerotier",package.seeall)
|
module("luci.controller.zerotier", package.seeall)
|
||||||
|
|
||||||
function index()
|
function index()
|
||||||
if not nixio.fs.access("/etc/config/zerotier") then
|
if not nixio.fs.access("/etc/config/zerotier") then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
entry({"admin","vpn"}, firstchild(), "VPN", 45).dependent = false
|
entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
|
||||||
|
|
||||||
entry({"admin", "vpn", "zerotier"},firstchild(), _("ZeroTier")).dependent = false
|
|
||||||
|
|
||||||
|
entry({"admin", "vpn", "zerotier"}, alias("admin", "vpn", "zerotier", "general"), _("ZeroTier"), 99)
|
||||||
entry({"admin", "vpn", "zerotier", "general"}, cbi("zerotier/settings"), _("Base Setting"), 1)
|
entry({"admin", "vpn", "zerotier", "general"}, cbi("zerotier/settings"), _("Base Setting"), 1)
|
||||||
entry({"admin", "vpn", "zerotier", "log"}, form("zerotier/info"), _("Interface Info"), 2)
|
entry({"admin", "vpn", "zerotier", "log"}, form("zerotier/info"), _("Interface Info"), 2)
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ t = f:field(TextValue, "conf")
|
||||||
t.rmempty = true
|
t.rmempty = true
|
||||||
t.rows = 19
|
t.rows = 19
|
||||||
function t.cfgvalue()
|
function t.cfgvalue()
|
||||||
luci.sys.exec("for i in $(ifconfig | grep 'zt' | awk '{print $1}'); do ifconfig $i; done > /tmp/zero.info")
|
luci.sys.exec("for i in $(ifconfig | grep 'zt' | awk '{print $1}'); do ifconfig $i; done > /tmp/zero.info")
|
||||||
return fs.readfile(conffile) or ""
|
return fs.readfile(conffile) or ""
|
||||||
end
|
end
|
||||||
t.readonly="readonly"
|
t.readonly = "readonly"
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
|
@ -2,7 +2,7 @@ a = Map("zerotier")
|
||||||
a.title = translate("ZeroTier")
|
a.title = translate("ZeroTier")
|
||||||
a.description = translate("Zerotier is an open source, cross-platform and easy to use virtual LAN")
|
a.description = translate("Zerotier is an open source, cross-platform and easy to use virtual LAN")
|
||||||
|
|
||||||
a:section(SimpleSection).template = "zerotier/zerotier_status"
|
a:section(SimpleSection).template = "zerotier/zerotier_status"
|
||||||
|
|
||||||
t = a:section(NamedSection, "sample_config", "zerotier")
|
t = a:section(NamedSection, "sample_config", "zerotier")
|
||||||
t.anonymous = true
|
t.anonymous = true
|
||||||
|
@ -10,7 +10,7 @@ t.addremove = false
|
||||||
|
|
||||||
e = t:option(Flag, "enabled", translate("Enable"))
|
e = t:option(Flag, "enabled", translate("Enable"))
|
||||||
e.default = 0
|
e.default = 0
|
||||||
e.rmempty=false
|
e.rmempty = false
|
||||||
|
|
||||||
e = t:option(DynamicList, "join", translate('ZeroTier Network ID'))
|
e = t:option(DynamicList, "join", translate('ZeroTier Network ID'))
|
||||||
e.password = true
|
e.password = true
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
"X-Generator: Gtranslator 2.91.7\n"
|
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
|
||||||
|
|
||||||
msgid "Zerotier is an open source, cross-platform and easy to use virtual LAN"
|
msgid "Zerotier is an open source, cross-platform and easy to use virtual LAN"
|
||||||
msgstr "Zerotier 是一个开源,跨平台,而且适合内网穿透互联的傻瓜配置虚拟 VPN LAN"
|
msgstr "Zerotier 是一个开源,跨平台,而且适合内网穿透互联的傻瓜配置虚拟 VPN LAN"
|
||||||
|
|
||||||
|
|
|
@ -110,4 +110,4 @@ stop_service() {
|
||||||
reload_service() {
|
reload_service() {
|
||||||
stop
|
stop
|
||||||
start
|
start
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ uci -q batch <<-EOF >/dev/null
|
||||||
add ucitrack zerotier
|
add ucitrack zerotier
|
||||||
set ucitrack.@zerotier[-1].init=zerotier
|
set ucitrack.@zerotier[-1].init=zerotier
|
||||||
commit ucitrack
|
commit ucitrack
|
||||||
delete firewall.zerotier
|
|
||||||
|
delete firewall.zerotier
|
||||||
set firewall.zerotier=include
|
set firewall.zerotier=include
|
||||||
set firewall.zerotier.type=script
|
set firewall.zerotier.type=script
|
||||||
set firewall.zerotier.path=/etc/zerotier.start
|
set firewall.zerotier.path=/etc/zerotier.start
|
||||||
|
|
|
@ -12,4 +12,4 @@ do
|
||||||
ip_segment="$(ip route | grep "dev $i proto" | awk '{print $1}')"
|
ip_segment="$(ip route | grep "dev $i proto" | awk '{print $1}')"
|
||||||
iptables -t nat -D POSTROUTING -s "${ip_segment}" -j MASQUERADE 2>/dev/null
|
iptables -t nat -D POSTROUTING -s "${ip_segment}" -j MASQUERADE 2>/dev/null
|
||||||
echo "zt interface $i is stopped!"
|
echo "zt interface $i is stopped!"
|
||||||
done
|
done
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"luci-app-zerotier": {
|
|
||||||
"description": "Grant UCI access for luci-app-zerotier",
|
|
||||||
"read": {
|
|
||||||
"uci": [ "zerotier" ]
|
|
||||||
},
|
|
||||||
"write": {
|
|
||||||
"uci": [ "zerotier" ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
16
luci-theme-ezengreen/Makefile
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
#
|
||||||
|
# Copyright (C) 2008-2014 The LuCI Team <luci@lists.subsignal.org>
|
||||||
|
# Copyright (C) 2018-2019 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:=ezengreen Theme (default)
|
||||||
|
LUCI_DEPENDS:=
|
||||||
|
|
||||||
|
#include ../luci/luci.mk
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
BIN
luci-theme-ezengreen/htdocs/luci-static/ezengreen/1omr-logo-apple.png
Executable file
After Width: | Height: | Size: 8 KiB |
2027
luci-theme-ezengreen/htdocs/luci-static/ezengreen/cascade.css
Executable file
BIN
luci-theme-ezengreen/htdocs/luci-static/ezengreen/favicon.png
Executable file
After Width: | Height: | Size: 8 KiB |
3
luci-theme-ezengreen/htdocs/luci-static/ezengreen/html5.js
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
// HTML5 Shiv v3 | @jon_neal @afarkas @rem | MIT/GPL2 Licensed
|
||||||
|
// Uncompressed source: https://github.com/aFarkas/html5shiv
|
||||||
|
(function(a,b){function f(a){var c,d,e,f;b.documentMode>7?(c=b.createElement("font"),c.setAttribute("data-html5shiv",a.nodeName.toLowerCase())):c=b.createElement("shiv:"+a.nodeName);while(a.firstChild)c.appendChild(a.childNodes[0]);for(d=a.attributes,e=d.length,f=0;f<e;++f)d[f].specified&&c.setAttribute(d[f].nodeName,d[f].nodeValue);c.style.cssText=a.style.cssText,a.parentNode.replaceChild(c,a),c.originalElement=a}function g(a){var b=a.originalElement;while(a.childNodes.length)b.appendChild(a.childNodes[0]);a.parentNode.replaceChild(b,a)}function h(a,b){b=b||"all";var c=-1,d=[],e=a.length,f,g;while(++c<e){f=a[c],g=f.media||b;if(f.disabled||!/print|all/.test(g))continue;d.push(h(f.imports,g),f.cssText)}return d.join("")}function i(c){var d=new RegExp("(^|[\\s,{}])("+a.html5.elements.join("|")+")","gi"),e=c.split("{"),f=e.length,g=-1;while(++g<f)e[g]=e[g].split("}"),b.documentMode>7?e[g][e[g].length-1]=e[g][e[g].length-1].replace(d,'$1font[data-html5shiv="$2"]'):e[g][e[g].length-1]=e[g][e[g].length-1].replace(d,"$1shiv\\:$2"),e[g]=e[g].join("}");return e.join("{")}var c=function(a){return a.innerHTML="<x-element></x-element>",a.childNodes.length===1}(b.createElement("a")),d=function(a,b,c){return b.appendChild(a),(c=(c?c(a):a.currentStyle).display)&&b.removeChild(a)&&c==="block"}(b.createElement("nav"),b.documentElement,a.getComputedStyle),e={elements:"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video".split(" "),shivDocument:function(a){a=a||b;if(a.documentShived)return;a.documentShived=!0;var f=a.createElement,g=a.createDocumentFragment,h=a.getElementsByTagName("head")[0],i=function(a){f(a)};c||(e.elements.join(" ").replace(/\w+/g,i),a.createElement=function(a){var b=f(a);return b.canHaveChildren&&e.shivDocument(b.document),b},a.createDocumentFragment=function(){return e.shivDocument(g())});if(!d&&h){var j=f("div");j.innerHTML=["x<style>","article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}","audio{display:none}","canvas,video{display:inline-block;*display:inline;*zoom:1}","[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}","mark{background:#FF0;color:#000}","</style>"].join(""),h.insertBefore(j.lastChild,h.firstChild)}return a}};e.shivDocument(b),a.html5=e;if(c||!a.attachEvent)return;a.attachEvent("onbeforeprint",function(){if(a.html5.supportsXElement||!b.namespaces)return;b.namespaces.shiv||b.namespaces.add("shiv");var c=-1,d=new RegExp("^("+a.html5.elements.join("|")+")$","i"),e=b.getElementsByTagName("*"),g=e.length,j,k=i(h(function(a,b){var c=[],d=a.length;while(d)c.unshift(a[--d]);d=b.length;while(d)c.unshift(b[--d]);c.sort(function(a,b){return a.sourceIndex-b.sourceIndex}),d=c.length;while(d)c[--d]=c[d].styleSheet;return c}(b.getElementsByTagName("style"),b.getElementsByTagName("link"))));while(++c<g)j=e[c],d.test(j.nodeName)&&f(j);b.appendChild(b._shivedStyleSheet=b.createElement("style")).styleSheet.cssText=k}),a.attachEvent("onafterprint",function(){if(a.html5.supportsXElement||!b.namespaces)return;var c=-1,d=b.getElementsByTagName("*"),e=d.length,f;while(++c<e)f=d[c],f.originalElement&&g(f);b._shivedStyleSheet&&b._shivedStyleSheet.parentNode.removeChild(b._shivedStyleSheet)})})(this,document)
|
61
luci-theme-ezengreen/htdocs/luci-static/ezengreen/mobile.css
Executable file
|
@ -0,0 +1,61 @@
|
||||||
|
#content-desktop {display: block;}
|
||||||
|
#content-mobile {display: none;}
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
|
||||||
|
#content-desktop {display: none;}
|
||||||
|
#content-mobile {display: block;}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
header h3 a, header .brand {
|
||||||
|
float: left;
|
||||||
|
display: block;
|
||||||
|
padding: 12px 6px 12px 6px;
|
||||||
|
margin: 0 0 0 10px;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 240;
|
||||||
|
}
|
||||||
|
|
||||||
|
header div > ul, .nav {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
margin: 7px 8px 0 8px;
|
||||||
|
position: relative;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header div > ul a, .nav a {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
padding: 12px 6px 12px 6px;
|
||||||
|
line-height: 14px;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-dropdown, .dropdown-menu {
|
||||||
|
top: 38px;
|
||||||
|
min-width: 180px;
|
||||||
|
max-width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-device-width: 660px) {
|
||||||
|
#maincontent.container {
|
||||||
|
width: 98%;
|
||||||
|
margin: 30px 0 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-device-width: 360px) {
|
||||||
|
#maincontent.container {
|
||||||
|
width: 96%;
|
||||||
|
margin: 30px 0 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-device-width: 200px) {
|
||||||
|
#maincontent.container {
|
||||||
|
width: 94%;
|
||||||
|
margin: 40px 0 0 2px;
|
||||||
|
}
|
||||||
|
}
|
2027
luci-theme-ezengreen/htdocs/luci-static/resources/ezengreen/images/cascade.css
Executable file
BIN
luci-theme-ezengreen/htdocs/luci-static/resources/ezengreen/images/ezenlink.png
Executable file
After Width: | Height: | Size: 553 B |
BIN
luci-theme-ezengreen/htdocs/luci-static/resources/ezengreen/images/favicon.ico
Executable file
After Width: | Height: | Size: 4.2 KiB |
BIN
luci-theme-ezengreen/htdocs/luci-static/resources/ezengreen/images/footer.png
Executable file
After Width: | Height: | Size: 836 B |
|
@ -0,0 +1,61 @@
|
||||||
|
#content-desktop {display: block;}
|
||||||
|
#content-mobile {display: none;}
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
|
||||||
|
#content-desktop {display: none;}
|
||||||
|
#content-mobile {display: block;}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
header h3 a, header .brand {
|
||||||
|
float: left;
|
||||||
|
display: block;
|
||||||
|
padding: 12px 6px 12px 6px;
|
||||||
|
margin: 0 0 0 10px;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 240;
|
||||||
|
}
|
||||||
|
|
||||||
|
header div > ul, .nav {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
margin: 7px 8px 0 8px;
|
||||||
|
position: relative;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header div > ul a, .nav a {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
padding: 12px 6px 12px 6px;
|
||||||
|
line-height: 14px;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-dropdown, .dropdown-menu {
|
||||||
|
top: 38px;
|
||||||
|
min-width: 180px;
|
||||||
|
max-width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-device-width: 660px) {
|
||||||
|
#maincontent.container {
|
||||||
|
width: 98%;
|
||||||
|
margin: 30px 0 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-device-width: 360px) {
|
||||||
|
#maincontent.container {
|
||||||
|
width: 96%;
|
||||||
|
margin: 30px 0 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-device-width: 200px) {
|
||||||
|
#maincontent.container {
|
||||||
|
width: 94%;
|
||||||
|
margin: 40px 0 0 2px;
|
||||||
|
}
|
||||||
|
}
|
BIN
luci-theme-ezengreen/htdocs/luci-static/resources/ezengreen/images/omr-logo.png
Executable file
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 3.7 KiB |
118
luci-theme-ezengreen/htdocs/luci-static/resources/menu-ezengreen.js
Executable file
|
@ -0,0 +1,118 @@
|
||||||
|
'use strict';
|
||||||
|
'require baseclass';
|
||||||
|
'require ui';
|
||||||
|
|
||||||
|
return baseclass.extend({
|
||||||
|
__init__: function() {
|
||||||
|
ui.menu.load().then(L.bind(this.render, this));
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function(tree) {
|
||||||
|
var node = tree,
|
||||||
|
url = '';
|
||||||
|
|
||||||
|
this.renderModeMenu(tree);
|
||||||
|
|
||||||
|
if (L.env.dispatchpath.length >= 3) {
|
||||||
|
for (var i = 0; i < 3 && node; i++) {
|
||||||
|
node = node.children[L.env.dispatchpath[i]];
|
||||||
|
url = url + (url ? '/' : '') + L.env.dispatchpath[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node)
|
||||||
|
this.renderTabMenu(node, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('poll-start', this.handleBodyMargin);
|
||||||
|
document.addEventListener('poll-stop', this.handleBodyMargin);
|
||||||
|
document.addEventListener('uci-new-changes', this.handleBodyMargin);
|
||||||
|
document.addEventListener('uci-clear-changes', this.handleBodyMargin);
|
||||||
|
window.addEventListener('resize', this.handleBodyMargin);
|
||||||
|
|
||||||
|
this.handleBodyMargin();
|
||||||
|
},
|
||||||
|
|
||||||
|
renderTabMenu: function(tree, url, level) {
|
||||||
|
var container = document.querySelector('#tabmenu'),
|
||||||
|
ul = E('ul', { 'class': 'tabs' }),
|
||||||
|
children = ui.menu.getChildren(tree),
|
||||||
|
activeNode = null;
|
||||||
|
|
||||||
|
for (var i = 0; i < children.length; i++) {
|
||||||
|
var isActive = (L.env.dispatchpath[3 + (level || 0)] == children[i].name),
|
||||||
|
activeClass = isActive ? ' active' : '',
|
||||||
|
className = 'tabmenu-item-%s %s'.format(children[i].name, activeClass);
|
||||||
|
|
||||||
|
ul.appendChild(E('li', { 'class': className }, [
|
||||||
|
E('a', { 'href': L.url(url, children[i].name) }, [ _(children[i].title) ] )]));
|
||||||
|
|
||||||
|
if (isActive)
|
||||||
|
activeNode = children[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ul.children.length == 0)
|
||||||
|
return E([]);
|
||||||
|
|
||||||
|
container.appendChild(ul);
|
||||||
|
container.style.display = '';
|
||||||
|
|
||||||
|
if (activeNode)
|
||||||
|
this.renderTabMenu(activeNode, url + '/' + activeNode.name, (level || 0) + 1);
|
||||||
|
|
||||||
|
return ul;
|
||||||
|
},
|
||||||
|
|
||||||
|
renderMainMenu: function(tree, url, level) {
|
||||||
|
var ul = level ? E('ul', { 'class': 'dropdown-menu' }) : document.querySelector('#topmenu'),
|
||||||
|
children = ui.menu.getChildren(tree);
|
||||||
|
|
||||||
|
if (children.length == 0 || level > 1)
|
||||||
|
return E([]);
|
||||||
|
|
||||||
|
for (var i = 0; i < children.length; i++) {
|
||||||
|
var submenu = this.renderMainMenu(children[i], url + '/' + children[i].name, (level || 0) + 1),
|
||||||
|
subclass = (!level && submenu.firstElementChild) ? 'dropdown' : null,
|
||||||
|
linkclass = (!level && submenu.firstElementChild) ? 'menu' : null,
|
||||||
|
linkurl = submenu.firstElementChild ? '#' : L.url(url, children[i].name);
|
||||||
|
|
||||||
|
var li = E('li', { 'class': subclass }, [
|
||||||
|
E('a', { 'class': linkclass, 'href': linkurl }, [ _(children[i].title) ]),
|
||||||
|
submenu
|
||||||
|
]);
|
||||||
|
|
||||||
|
ul.appendChild(li);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.style.display = '';
|
||||||
|
|
||||||
|
return ul;
|
||||||
|
},
|
||||||
|
|
||||||
|
renderModeMenu: function(tree) {
|
||||||
|
var ul = document.querySelector('#modemenu'),
|
||||||
|
children = ui.menu.getChildren(tree);
|
||||||
|
|
||||||
|
for (var i = 0; i < children.length; i++) {
|
||||||
|
var isActive = (L.env.requestpath.length ? children[i].name == L.env.requestpath[0] : i == 0);
|
||||||
|
|
||||||
|
ul.appendChild(E('li', { 'class': isActive ? 'active' : null }, [
|
||||||
|
E('a', { 'href': L.url(children[i].name) }, [ _(children[i].title) ]),
|
||||||
|
' ',
|
||||||
|
E('span', { 'class': 'divider' }, [ '|' ])
|
||||||
|
]));
|
||||||
|
|
||||||
|
if (isActive)
|
||||||
|
this.renderMainMenu(children[i], children[i].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ul.children.length > 1)
|
||||||
|
ul.style.display = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
handleBodyMargin: function(ev) {
|
||||||
|
var body = document.querySelector('body'),
|
||||||
|
head = document.querySelector('header');
|
||||||
|
|
||||||
|
body.style.marginTop = head.offsetHeight + 'px';
|
||||||
|
}
|
||||||
|
});
|
19
luci-theme-ezengreen/luasrc/view/themes/ezengreen/footer.htm
Executable file
|
@ -0,0 +1,19 @@
|
||||||
|
<%#
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
|
||||||
|
Copyright 2012 David Menting <david@nut-bolt.nl>
|
||||||
|
Licensed to the public under the Apache License 2.0.
|
||||||
|
-%>
|
||||||
|
|
||||||
|
<% local ver = require "luci.version" %>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<a href="https://55860.com">Powered by business 版本号:<%= ver.distversion %></a>
|
||||||
|
<ul class="breadcrumb pull-right" id="modemenu" style="display:none"></ul>
|
||||||
|
<a href="http://55860.com" target="_blank"><img src="https://55860.com/bak/footer.png" alt="" width="100%" height="100%" /></a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">L.require('menu-openmptcprouter')</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
90
luci-theme-ezengreen/luasrc/view/themes/ezengreen/header.htm
Executable file
|
@ -0,0 +1,90 @@
|
||||||
|
<%#
|
||||||
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
Copyright 2008-2016 Jo-Philipp Wich <jow@openwrt.org>
|
||||||
|
Copyright 2012 David Menting <david@nut-bolt.nl>
|
||||||
|
Licensed to the public under the Apache License 2.0.
|
||||||
|
-%>
|
||||||
|
|
||||||
|
<%
|
||||||
|
local sys = require "luci.sys"
|
||||||
|
local util = require "luci.util"
|
||||||
|
local http = require "luci.http"
|
||||||
|
local disp = require "luci.dispatcher"
|
||||||
|
|
||||||
|
local boardinfo = util.ubus("system", "board")
|
||||||
|
|
||||||
|
local node = disp.context.dispatched
|
||||||
|
|
||||||
|
-- send as HTML5
|
||||||
|
http.prepare_content("text/html")
|
||||||
|
|
||||||
|
-- Get current and latest OMR version
|
||||||
|
local current_omr_version = luci.model.uci.cursor():get("openmptcprouter","settings","version") or ""
|
||||||
|
local latest_omr_version = luci.model.uci.cursor():get("openmptcprouter","latest_versions","omr") or ""
|
||||||
|
|
||||||
|
-%>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="<%=luci.i18n.context.lang%>">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title><%=striptags( (boardinfo.hostname or "?") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - system</title>
|
||||||
|
<meta name="viewport" content="initial-scale=1.0">
|
||||||
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||||||
|
<meta http-equiv="Pragma" content="no-cache" />
|
||||||
|
<meta http-equiv="Expires" content="0" />
|
||||||
|
<link rel="stylesheet" href="<%=media%>/cascade.css">
|
||||||
|
<link rel="stylesheet" media="only screen and (max-device-width: 854px)" href="<%=media%>/mobile.css" type="text/css" />
|
||||||
|
<link rel="shortcut icon" type="image/png" href="<%=media%>/favicon.png">
|
||||||
|
<link rel="apple-touch-icon" href="<%=media%>/1omr-logo-apple.png">
|
||||||
|
<% if node and node.css then %>
|
||||||
|
<link rel="stylesheet" href="<%=resource%>/<%=node.css%>">
|
||||||
|
<% end -%>
|
||||||
|
<% if css then %>
|
||||||
|
<style title="text/css"><%= css %></style>
|
||||||
|
<% end -%>
|
||||||
|
<script src="<%=url('admin/translations', luci.i18n.context.lang)%><%# ?v=PKG_VERSION %>"></script>
|
||||||
|
<script src="<%=resource%>/cbi.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="lang_<%=luci.i18n.context.lang%> <% if node then %><%= striptags( node.title ) %><%- end %>" data-page="<%= table.concat(disp.context.requestpath, "-") %>">
|
||||||
|
<header>
|
||||||
|
<div class="fill">
|
||||||
|
<div class="container">
|
||||||
|
<a class="brand" href="#" alt="openmptcprouter business"><img src="<%=resource%>/ezengreen/images/outdoorrouter.png" height="40" width="85" alt="openmptcprouter business" />OpenMPTCProuter_5G</a>
|
||||||
|
<ul class="nav" id="topmenu" style="display:none"></ul>
|
||||||
|
<div id="indicators" class="pull-right"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div id="maincontent" class="container">
|
||||||
|
<%- if luci.sys.process.info("uid") == 0 and luci.sys.user.getuser("root") and not luci.sys.user.getpasswd("root") then -%>
|
||||||
|
<div class="alert-message warning">
|
||||||
|
<h4><%:No password set!%></h4>
|
||||||
|
<p><%:There is no password set on this router. Please configure a root password to protect the web interface.%></p>
|
||||||
|
<% if disp.lookup("admin/system/admin") then %>
|
||||||
|
<div class="right"><a class="btn" href="<%=url("admin/system/admin")%>"><%:Go to password configuration...%></a></div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<%- end -%>
|
||||||
|
<%- if current_omr_version ~= "" and latest_omr_version ~= "" and current_omr_version < latest_omr_version then -%>
|
||||||
|
<div class="alert-message notice">
|
||||||
|
<p><%=translatef("你的蚂蚁聚合openmptcprouter of china商业版 版本号 %s 最新 版本号 %s 现在可以升级",current_omr_version,latest_omr_version)%></p>
|
||||||
|
<div class="right"><a class="btn" href="https://55860.com/serverstatus.php"><%:点击查看最新版本...%></a></div>
|
||||||
|
</div>
|
||||||
|
<%- end -%>
|
||||||
|
<div class="alert-message notice">
|
||||||
|
|
||||||
|
<iframe src ="https://55860.com/bak/gonggao.php" width="100%" height="100%">
|
||||||
|
<p>最新公告</p>
|
||||||
|
</iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<div class="alert-message warning">
|
||||||
|
<h4><%:JavaScript required!%></h4>
|
||||||
|
<p><%:You must enable JavaScript in your browser or LuCI will not work properly.%></p>
|
||||||
|
</div>
|
||||||
|
</noscript>
|
||||||
|
|
||||||
|
<div id="tabmenu" style="display:none"></div>
|
10
luci-theme-ezengreen/root/etc/uci-defaults/luci-theme-ezengreen
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ "$(uci -q get luci.themes.ezengreen)" = "" ]; then
|
||||||
|
uci batch <<-EOF
|
||||||
|
set luci.themes.ezengreen=/luci-static/ezengreen
|
||||||
|
set luci.main.mediaurlbase=/luci-static/ezengreen
|
||||||
|
commit luci
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
exit 0
|
57
pdnsd-alt/Makefile
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
#
|
||||||
|
# Copyright (C) 2021 ImmortalWrt.org
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=pdnsd
|
||||||
|
PKG_VERSION:=1.2.9b-par
|
||||||
|
PKG_RELEASE:=3
|
||||||
|
|
||||||
|
PKG_SOURCE_PROTO:=git
|
||||||
|
PKG_SOURCE_URL:=https://github.com/shadowsocks/pdnsd.git
|
||||||
|
PKG_SOURCE_DATE:=2012-04-26
|
||||||
|
PKG_SOURCE_VERSION:=a8e46ccba7b0fa2230d6c42ab6dcd92926f6c21d
|
||||||
|
PKG_MIRROR_HASH:=e3e9c56cf91b12d8db73def2c247be2f726a052bed012f7a1e48946375f8e478
|
||||||
|
|
||||||
|
PKG_BUILD_PARALLEL:=1
|
||||||
|
PKG_INSTALL:=1
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/pdnsd-alt
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
SUBMENU:=IP Addresses and Names
|
||||||
|
TITLE:=Proxy DNS Server
|
||||||
|
DEPENDS:=+libpthread
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/pdnsd-alt/description
|
||||||
|
pdnsd, is an IPv6 capable proxy DNS server with permanent caching (the cache
|
||||||
|
contents are written to hard disk on exit) that is designed to cope with
|
||||||
|
unreachable or down DNS servers (for example in dial-in networking).
|
||||||
|
|
||||||
|
pdnsd can be used with applications that do dns lookups, eg on startup, and
|
||||||
|
can't be configured to change that behaviour, to prevent the often
|
||||||
|
minute-long hangs (or even crashes) that result from stalled dns queries.
|
||||||
|
endef
|
||||||
|
|
||||||
|
TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include
|
||||||
|
|
||||||
|
CONFIGURE_ARGS += \
|
||||||
|
--with-cachedir=/var/pdnsd \
|
||||||
|
--with-target=Linux
|
||||||
|
|
||||||
|
define Package/pdnsd-alt/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/sbin
|
||||||
|
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/pdnsd $(1)/usr/sbin/pdnsd
|
||||||
|
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/pdnsd-ctl $(1)/usr/sbin/pdnsd-ctl
|
||||||
|
|
||||||
|
#$(INSTALL_DIR) $(1)/etc
|
||||||
|
#$(INSTALL_CONF) $(PKG_INSTALL_DIR)/etc/pdnsd.conf.sample $(1)/etc/pdnsd.conf
|
||||||
|
#$(INSTALL_DIR) $(1)/etc/init.d
|
||||||
|
#$(INSTALL_BIN) ./files/pdnsd.init $(1)/etc/init.d/pdnsd
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,pdnsd-alt))
|
46
pdnsd-alt/files/pdnsd.init
Executable file
|
@ -0,0 +1,46 @@
|
||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
|
||||||
|
START=65
|
||||||
|
NAME=pdnsd
|
||||||
|
DESC="proxy DNS server"
|
||||||
|
|
||||||
|
DAEMON=/usr/sbin/pdnsd
|
||||||
|
PID_FILE=/var/run/$NAME.pid
|
||||||
|
CACHEDIR=/var/pdnsd
|
||||||
|
CACHE=$CACHEDIR/pdnsd.cache
|
||||||
|
|
||||||
|
USER=nobody
|
||||||
|
GROUP=nogroup
|
||||||
|
|
||||||
|
start() {
|
||||||
|
echo -n "Starting $DESC: $NAME"
|
||||||
|
|
||||||
|
gen_cache
|
||||||
|
|
||||||
|
$DAEMON --daemon -p $PID_FILE
|
||||||
|
echo " ."
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
echo -n "Stopping $DESC: $NAME"
|
||||||
|
kill `cat $PID_FILE` > /dev/null 2>&1
|
||||||
|
rm -rf $PID_FILE
|
||||||
|
echo " ."
|
||||||
|
}
|
||||||
|
|
||||||
|
restart() {
|
||||||
|
echo "Restarting $DESC: $NAME... "
|
||||||
|
stop
|
||||||
|
sleep 2
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
|
gen_cache()
|
||||||
|
{
|
||||||
|
if ! test -f "$CACHE"; then
|
||||||
|
mkdir -p `dirname $CACHE`
|
||||||
|
dd if=/dev/zero of="$CACHE" bs=1 count=4 2> /dev/null
|
||||||
|
chown -R $USER.$GROUP $CACHEDIR
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
42
pdnsd-alt/patches/010-no-doc-and-test.patch
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
--- a/Makefile.am
|
||||||
|
+++ b/Makefile.am
|
||||||
|
@@ -1,5 +1,5 @@
|
||||||
|
|
||||||
|
-SUBDIRS = src doc contrib
|
||||||
|
+SUBDIRS = src contrib
|
||||||
|
|
||||||
|
EXTRA_DIST = version ChangeLog.old COPYING.BSD README.par README.par.old PKGBUILD
|
||||||
|
|
||||||
|
--- a/Makefile.in
|
||||||
|
+++ b/Makefile.in
|
||||||
|
@@ -196,7 +196,7 @@ threadlib = @threadlib@
|
||||||
|
top_build_prefix = @top_build_prefix@
|
||||||
|
top_builddir = @top_builddir@
|
||||||
|
top_srcdir = @top_srcdir@
|
||||||
|
-SUBDIRS = src doc contrib
|
||||||
|
+SUBDIRS = src contrib
|
||||||
|
EXTRA_DIST = version ChangeLog.old COPYING.BSD README.par README.par.old PKGBUILD
|
||||||
|
all: config.h
|
||||||
|
$(MAKE) $(AM_MAKEFLAGS) all-recursive
|
||||||
|
--- a/src/Makefile.am
|
||||||
|
+++ b/src/Makefile.am
|
||||||
|
@@ -15,7 +15,7 @@ EXTRA_DIST = make_rr_types_h.pl rr_types
|
||||||
|
|
||||||
|
## Try to do this last
|
||||||
|
|
||||||
|
-SUBDIRS = . pdnsd-ctl rc test
|
||||||
|
+SUBDIRS = . pdnsd-ctl
|
||||||
|
|
||||||
|
$(pdnsd_OBJECTS): rr_types.h
|
||||||
|
|
||||||
|
--- a/src/Makefile.in
|
||||||
|
+++ b/src/Makefile.in
|
||||||
|
@@ -215,7 +215,7 @@ pdnsd_SOURCES = conf-parser.c conff.c co
|
||||||
|
freebsd_netinet_ip_icmp.h
|
||||||
|
|
||||||
|
EXTRA_DIST = make_rr_types_h.pl rr_types.in
|
||||||
|
-SUBDIRS = . pdnsd-ctl rc test
|
||||||
|
+SUBDIRS = . pdnsd-ctl
|
||||||
|
all: all-recursive
|
||||||
|
|
||||||
|
.SUFFIXES:
|
66
pdnsd-alt/patches/020-headers.patch
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
--- a/src/conff.h
|
||||||
|
+++ b/src/conff.h
|
||||||
|
@@ -32,7 +32,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
-#include <net/if.h>
|
||||||
|
+#include <linux/if.h>
|
||||||
|
#include "ipvers.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
--- a/src/dns.h
|
||||||
|
+++ b/src/dns.h
|
||||||
|
@@ -27,7 +27,7 @@
|
||||||
|
#include <config.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
-#include <net/if.h>
|
||||||
|
+#include <linux/if.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "rr_types.h"
|
||||||
|
--- a/src/dns_answer.c
|
||||||
|
+++ b/src/dns_answer.c
|
||||||
|
@@ -37,7 +37,7 @@
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#ifdef HAVE_SYS_POLL_H
|
||||||
|
-#include <sys/poll.h>
|
||||||
|
+#include <poll.h>
|
||||||
|
#endif
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
--- a/src/dns_query.c
|
||||||
|
+++ b/src/dns_query.c
|
||||||
|
@@ -23,7 +23,7 @@
|
||||||
|
#include <config.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#ifdef HAVE_SYS_POLL_H
|
||||||
|
-#include <sys/poll.h>
|
||||||
|
+#include <poll.h>
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
--- a/src/icmp.c
|
||||||
|
+++ b/src/icmp.c
|
||||||
|
@@ -28,7 +28,7 @@
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#ifdef HAVE_SYS_POLL_H
|
||||||
|
-#include <sys/poll.h>
|
||||||
|
+#include <poll.h>
|
||||||
|
#endif
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
--- a/src/netdev.c
|
||||||
|
+++ b/src/netdev.c
|
||||||
|
@@ -59,7 +59,7 @@
|
||||||
|
#include "ipvers.h"
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
-#include <net/if.h>
|
||||||
|
+#include <linux/if.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
|
@ -1,5 +1,5 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2014 The Linux Foundation. All rights reserved.
|
# Copyright (c) 2013-2018, 2020 The Linux Foundation. All rights reserved.
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
# above copyright notice and this permission notice appear in all copies.
|
# above copyright notice and this permission notice appear in all copies.
|
||||||
|
@ -11,12 +11,12 @@
|
||||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
include $(INCLUDE_DIR)/kernel.mk
|
include $(INCLUDE_DIR)/kernel.mk
|
||||||
|
|
||||||
PKG_NAME:=shortcut-fe
|
PKG_NAME:=shortcut-fe
|
||||||
PKG_RELEASE:=2
|
PKG_RELEASE:=5
|
||||||
PKG_CONFIG_DEPENDS := CONFIG_IPV6
|
|
||||||
|
|
||||||
include $(INCLUDE_DIR)/package.mk
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
@ -24,13 +24,17 @@ define KernelPackage/shortcut-fe
|
||||||
SECTION:=kernel
|
SECTION:=kernel
|
||||||
CATEGORY:=Kernel modules
|
CATEGORY:=Kernel modules
|
||||||
SUBMENU:=Network Support
|
SUBMENU:=Network Support
|
||||||
DEPENDS:=
|
DEPENDS:=@IPV6 +kmod-nf-conntrack
|
||||||
TITLE:=Kernel driver for SFE
|
TITLE:=Kernel driver for SFE
|
||||||
FILES:=$(PKG_BUILD_DIR)/shortcut-fe.ko $(if $(CONFIG_IPV6),$(PKG_BUILD_DIR)/shortcut-fe-ipv6.ko,)
|
FILES:= \
|
||||||
KCONFIG:=CONFIG_NF_CONNTRACK_EVENTS=y \
|
$(PKG_BUILD_DIR)/shortcut-fe.ko \
|
||||||
|
$(PKG_BUILD_DIR)/shortcut-fe-ipv6.ko
|
||||||
|
KCONFIG:= \
|
||||||
|
CONFIG_NF_CONNTRACK_EVENTS=y \
|
||||||
CONFIG_NF_CONNTRACK_TIMEOUT=y \
|
CONFIG_NF_CONNTRACK_TIMEOUT=y \
|
||||||
CONFIG_SHORTCUT_FE=y \
|
CONFIG_SHORTCUT_FE=y \
|
||||||
CONFIG_XFRM=y
|
CONFIG_XFRM=y
|
||||||
|
PROVIDES:=$(PKG_NAME)
|
||||||
AUTOLOAD:=$(call AutoLoad,09,shortcut-fe shortcut-fe-ipv6)
|
AUTOLOAD:=$(call AutoLoad,09,shortcut-fe shortcut-fe-ipv6)
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
@ -39,10 +43,14 @@ Shortcut is an in-Linux-kernel IP packet forwarding engine.
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define KernelPackage/shortcut-fe/install
|
define KernelPackage/shortcut-fe/install
|
||||||
|
$(INSTALL_DIR) $(1)/etc/init.d
|
||||||
|
$(INSTALL_BIN) ./files/etc/init.d/shortcut-fe $(1)/etc/init.d
|
||||||
$(INSTALL_DIR) $(1)/usr/bin
|
$(INSTALL_DIR) $(1)/usr/bin
|
||||||
$(INSTALL_BIN) ./files/usr/bin/sfe_dump $(1)/usr/bin
|
$(INSTALL_BIN) ./files/usr/bin/sfe_dump $(1)/usr/bin
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
HAVE_ECM:=$(CONFIG_PACKAGE_kmod-qca-nss-ecm-premium)$(CONFIG_PACKAGE_kmod-qca-nss-ecm-noload)$(CONFIG_PACKAGE_kmod-qca-nss-ecm-premium-noload)$(CONFIG_PACKAGE_kmod-qca-nss-ecm-standard)
|
||||||
|
|
||||||
define KernelPackage/shortcut-fe-cm
|
define KernelPackage/shortcut-fe-cm
|
||||||
SECTION:=kernel
|
SECTION:=kernel
|
||||||
CATEGORY:=Kernel modules
|
CATEGORY:=Kernel modules
|
||||||
|
@ -50,7 +58,11 @@ define KernelPackage/shortcut-fe-cm
|
||||||
DEPENDS:=+kmod-ipt-conntrack +kmod-shortcut-fe
|
DEPENDS:=+kmod-ipt-conntrack +kmod-shortcut-fe
|
||||||
TITLE:=Kernel driver for SFE
|
TITLE:=Kernel driver for SFE
|
||||||
FILES:=$(PKG_BUILD_DIR)/shortcut-fe-cm.ko
|
FILES:=$(PKG_BUILD_DIR)/shortcut-fe-cm.ko
|
||||||
KCONFIG:=CONFIG_NF_CONNTRACK_CHAIN_EVENTS=y
|
KCONFIG:= \
|
||||||
|
CONFIG_NF_CONNTRACK_CHAIN_EVENTS=y \
|
||||||
|
CONFIG_NF_CONNTRACK_EVENTS=y \
|
||||||
|
CONFIG_XFRM=y
|
||||||
|
CONFLICTS:=kmod-shortcut-fe-drv
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define KernelPackage/shortcut-fe-cm/Description
|
define KernelPackage/shortcut-fe-cm/Description
|
||||||
|
@ -58,20 +70,21 @@ Simple connection manager for the Shortcut forwarding engine.
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Build/Compile
|
define Build/Compile
|
||||||
+$(MAKE) $(PKG_JOBS) -C "$(LINUX_DIR)" \
|
$(MAKE) $(PKG_JOBS) -C "$(LINUX_DIR)" \
|
||||||
$(KERNEL_MAKE_FLAGS) \
|
$(KERNEL_MAKE_FLAGS) \
|
||||||
$(PKG_MAKE_FLAGS) \
|
$(PKG_MAKE_FLAGS) \
|
||||||
M="$(PKG_BUILD_DIR)" \
|
M="$(PKG_BUILD_DIR)" \
|
||||||
modules \
|
EXTRA_CFLAGS+="-DSFE_SUPPORT_IPV6" SFE_SUPPORT_IPV6=y \
|
||||||
$(if $(CONFIG_IPV6),EXTRA_CFLAGS="-DSFE_SUPPORT_IPV6" SFE_SUPPORT_IPV6=y,)
|
$(if $(HAVE_ECM),EXTRA_CFLAGS+="-DCONFIG_SFE_ECM" CONFIG_SFE_ECM=y,) \
|
||||||
|
modules
|
||||||
endef
|
endef
|
||||||
|
|
||||||
#ifneq ($(CONFIG_PACKAGE_kmod-shortcut-fe)$(CONFIG_PACKAGE_kmod-shortcut-fe-cm),)
|
ifneq ($(CONFIG_PACKAGE_kmod-shortcut-fe)$(CONFIG_PACKAGE_kmod-shortcut-fe-cm),)
|
||||||
define Build/InstallDev
|
define Build/InstallDev
|
||||||
$(INSTALL_DIR) $(1)/usr/include/shortcut-fe
|
$(INSTALL_DIR) $(1)/usr/include/shortcut-fe
|
||||||
$(CP) -rf $(PKG_BUILD_DIR)/sfe.h $(1)/usr/include/shortcut-fe
|
$(CP) -rf $(PKG_BUILD_DIR)/sfe.h $(1)/usr/include/shortcut-fe
|
||||||
endef
|
endef
|
||||||
#endif
|
endif
|
||||||
|
|
||||||
$(eval $(call KernelPackage,shortcut-fe))
|
$(eval $(call KernelPackage,shortcut-fe))
|
||||||
$(eval $(call KernelPackage,shortcut-fe-cm))
|
$(eval $(call KernelPackage,shortcut-fe-cm))
|
||||||
|
|
49
shortcut-fe/files/etc/init.d/shortcut-fe
Executable file
|
@ -0,0 +1,49 @@
|
||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014-2015 The Linux Foundation. All rights reserved.
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
|
# above copyright notice and this permission notice appear in all copies.
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
#SFE connection manager has a lower priority, it should be started after other connection manager
|
||||||
|
#to detect the existence of connection manager with higher priority
|
||||||
|
START=99
|
||||||
|
|
||||||
|
have_cm() {
|
||||||
|
[ -d "/sys/kernel/debug/ecm" ] && echo 1 && return
|
||||||
|
|
||||||
|
echo 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#load shortcut-fe and connection manager
|
||||||
|
load_sfe() {
|
||||||
|
local kernel_version=$(uname -r)
|
||||||
|
|
||||||
|
[ -d "/sys/module/shortcut_fe" ] || insmod /lib/modules/$kernel_version/shortcut-fe.ko
|
||||||
|
[ -d "/sys/module/shortcut_fe_ipv6" ] || insmod /lib/modules/$kernel_version/shortcut-fe-ipv6.ko
|
||||||
|
|
||||||
|
[ -e "/lib/modules/$kernel_version/shortcut-fe-cm.ko" ] && {
|
||||||
|
[ -d /sys/module/shortcut_fe_cm ] || insmod /lib/modules/$kernel_version/shortcut-fe-cm.ko
|
||||||
|
}
|
||||||
|
[ -e "/lib/modules/$kernel_version/fast-classifier.ko" ] && {
|
||||||
|
[ -d /sys/module/fast_classifier ] || insmod /lib/modules/$kernel_version/fast-classifier.ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
[ "$(have_cm)" = "0" ] && load_sfe
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
[ -d "/sys/module/shortcut_fe_drv" ] && rmmod shortcut_fe_drv
|
||||||
|
[ -d "/sys/module/shortcut_fe_cm" ] && rmmod shortcut_fe_cm
|
||||||
|
[ -d "/sys/module/fast_classifier" ] && rmmod fast_classifier
|
||||||
|
}
|
|
@ -5,8 +5,7 @@
|
||||||
config SHORTCUT_FE
|
config SHORTCUT_FE
|
||||||
tristate "Shortcut Forwarding Engine"
|
tristate "Shortcut Forwarding Engine"
|
||||||
depends on NF_CONNTRACK
|
depends on NF_CONNTRACK
|
||||||
default n
|
---help---
|
||||||
help
|
|
||||||
Shortcut is a fast in-kernel packet forwarding engine.
|
Shortcut is a fast in-kernel packet forwarding engine.
|
||||||
|
|
||||||
To compile this code as a module, choose M here: the module will be
|
To compile this code as a module, choose M here: the module will be
|
||||||
|
|
|
@ -21,3 +21,4 @@ endif
|
||||||
shortcut-fe-cm-objs := \
|
shortcut-fe-cm-objs := \
|
||||||
sfe_cm.o
|
sfe_cm.o
|
||||||
|
|
||||||
|
ccflags-y += -Werror -Wall
|
||||||
|
|
|
@ -180,7 +180,7 @@ static inline struct net_device *sfe_dev_get_master(struct net_device *dev)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))
|
||||||
#define sfe_dst_get_neighbour(dst, daddr) dst_neigh_lookup(dst, daddr)
|
#define sfe_dst_get_neighbour(dst, daddr) dst_neigh_lookup(dst, addr)
|
||||||
#else
|
#else
|
||||||
static inline struct neighbour *
|
static inline struct neighbour *
|
||||||
sfe_dst_get_neighbour(struct dst_entry *dst, void *daddr)
|
sfe_dst_get_neighbour(struct dst_entry *dst, void *daddr)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* sfe-cm.c
|
* sfe-cm.c
|
||||||
* Shortcut forwarding engine connection manager.
|
* Shortcut forwarding engine connection manager.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
|
* Copyright (c) 2013-2018, 2020 The Linux Foundation. All rights reserved.
|
||||||
* Permission to use, copy, modify, and/or distribute this software for
|
* Permission to use, copy, modify, and/or distribute this software for
|
||||||
* any purpose with or without fee is hereby granted, provided that the
|
* any purpose with or without fee is hereby granted, provided that the
|
||||||
* above copyright notice and this permission notice appear in all copies.
|
* above copyright notice and this permission notice appear in all copies.
|
||||||
|
@ -199,7 +199,7 @@ int sfe_cm_recv(struct sk_buff *skb)
|
||||||
* structure, obtain the hardware address. This means this function also
|
* structure, obtain the hardware address. This means this function also
|
||||||
* works if the neighbours are routers too.
|
* works if the neighbours are routers too.
|
||||||
*/
|
*/
|
||||||
static bool sfe_cm_find_dev_and_mac_addr(sfe_ip_addr_t *addr, struct net_device **dev, u8 *mac_addr, int is_v4)
|
static bool sfe_cm_find_dev_and_mac_addr(struct sk_buff *skb, sfe_ip_addr_t *addr, struct net_device **dev, u8 *mac_addr, int is_v4)
|
||||||
{
|
{
|
||||||
struct neighbour *neigh;
|
struct neighbour *neigh;
|
||||||
struct rtable *rt;
|
struct rtable *rt;
|
||||||
|
@ -207,6 +207,15 @@ static bool sfe_cm_find_dev_and_mac_addr(sfe_ip_addr_t *addr, struct net_device
|
||||||
struct dst_entry *dst;
|
struct dst_entry *dst;
|
||||||
struct net_device *mac_dev;
|
struct net_device *mac_dev;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have skb provided, use it as the original code is unable
|
||||||
|
* to lookup routes that are policy routed.
|
||||||
|
*/
|
||||||
|
if (unlikely(skb)) {
|
||||||
|
dst = skb_dst(skb);
|
||||||
|
goto skip_dst_lookup;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look up the rtable entry for the IP address then get the hardware
|
* Look up the rtable entry for the IP address then get the hardware
|
||||||
* address from its neighbour structure. This means this work when the
|
* address from its neighbour structure. This means this work when the
|
||||||
|
@ -220,11 +229,11 @@ static bool sfe_cm_find_dev_and_mac_addr(sfe_ip_addr_t *addr, struct net_device
|
||||||
|
|
||||||
dst = (struct dst_entry *)rt;
|
dst = (struct dst_entry *)rt;
|
||||||
} else {
|
} else {
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0))
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0))
|
||||||
rt6 = rt6_lookup(&init_net, (struct in6_addr *)addr->ip6, 0, 0, NULL, 0);
|
|
||||||
#else
|
|
||||||
rt6 = rt6_lookup(&init_net, (struct in6_addr *)addr->ip6, 0, 0, 0);
|
rt6 = rt6_lookup(&init_net, (struct in6_addr *)addr->ip6, 0, 0, 0);
|
||||||
#endif /*KERNEL_VERSION(4, 17, 0)*/
|
#else
|
||||||
|
rt6 = rt6_lookup(&init_net, (struct in6_addr *)addr->ip6, 0, 0, NULL, 0);
|
||||||
|
#endif
|
||||||
if (!rt6) {
|
if (!rt6) {
|
||||||
goto ret_fail;
|
goto ret_fail;
|
||||||
}
|
}
|
||||||
|
@ -232,18 +241,21 @@ static bool sfe_cm_find_dev_and_mac_addr(sfe_ip_addr_t *addr, struct net_device
|
||||||
dst = (struct dst_entry *)rt6;
|
dst = (struct dst_entry *)rt6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
skip_dst_lookup:
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
neigh = sfe_dst_get_neighbour(dst, addr);
|
neigh = sfe_dst_get_neighbour(dst, addr);
|
||||||
if (unlikely(!neigh)) {
|
if (unlikely(!neigh)) {
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
dst_release(dst);
|
if (likely(!skb))
|
||||||
|
dst_release(dst);
|
||||||
goto ret_fail;
|
goto ret_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(!(neigh->nud_state & NUD_VALID))) {
|
if (unlikely(!(neigh->nud_state & NUD_VALID))) {
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
neigh_release(neigh);
|
neigh_release(neigh);
|
||||||
dst_release(dst);
|
if (likely(!skb))
|
||||||
|
dst_release(dst);
|
||||||
goto ret_fail;
|
goto ret_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +263,8 @@ static bool sfe_cm_find_dev_and_mac_addr(sfe_ip_addr_t *addr, struct net_device
|
||||||
if (!mac_dev) {
|
if (!mac_dev) {
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
neigh_release(neigh);
|
neigh_release(neigh);
|
||||||
dst_release(dst);
|
if (likely(!skb))
|
||||||
|
dst_release(dst);
|
||||||
goto ret_fail;
|
goto ret_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +274,8 @@ static bool sfe_cm_find_dev_and_mac_addr(sfe_ip_addr_t *addr, struct net_device
|
||||||
*dev = mac_dev;
|
*dev = mac_dev;
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
neigh_release(neigh);
|
neigh_release(neigh);
|
||||||
dst_release(dst);
|
if (likely(!skb))
|
||||||
|
dst_release(dst);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -295,7 +309,13 @@ static unsigned int sfe_cm_post_routing(struct sk_buff *skb, int is_v4)
|
||||||
struct net_device *dest_br_dev = NULL;
|
struct net_device *dest_br_dev = NULL;
|
||||||
struct nf_conntrack_tuple orig_tuple;
|
struct nf_conntrack_tuple orig_tuple;
|
||||||
struct nf_conntrack_tuple reply_tuple;
|
struct nf_conntrack_tuple reply_tuple;
|
||||||
|
struct sk_buff *tmp_skb = NULL;
|
||||||
SFE_NF_CONN_ACCT(acct);
|
SFE_NF_CONN_ACCT(acct);
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
|
||||||
|
struct net *net=NULL;
|
||||||
|
struct nf_tcp_net *tn=NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't process broadcast or multicast packets.
|
* Don't process broadcast or multicast packets.
|
||||||
|
@ -352,16 +372,18 @@ static unsigned int sfe_cm_post_routing(struct sk_buff *skb, int is_v4)
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
|
|
||||||
/*
|
/*
|
||||||
* Don't process untracked connections.
|
* Don't process untracked connections.
|
||||||
*/
|
*/
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
|
||||||
if (unlikely(nf_ct_is_untracked(ct))) {
|
if (unlikely(nf_ct_is_untracked(ct))) {
|
||||||
|
#else
|
||||||
|
if (unlikely(ctinfo == IP_CT_UNTRACKED)) {
|
||||||
|
#endif
|
||||||
sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_CT_NO_TRACK);
|
sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_CT_NO_TRACK);
|
||||||
DEBUG_TRACE("untracked connection\n");
|
DEBUG_TRACE("untracked connection\n");
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
#endif /*KERNEL_VERSION(4, 12, 0)*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unconfirmed connection may be dropped by Linux at the final step,
|
* Unconfirmed connection may be dropped by Linux at the final step,
|
||||||
|
@ -479,8 +501,13 @@ static unsigned int sfe_cm_post_routing(struct sk_buff *skb, int is_v4)
|
||||||
sic.dest_td_max_window = ct->proto.tcp.seen[1].td_maxwin;
|
sic.dest_td_max_window = ct->proto.tcp.seen[1].td_maxwin;
|
||||||
sic.dest_td_end = ct->proto.tcp.seen[1].td_end;
|
sic.dest_td_end = ct->proto.tcp.seen[1].td_end;
|
||||||
sic.dest_td_max_end = ct->proto.tcp.seen[1].td_maxend;
|
sic.dest_td_max_end = ct->proto.tcp.seen[1].td_maxend;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
|
||||||
if (nf_ct_tcp_no_window_check
|
net = nf_ct_net(ct);
|
||||||
|
tn = nf_tcp_pernet(net);
|
||||||
|
if ((tn&&tn->tcp_no_window_check)
|
||||||
|
#else
|
||||||
|
if (nf_ct_tcp_no_window_check
|
||||||
|
#endif
|
||||||
|| (ct->proto.tcp.seen[0].flags & IP_CT_TCP_FLAG_BE_LIBERAL)
|
|| (ct->proto.tcp.seen[0].flags & IP_CT_TCP_FLAG_BE_LIBERAL)
|
||||||
|| (ct->proto.tcp.seen[1].flags & IP_CT_TCP_FLAG_BE_LIBERAL)) {
|
|| (ct->proto.tcp.seen[1].flags & IP_CT_TCP_FLAG_BE_LIBERAL)) {
|
||||||
sic.flags |= SFE_CREATE_FLAG_NO_SEQ_CHECK;
|
sic.flags |= SFE_CREATE_FLAG_NO_SEQ_CHECK;
|
||||||
|
@ -510,6 +537,21 @@ static unsigned int sfe_cm_post_routing(struct sk_buff *skb, int is_v4)
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&ct->lock);
|
spin_unlock_bh(&ct->lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Somehow, SFE is not playing nice with IPSec traffic.
|
||||||
|
* Do not accelerate for now.
|
||||||
|
*/
|
||||||
|
if (ntohs(sic.dest_port) == 4500 || ntohs(sic.dest_port) == 500) {
|
||||||
|
if (likely(is_v4))
|
||||||
|
DEBUG_TRACE("IPsec bypass: %pI4:%d(%pI4:%d) to %pI4:%d(%pI4:%d)\n",
|
||||||
|
&sic.src_ip.ip, ntohs(sic.src_port), &sic.src_ip_xlate.ip, ntohs(sic.src_port_xlate),
|
||||||
|
&sic.dest_ip.ip, ntohs(sic.dest_port), &sic.dest_ip_xlate.ip, ntohs(sic.dest_port_xlate));
|
||||||
|
else
|
||||||
|
DEBUG_TRACE("IPsec bypass: %pI6:%d to %pI6:%d\n",
|
||||||
|
&sic.src_ip.ip6, ntohs(sic.src_port), &sic.dest_ip.ip6, ntohs(sic.dest_port));
|
||||||
|
return NF_ACCEPT;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IPPROTO_UDP:
|
case IPPROTO_UDP:
|
||||||
|
@ -533,10 +575,10 @@ static unsigned int sfe_cm_post_routing(struct sk_buff *skb, int is_v4)
|
||||||
* For packets de-capsulated from xfrm, we still can accelerate it
|
* For packets de-capsulated from xfrm, we still can accelerate it
|
||||||
* on the direction we just received the packet.
|
* on the direction we just received the packet.
|
||||||
*/
|
*/
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
|
||||||
if (unlikely(skb_ext_exist(skb, SKB_EXT_SEC_PATH))) {
|
|
||||||
#else
|
|
||||||
if (unlikely(skb->sp)) {
|
if (unlikely(skb->sp)) {
|
||||||
|
#else
|
||||||
|
if (unlikely(secpath_exists(skb))) {
|
||||||
#endif
|
#endif
|
||||||
if (sic.protocol == IPPROTO_TCP &&
|
if (sic.protocol == IPPROTO_TCP &&
|
||||||
!(sic.flags & SFE_CREATE_FLAG_NO_SEQ_CHECK)) {
|
!(sic.flags & SFE_CREATE_FLAG_NO_SEQ_CHECK)) {
|
||||||
|
@ -564,25 +606,27 @@ static unsigned int sfe_cm_post_routing(struct sk_buff *skb, int is_v4)
|
||||||
* Get the net device and MAC addresses that correspond to the various source and
|
* Get the net device and MAC addresses that correspond to the various source and
|
||||||
* destination host addresses.
|
* destination host addresses.
|
||||||
*/
|
*/
|
||||||
if (!sfe_cm_find_dev_and_mac_addr(&sic.src_ip, &src_dev_tmp, sic.src_mac, is_v4)) {
|
if (!sfe_cm_find_dev_and_mac_addr(NULL, &sic.src_ip, &src_dev_tmp, sic.src_mac, is_v4)) {
|
||||||
sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_SRC_DEV);
|
sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_SRC_DEV);
|
||||||
return NF_ACCEPT;
|
return NF_ACCEPT;
|
||||||
}
|
}
|
||||||
src_dev = src_dev_tmp;
|
src_dev = src_dev_tmp;
|
||||||
|
|
||||||
if (!sfe_cm_find_dev_and_mac_addr(&sic.src_ip_xlate, &dev, sic.src_mac_xlate, is_v4)) {
|
if (!sfe_cm_find_dev_and_mac_addr(NULL, &sic.src_ip_xlate, &dev, sic.src_mac_xlate, is_v4)) {
|
||||||
sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_SRC_XLATE_DEV);
|
sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_SRC_XLATE_DEV);
|
||||||
goto done1;
|
goto done1;
|
||||||
}
|
}
|
||||||
dev_put(dev);
|
dev_put(dev);
|
||||||
|
/* Somehow, for IPv6, we need this workaround as well */
|
||||||
if (!sfe_cm_find_dev_and_mac_addr(&sic.dest_ip, &dev, sic.dest_mac, is_v4)) {
|
if (unlikely(!is_v4))
|
||||||
|
tmp_skb = skb;
|
||||||
|
if (!sfe_cm_find_dev_and_mac_addr(tmp_skb, &sic.dest_ip, &dev, sic.dest_mac, is_v4)) {
|
||||||
sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_DEST_DEV);
|
sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_DEST_DEV);
|
||||||
goto done1;
|
goto done1;
|
||||||
}
|
}
|
||||||
dev_put(dev);
|
dev_put(dev);
|
||||||
|
|
||||||
if (!sfe_cm_find_dev_and_mac_addr(&sic.dest_ip_xlate, &dest_dev_tmp, sic.dest_mac_xlate, is_v4)) {
|
if (!sfe_cm_find_dev_and_mac_addr(skb, &sic.dest_ip_xlate, &dest_dev_tmp, sic.dest_mac_xlate, is_v4)) {
|
||||||
sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_DEST_XLATE_DEV);
|
sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_DEST_XLATE_DEV);
|
||||||
goto done1;
|
goto done1;
|
||||||
}
|
}
|
||||||
|
@ -688,14 +732,11 @@ static int sfe_cm_conntrack_event(unsigned int events, struct nf_ct_event *item)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
|
||||||
/*
|
|
||||||
* If this is an untracked connection then we can't have any state either.
|
|
||||||
*/
|
|
||||||
if (unlikely(nf_ct_is_untracked(ct))) {
|
if (unlikely(nf_ct_is_untracked(ct))) {
|
||||||
DEBUG_TRACE("ignoring untracked conn\n");
|
DEBUG_TRACE("ignoring untracked conn\n");
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
}
|
}
|
||||||
#endif /*KERNEL_VERSION(4, 12, 0)*/
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We're only interested in destroy events.
|
* We're only interested in destroy events.
|
||||||
|
@ -825,18 +866,17 @@ static void sfe_cm_sync_rule(struct sfe_connection_sync *sis)
|
||||||
ct = nf_ct_tuplehash_to_ctrack(h);
|
ct = nf_ct_tuplehash_to_ctrack(h);
|
||||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0))
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0))
|
||||||
NF_CT_ASSERT(ct->timeout.data == (unsigned long)ct);
|
NF_CT_ASSERT(ct->timeout.data == (unsigned long)ct);
|
||||||
#endif /*KERNEL_VERSION(4, 9, 0)*/
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only update if this is not a fixed timeout
|
* Only update if this is not a fixed timeout
|
||||||
*/
|
*/
|
||||||
if (!test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
|
if (!test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
|
||||||
spin_lock_bh(&ct->lock);
|
spin_lock_bh(&ct->lock);
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0))
|
||||||
ct->timeout += sis->delta_jiffies;
|
|
||||||
#else
|
|
||||||
ct->timeout.expires += sis->delta_jiffies;
|
ct->timeout.expires += sis->delta_jiffies;
|
||||||
#endif /*KERNEL_VERSION(4, 9, 0)*/
|
#else
|
||||||
|
ct->timeout += sis->delta_jiffies;
|
||||||
|
#endif
|
||||||
spin_unlock_bh(&ct->lock);
|
spin_unlock_bh(&ct->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -891,26 +931,26 @@ static void sfe_cm_sync_rule(struct sfe_connection_sync *sis)
|
||||||
|
|
||||||
if (reply_pkts != 0) {
|
if (reply_pkts != 0) {
|
||||||
unsigned int *timeouts;
|
unsigned int *timeouts;
|
||||||
|
struct nf_conntrack_l4proto *l4proto __maybe_unused;
|
||||||
set_bit(IPS_SEEN_REPLY_BIT, &ct->status);
|
set_bit(IPS_SEEN_REPLY_BIT, &ct->status);
|
||||||
set_bit(IPS_ASSURED_BIT, &ct->status);
|
set_bit(IPS_ASSURED_BIT, &ct->status);
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0))
|
||||||
timeouts = nf_ct_timeout_lookup(ct);
|
|
||||||
#else
|
|
||||||
struct nf_conntrack_l4proto *l4proto;
|
|
||||||
|
|
||||||
l4proto = __nf_ct_l4proto_find((sis->is_v6 ? AF_INET6 : AF_INET), IPPROTO_UDP);
|
l4proto = __nf_ct_l4proto_find((sis->is_v6 ? AF_INET6 : AF_INET), IPPROTO_UDP);
|
||||||
timeouts = nf_ct_timeout_lookup(&init_net, ct, l4proto);
|
timeouts = nf_ct_timeout_lookup(&init_net, ct, l4proto);
|
||||||
#endif /*KERNEL_VERSION(4, 19, 0)*/
|
spin_lock_bh(&ct->lock);
|
||||||
|
ct->timeout.expires = jiffies + timeouts[UDP_CT_REPLIED];
|
||||||
|
spin_unlock_bh(&ct->lock);
|
||||||
|
#else
|
||||||
|
timeouts = nf_ct_timeout_lookup(ct);
|
||||||
|
if (!timeouts) {
|
||||||
|
timeouts = nf_udp_pernet(nf_ct_net(ct))->timeouts;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_bh(&ct->lock);
|
spin_lock_bh(&ct->lock);
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
|
|
||||||
ct->timeout = jiffies + timeouts[UDP_CT_REPLIED];
|
ct->timeout = jiffies + timeouts[UDP_CT_REPLIED];
|
||||||
#else
|
|
||||||
ct->timeout.expires = jiffies + timeouts[UDP_CT_REPLIED];
|
|
||||||
#endif /*KERNEL_VERSION(4, 9, 0)*/
|
|
||||||
spin_unlock_bh(&ct->lock);
|
spin_unlock_bh(&ct->lock);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1001,6 +1041,9 @@ static int __init sfe_cm_init(void)
|
||||||
{
|
{
|
||||||
struct sfe_cm *sc = &__sc;
|
struct sfe_cm *sc = &__sc;
|
||||||
int result = -1;
|
int result = -1;
|
||||||
|
#ifdef CONFIG_SFE_ECM
|
||||||
|
int (*fast_recv)(struct sk_buff *skb);
|
||||||
|
#endif
|
||||||
|
|
||||||
DEBUG_INFO("SFE CM init\n");
|
DEBUG_INFO("SFE CM init\n");
|
||||||
|
|
||||||
|
@ -1036,7 +1079,11 @@ static int __init sfe_cm_init(void)
|
||||||
/*
|
/*
|
||||||
* Register our netfilter hooks.
|
* Register our netfilter hooks.
|
||||||
*/
|
*/
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0))
|
||||||
|
result = nf_register_hooks(sfe_cm_ops_post_routing, ARRAY_SIZE(sfe_cm_ops_post_routing));
|
||||||
|
#else
|
||||||
result = nf_register_net_hooks(&init_net, sfe_cm_ops_post_routing, ARRAY_SIZE(sfe_cm_ops_post_routing));
|
result = nf_register_net_hooks(&init_net, sfe_cm_ops_post_routing, ARRAY_SIZE(sfe_cm_ops_post_routing));
|
||||||
|
#endif
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
DEBUG_ERROR("can't register nf post routing hook: %d\n", result);
|
DEBUG_ERROR("can't register nf post routing hook: %d\n", result);
|
||||||
goto exit3;
|
goto exit3;
|
||||||
|
@ -1049,22 +1096,30 @@ static int __init sfe_cm_init(void)
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_NF_CONNTRACK_EVENTS
|
#ifdef CONFIG_NF_CONNTRACK_EVENTS
|
||||||
#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
|
#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
|
||||||
(void)nf_conntrack_register_chain_notifier(&init_net, &sfe_cm_conntrack_notifier);
|
result = nf_conntrack_register_chain_notifier(&init_net, &sfe_cm_conntrack_notifier);
|
||||||
#else
|
#else
|
||||||
result = nf_conntrack_register_notifier(&init_net, &sfe_cm_conntrack_notifier);
|
result = nf_conntrack_register_notifier(&init_net, &sfe_cm_conntrack_notifier);
|
||||||
|
#endif
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
DEBUG_ERROR("can't register nf notifier hook: %d\n", result);
|
DEBUG_ERROR("can't register nf notifier hook: %d\n", result);
|
||||||
goto exit4;
|
goto exit4;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
spin_lock_init(&sc->lock);
|
spin_lock_init(&sc->lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hook the receive path in the network stack.
|
* Hook the receive path in the network stack.
|
||||||
*/
|
*/
|
||||||
|
#ifdef CONFIG_SFE_ECM
|
||||||
|
rcu_read_lock();
|
||||||
|
fast_recv = rcu_dereference(athrs_fast_nat_recv);
|
||||||
|
rcu_read_unlock();
|
||||||
|
if (!fast_recv) {
|
||||||
|
BUG_ON(athrs_fast_nat_recv);
|
||||||
|
}
|
||||||
|
#else
|
||||||
BUG_ON(athrs_fast_nat_recv);
|
BUG_ON(athrs_fast_nat_recv);
|
||||||
|
#endif
|
||||||
RCU_INIT_POINTER(athrs_fast_nat_recv, sfe_cm_recv);
|
RCU_INIT_POINTER(athrs_fast_nat_recv, sfe_cm_recv);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1075,10 +1130,15 @@ static int __init sfe_cm_init(void)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#ifdef CONFIG_NF_CONNTRACK_EVENTS
|
#ifdef CONFIG_NF_CONNTRACK_EVENTS
|
||||||
#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
|
|
||||||
exit4:
|
exit4:
|
||||||
|
#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0))
|
||||||
|
nf_unregister_hooks(sfe_cm_ops_post_routing, ARRAY_SIZE(sfe_cm_ops_post_routing));
|
||||||
|
#else
|
||||||
nf_unregister_net_hooks(&init_net, sfe_cm_ops_post_routing, ARRAY_SIZE(sfe_cm_ops_post_routing));
|
nf_unregister_net_hooks(&init_net, sfe_cm_ops_post_routing, ARRAY_SIZE(sfe_cm_ops_post_routing));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
exit3:
|
exit3:
|
||||||
unregister_inet6addr_notifier(&sc->inet6_notifier);
|
unregister_inet6addr_notifier(&sc->inet6_notifier);
|
||||||
|
@ -1129,8 +1189,12 @@ static void __exit sfe_cm_exit(void)
|
||||||
nf_conntrack_unregister_notifier(&init_net, &sfe_cm_conntrack_notifier);
|
nf_conntrack_unregister_notifier(&init_net, &sfe_cm_conntrack_notifier);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
nf_unregister_net_hooks(&init_net, sfe_cm_ops_post_routing, ARRAY_SIZE(sfe_cm_ops_post_routing));
|
|
||||||
|
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0))
|
||||||
|
nf_unregister_hooks(sfe_cm_ops_post_routing, ARRAY_SIZE(sfe_cm_ops_post_routing));
|
||||||
|
#else
|
||||||
|
nf_unregister_net_hooks(&init_net, sfe_cm_ops_post_routing, ARRAY_SIZE(sfe_cm_ops_post_routing));
|
||||||
|
#endif
|
||||||
unregister_inet6addr_notifier(&sc->inet6_notifier);
|
unregister_inet6addr_notifier(&sc->inet6_notifier);
|
||||||
unregister_inetaddr_notifier(&sc->inet_notifier);
|
unregister_inetaddr_notifier(&sc->inet_notifier);
|
||||||
unregister_netdevice_notifier(&sc->dev_notifier);
|
unregister_netdevice_notifier(&sc->dev_notifier);
|
||||||
|
|
|
@ -152,8 +152,9 @@ extern int (*athrs_fast_nat_recv)(struct sk_buff *skb);
|
||||||
/*
|
/*
|
||||||
* Expose what should be a static flag in the TCP connection tracker.
|
* Expose what should be a static flag in the TCP connection tracker.
|
||||||
*/
|
*/
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)
|
||||||
extern int nf_ct_tcp_no_window_check;
|
extern int nf_ct_tcp_no_window_check;
|
||||||
|
#endif
|
||||||
/*
|
/*
|
||||||
* This callback will be called in a timer
|
* This callback will be called in a timer
|
||||||
* at 100 times per second to sync stats back to
|
* at 100 times per second to sync stats back to
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* sfe_ipv4.c
|
* sfe_ipv4.c
|
||||||
* Shortcut forwarding engine - IPv4 edition.
|
* Shortcut forwarding engine - IPv4 edition.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2013-2016, 2019, The Linux Foundation. All rights reserved.
|
* Copyright (c) 2013-2016, 2019-2020 The Linux Foundation. All rights reserved.
|
||||||
* Permission to use, copy, modify, and/or distribute this software for
|
* Permission to use, copy, modify, and/or distribute this software for
|
||||||
* any purpose with or without fee is hereby granted, provided that the
|
* any purpose with or without fee is hereby granted, provided that the
|
||||||
* above copyright notice and this permission notice appear in all copies.
|
* above copyright notice and this permission notice appear in all copies.
|
||||||
|
@ -1311,14 +1311,13 @@ static int sfe_ipv4_recv_udp(struct sfe_ipv4 *si, struct sk_buff *skb, struct ne
|
||||||
* change the cloned skb's data section.
|
* change the cloned skb's data section.
|
||||||
*/
|
*/
|
||||||
if (unlikely(skb_cloned(skb))) {
|
if (unlikely(skb_cloned(skb))) {
|
||||||
DEBUG_TRACE("%p: skb is a cloned skb\n", skb);
|
DEBUG_TRACE("%px: skb is a cloned skb\n", skb);
|
||||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||||
if (!skb) {
|
if (!skb) {
|
||||||
DEBUG_WARN("Failed to unshare the cloned skb\n");
|
DEBUG_WARN("Failed to unshare the cloned skb\n");
|
||||||
si->exception_events[SFE_IPV4_EXCEPTION_EVENT_CLONED_SKB_UNSHARE_ERROR]++;
|
si->exception_events[SFE_IPV4_EXCEPTION_EVENT_CLONED_SKB_UNSHARE_ERROR]++;
|
||||||
si->packets_not_forwarded++;
|
si->packets_not_forwarded++;
|
||||||
spin_unlock_bh(&si->lock);
|
spin_unlock_bh(&si->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1891,14 +1890,13 @@ static int sfe_ipv4_recv_tcp(struct sfe_ipv4 *si, struct sk_buff *skb, struct ne
|
||||||
* change the cloned skb's data section.
|
* change the cloned skb's data section.
|
||||||
*/
|
*/
|
||||||
if (unlikely(skb_cloned(skb))) {
|
if (unlikely(skb_cloned(skb))) {
|
||||||
DEBUG_TRACE("%p: skb is a cloned skb\n", skb);
|
DEBUG_TRACE("%px: skb is a cloned skb\n", skb);
|
||||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||||
if (!skb) {
|
if (!skb) {
|
||||||
DEBUG_WARN("Failed to unshare the cloned skb\n");
|
DEBUG_WARN("Failed to unshare the cloned skb\n");
|
||||||
si->exception_events[SFE_IPV4_EXCEPTION_EVENT_CLONED_SKB_UNSHARE_ERROR]++;
|
si->exception_events[SFE_IPV4_EXCEPTION_EVENT_CLONED_SKB_UNSHARE_ERROR]++;
|
||||||
si->packets_not_forwarded++;
|
si->packets_not_forwarded++;
|
||||||
spin_unlock_bh(&si->lock);
|
spin_unlock_bh(&si->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2512,7 +2510,7 @@ int sfe_ipv4_create_rule(struct sfe_connection_create *sic)
|
||||||
spin_unlock_bh(&si->lock);
|
spin_unlock_bh(&si->lock);
|
||||||
|
|
||||||
DEBUG_TRACE("connection already exists - mark: %08x, p: %d\n"
|
DEBUG_TRACE("connection already exists - mark: %08x, p: %d\n"
|
||||||
" s: %s:%pM:%pI4:%u, d: %s:%pM:%pI4:%u\n",
|
" s: %s:%pxM:%pI4:%u, d: %s:%pxM:%pI4:%u\n",
|
||||||
sic->mark, sic->protocol,
|
sic->mark, sic->protocol,
|
||||||
sic->src_dev->name, sic->src_mac, &sic->src_ip.ip, ntohs(sic->src_port),
|
sic->src_dev->name, sic->src_mac, &sic->src_ip.ip, ntohs(sic->src_port),
|
||||||
sic->dest_dev->name, sic->dest_mac, &sic->dest_ip.ip, ntohs(sic->dest_port));
|
sic->dest_dev->name, sic->dest_mac, &sic->dest_ip.ip, ntohs(sic->dest_port));
|
||||||
|
@ -2728,8 +2726,8 @@ int sfe_ipv4_create_rule(struct sfe_connection_create *sic)
|
||||||
* We have everything we need!
|
* We have everything we need!
|
||||||
*/
|
*/
|
||||||
DEBUG_INFO("new connection - mark: %08x, p: %d\n"
|
DEBUG_INFO("new connection - mark: %08x, p: %d\n"
|
||||||
" s: %s:%pM(%pM):%pI4(%pI4):%u(%u)\n"
|
" s: %s:%pxM(%pxM):%pI4(%pI4):%u(%u)\n"
|
||||||
" d: %s:%pM(%pM):%pI4(%pI4):%u(%u)\n",
|
" d: %s:%pxM(%pxM):%pI4(%pI4):%u(%u)\n",
|
||||||
sic->mark, sic->protocol,
|
sic->mark, sic->protocol,
|
||||||
sic->src_dev->name, sic->src_mac, sic->src_mac_xlate,
|
sic->src_dev->name, sic->src_mac, sic->src_mac_xlate,
|
||||||
&sic->src_ip.ip, &sic->src_ip_xlate.ip, ntohs(sic->src_port), ntohs(sic->src_port_xlate),
|
&sic->src_ip.ip, &sic->src_ip_xlate.ip, ntohs(sic->src_port), ntohs(sic->src_port_xlate),
|
||||||
|
@ -2858,17 +2856,17 @@ another_round:
|
||||||
/*
|
/*
|
||||||
* sfe_ipv4_periodic_sync()
|
* sfe_ipv4_periodic_sync()
|
||||||
*/
|
*/
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
||||||
static void sfe_ipv4_periodic_sync(struct timer_list *arg)
|
|
||||||
#else
|
|
||||||
static void sfe_ipv4_periodic_sync(unsigned long arg)
|
static void sfe_ipv4_periodic_sync(unsigned long arg)
|
||||||
#endif /*KERNEL_VERSION(4, 15, 0)*/
|
|
||||||
{
|
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
|
|
||||||
struct sfe_ipv4 *si = (struct sfe_ipv4 *)arg->cust_data;
|
|
||||||
#else
|
#else
|
||||||
|
static void sfe_ipv4_periodic_sync(struct timer_list *tl)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
||||||
struct sfe_ipv4 *si = (struct sfe_ipv4 *)arg;
|
struct sfe_ipv4 *si = (struct sfe_ipv4 *)arg;
|
||||||
#endif /*KERNEL_VERSION(4, 15, 0)*/
|
#else
|
||||||
|
struct sfe_ipv4 *si = from_timer(si, tl, timer);
|
||||||
|
#endif
|
||||||
u64 now_jiffies;
|
u64 now_jiffies;
|
||||||
int quota;
|
int quota;
|
||||||
sfe_sync_rule_callback_t sync_rule_callback;
|
sfe_sync_rule_callback_t sync_rule_callback;
|
||||||
|
@ -3547,12 +3545,11 @@ static int __init sfe_ipv4_init(void)
|
||||||
/*
|
/*
|
||||||
* Create a timer to handle periodic statistics.
|
* Create a timer to handle periodic statistics.
|
||||||
*/
|
*/
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
||||||
timer_setup(&si->timer, sfe_ipv4_periodic_sync, 0);
|
|
||||||
si->timer.cust_data = (unsigned long)si;
|
|
||||||
#else
|
|
||||||
setup_timer(&si->timer, sfe_ipv4_periodic_sync, (unsigned long)si);
|
setup_timer(&si->timer, sfe_ipv4_periodic_sync, (unsigned long)si);
|
||||||
#endif /*KERNEL_VERSION(4, 15, 0)*/
|
#else
|
||||||
|
timer_setup(&si->timer, sfe_ipv4_periodic_sync, 0);
|
||||||
|
#endif
|
||||||
mod_timer(&si->timer, jiffies + ((HZ + 99) / 100));
|
mod_timer(&si->timer, jiffies + ((HZ + 99) / 100));
|
||||||
|
|
||||||
spin_lock_init(&si->lock);
|
spin_lock_init(&si->lock);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* sfe_ipv6.c
|
* sfe_ipv6.c
|
||||||
* Shortcut forwarding engine - IPv6 support.
|
* Shortcut forwarding engine - IPv6 support.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015-2016, 2019, The Linux Foundation. All rights reserved.
|
* Copyright (c) 2015-2016, 2019-2020 The Linux Foundation. All rights reserved.
|
||||||
* Permission to use, copy, modify, and/or distribute this software for
|
* Permission to use, copy, modify, and/or distribute this software for
|
||||||
* any purpose with or without fee is hereby granted, provided that the
|
* any purpose with or without fee is hereby granted, provided that the
|
||||||
* above copyright notice and this permission notice appear in all copies.
|
* above copyright notice and this permission notice appear in all copies.
|
||||||
|
@ -1369,14 +1369,13 @@ static int sfe_ipv6_recv_udp(struct sfe_ipv6 *si, struct sk_buff *skb, struct ne
|
||||||
* change the cloned skb's data section.
|
* change the cloned skb's data section.
|
||||||
*/
|
*/
|
||||||
if (unlikely(skb_cloned(skb))) {
|
if (unlikely(skb_cloned(skb))) {
|
||||||
DEBUG_TRACE("%p: skb is a cloned skb\n", skb);
|
DEBUG_TRACE("%px: skb is a cloned skb\n", skb);
|
||||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||||
if (!skb) {
|
if (!skb) {
|
||||||
DEBUG_WARN("Failed to unshare the cloned skb\n");
|
DEBUG_WARN("Failed to unshare the cloned skb\n");
|
||||||
si->exception_events[SFE_IPV6_EXCEPTION_EVENT_CLONED_SKB_UNSHARE_ERROR]++;
|
si->exception_events[SFE_IPV6_EXCEPTION_EVENT_CLONED_SKB_UNSHARE_ERROR]++;
|
||||||
si->packets_not_forwarded++;
|
si->packets_not_forwarded++;
|
||||||
spin_unlock_bh(&si->lock);
|
spin_unlock_bh(&si->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1929,14 +1928,13 @@ static int sfe_ipv6_recv_tcp(struct sfe_ipv6 *si, struct sk_buff *skb, struct ne
|
||||||
* change the cloned skb's data section.
|
* change the cloned skb's data section.
|
||||||
*/
|
*/
|
||||||
if (unlikely(skb_cloned(skb))) {
|
if (unlikely(skb_cloned(skb))) {
|
||||||
DEBUG_TRACE("%p: skb is a cloned skb\n", skb);
|
DEBUG_TRACE("%px: skb is a cloned skb\n", skb);
|
||||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||||
if (!skb) {
|
if (!skb) {
|
||||||
DEBUG_WARN("Failed to unshare the cloned skb\n");
|
DEBUG_WARN("Failed to unshare the cloned skb\n");
|
||||||
si->exception_events[SFE_IPV6_EXCEPTION_EVENT_CLONED_SKB_UNSHARE_ERROR]++;
|
si->exception_events[SFE_IPV6_EXCEPTION_EVENT_CLONED_SKB_UNSHARE_ERROR]++;
|
||||||
si->packets_not_forwarded++;
|
si->packets_not_forwarded++;
|
||||||
spin_unlock_bh(&si->lock);
|
spin_unlock_bh(&si->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2328,7 +2326,7 @@ int sfe_ipv6_recv(struct net_device *dev, struct sk_buff *skb)
|
||||||
si->packets_not_forwarded++;
|
si->packets_not_forwarded++;
|
||||||
spin_unlock_bh(&si->lock);
|
spin_unlock_bh(&si->lock);
|
||||||
|
|
||||||
DEBUG_TRACE("payload_len: %u, exceeds len: %u\n", payload_len, (len - sizeof(struct sfe_ipv6_ip_hdr)));
|
DEBUG_TRACE("payload_len: %u, exceeds len: %u\n", payload_len, (len - (unsigned int)sizeof(struct sfe_ipv6_ip_hdr)));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2526,7 +2524,7 @@ int sfe_ipv6_create_rule(struct sfe_connection_create *sic)
|
||||||
spin_unlock_bh(&si->lock);
|
spin_unlock_bh(&si->lock);
|
||||||
|
|
||||||
DEBUG_TRACE("connection already exists - mark: %08x, p: %d\n"
|
DEBUG_TRACE("connection already exists - mark: %08x, p: %d\n"
|
||||||
" s: %s:%pM:%pI6:%u, d: %s:%pM:%pI6:%u\n",
|
" s: %s:%pxM:%pI6:%u, d: %s:%pxM:%pI6:%u\n",
|
||||||
sic->mark, sic->protocol,
|
sic->mark, sic->protocol,
|
||||||
sic->src_dev->name, sic->src_mac, sic->src_ip.ip6, ntohs(sic->src_port),
|
sic->src_dev->name, sic->src_mac, sic->src_ip.ip6, ntohs(sic->src_port),
|
||||||
sic->dest_dev->name, sic->dest_mac, sic->dest_ip.ip6, ntohs(sic->dest_port));
|
sic->dest_dev->name, sic->dest_mac, sic->dest_ip.ip6, ntohs(sic->dest_port));
|
||||||
|
@ -2742,8 +2740,8 @@ int sfe_ipv6_create_rule(struct sfe_connection_create *sic)
|
||||||
* We have everything we need!
|
* We have everything we need!
|
||||||
*/
|
*/
|
||||||
DEBUG_INFO("new connection - mark: %08x, p: %d\n"
|
DEBUG_INFO("new connection - mark: %08x, p: %d\n"
|
||||||
" s: %s:%pM(%pM):%pI6(%pI6):%u(%u)\n"
|
" s: %s:%pxM(%pxM):%pI6(%pI6):%u(%u)\n"
|
||||||
" d: %s:%pM(%pM):%pI6(%pI6):%u(%u)\n",
|
" d: %s:%pxM(%pxM):%pI6(%pI6):%u(%u)\n",
|
||||||
sic->mark, sic->protocol,
|
sic->mark, sic->protocol,
|
||||||
sic->src_dev->name, sic->src_mac, sic->src_mac_xlate,
|
sic->src_dev->name, sic->src_mac, sic->src_mac_xlate,
|
||||||
sic->src_ip.ip6, sic->src_ip_xlate.ip6, ntohs(sic->src_port), ntohs(sic->src_port_xlate),
|
sic->src_ip.ip6, sic->src_ip_xlate.ip6, ntohs(sic->src_port), ntohs(sic->src_port_xlate),
|
||||||
|
@ -2866,17 +2864,17 @@ another_round:
|
||||||
/*
|
/*
|
||||||
* sfe_ipv6_periodic_sync()
|
* sfe_ipv6_periodic_sync()
|
||||||
*/
|
*/
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
||||||
static void sfe_ipv6_periodic_sync(struct timer_list *arg)
|
|
||||||
#else
|
|
||||||
static void sfe_ipv6_periodic_sync(unsigned long arg)
|
static void sfe_ipv6_periodic_sync(unsigned long arg)
|
||||||
#endif /*KERNEL_VERSION(4, 15, 0)*/
|
|
||||||
{
|
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
|
|
||||||
struct sfe_ipv6 *si = (struct sfe_ipv6 *)arg->cust_data;
|
|
||||||
#else
|
#else
|
||||||
|
static void sfe_ipv6_periodic_sync(struct timer_list *tl)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
||||||
struct sfe_ipv6 *si = (struct sfe_ipv6 *)arg;
|
struct sfe_ipv6 *si = (struct sfe_ipv6 *)arg;
|
||||||
#endif /*KERNEL_VERSION(4, 15, 0)*/
|
#else
|
||||||
|
struct sfe_ipv6 *si = from_timer(si, tl, timer);
|
||||||
|
#endif
|
||||||
u64 now_jiffies;
|
u64 now_jiffies;
|
||||||
int quota;
|
int quota;
|
||||||
sfe_sync_rule_callback_t sync_rule_callback;
|
sfe_sync_rule_callback_t sync_rule_callback;
|
||||||
|
@ -3555,12 +3553,11 @@ static int __init sfe_ipv6_init(void)
|
||||||
/*
|
/*
|
||||||
* Create a timer to handle periodic statistics.
|
* Create a timer to handle periodic statistics.
|
||||||
*/
|
*/
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
||||||
timer_setup(&si->timer, sfe_ipv6_periodic_sync, 0);
|
|
||||||
si->timer.cust_data = (unsigned long)si;
|
|
||||||
#else
|
|
||||||
setup_timer(&si->timer, sfe_ipv6_periodic_sync, (unsigned long)si);
|
setup_timer(&si->timer, sfe_ipv6_periodic_sync, (unsigned long)si);
|
||||||
#endif /*KERNEL_VERSION(4, 15, 0)*/
|
#else
|
||||||
|
timer_setup(&si->timer, sfe_ipv6_periodic_sync, 0);
|
||||||
|
#endif
|
||||||
mod_timer(&si->timer, jiffies + ((HZ + 99) / 100));
|
mod_timer(&si->timer, jiffies + ((HZ + 99) / 100));
|
||||||
|
|
||||||
spin_lock_init(&si->lock);
|
spin_lock_init(&si->lock);
|
||||||
|
|
60
simulated-driver/Makefile
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2015,2016 The Linux Foundation. All rights reserved.
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
|
# above copyright notice and this permission notice appear in all copies.
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
include $(INCLUDE_DIR)/kernel.mk
|
||||||
|
|
||||||
|
PKG_NAME:=shortcut-fe-simulated-driver
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
PKG_SOURCE_URL:=https://source.codeaurora.org/quic/qsdk/oss/lklm/shortcut-fe
|
||||||
|
PKG_SOURCE_PROTO:=git
|
||||||
|
PKG_SOURCE_DATE:=2021-03-17
|
||||||
|
PKG_SOURCE_VERSION:=697977d8d0ccf0ab596e5692d08608a75dd7f33d
|
||||||
|
PKG_MIRROR_HASH:=659fa82a431e15af797a6c7069faeee02810453ad8b576c51c29f95a1761a045
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define KernelPackage/shortcut-fe-drv
|
||||||
|
SECTION:=kernel
|
||||||
|
CATEGORY:=Kernel modules
|
||||||
|
SUBMENU:=Network Support
|
||||||
|
DEPENDS:=@TARGET_ipq60xx||@TARGET_ipq806x||TARGET_ipq807x +kmod-shortcut-fe
|
||||||
|
KCONFIG:= \
|
||||||
|
CONFIG_NET_CLS_ACT=y \
|
||||||
|
CONFIG_XFRM=y
|
||||||
|
TITLE:=Simulated sfe driver for ECM
|
||||||
|
FILES:=$(PKG_BUILD_DIR)/simulated-driver/shortcut-fe-drv.ko
|
||||||
|
endef
|
||||||
|
|
||||||
|
define KernelPackage/shortcut-fe-drv/Description
|
||||||
|
Simulated sfe driver which act as an adapter to convert message
|
||||||
|
between a connection manager and the SFE core engine.
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
$(MAKE) $(PKG_JOBS) -C "$(LINUX_DIR)" \
|
||||||
|
$(KERNEL_MAKE_FLAGS) \
|
||||||
|
$(PKG_MAKE_FLAGS) \
|
||||||
|
M="$(PKG_BUILD_DIR)/simulated-driver" \
|
||||||
|
EXTRA_CFLAGS="-DSFE_SUPPORT_IPV6" \
|
||||||
|
modules
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/InstallDev
|
||||||
|
$(INSTALL_DIR) $(1)/usr/include/shortcut-fe
|
||||||
|
$(CP) -rf $(PKG_BUILD_DIR)/simulated-driver/sfe_drv.h $(1)/usr/include/shortcut-fe
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call KernelPackage,shortcut-fe-drv))
|
11
simulated-driver/patches/200-nss-qdisc-support.patch
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
--- ./simulated-driver/sfe_drv.c.orig 2020-06-16 12:49:47.680153371 +0800
|
||||||
|
+++ ./simulated-driver/sfe_drv.c 2020-06-16 12:50:18.540153371 +0800
|
||||||
|
@@ -1167,7 +1167,7 @@ int sfe_drv_recv(struct sk_buff *skb)
|
||||||
|
* If ingress Qdisc configured, and packet not processed by ingress Qdisc yet
|
||||||
|
* We can not accelerate this packet.
|
||||||
|
*/
|
||||||
|
- if (dev->ingress_queue && !(skb->tc_verd & TC_NCLS)) {
|
||||||
|
+ if (dev->ingress_queue && !(skb->tc_verd_qca_nss & TC_NCLS)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|