Add CTCGFW's main code

This commit is contained in:
CN_SZTL 2020-02-01 02:36:42 +08:00
parent 030cb13ae1
commit 83f33178d0
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
2374 changed files with 534382 additions and 1 deletions

View File

@ -15,7 +15,8 @@ DEVICE_TYPE?=router
# Default packages - the really basic set
DEFAULT_PACKAGES:=base-files libc libgcc busybox dropbear mtd uci opkg netifd fstools uclient-fetch logd urandom-seed urngd \
kmod-nf-nathelper kmod-nf-nathelper-extra kmod-ipt-raw wget libustream-openssl ca-certificates \
default-settings luci luci-compat luci-lib-ipkg
default-settings luci luci-compat luci-lib-ipkg \
luci-app-sfe luci-app-flowoffload
# For nas targets
DEFAULT_PACKAGES.nas:=block-mount fdisk lsblk mdadm
# For router targets

View File

@ -0,0 +1,54 @@
#
# Copyright (C) 2014-2018 OpenWrt-dist
# Copyright (C) 2014-2018 Jian Chang <aa65535@live.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=ChinaDNS
PKG_VERSION:=2.0.1
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/lrinQVQ/openwrt-chinadns/releases/download/v$(PKG_VERSION)
PKG_MD5SUM:=5c7aa6a89a969a1dd6469b4808b0759b
PKG_LICENSE:=GPLv3
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=Jian Chang <aa65535@live.com>
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)/$(PKG_NAME)-$(PKG_VERSION)
PKG_INSTALL:=1
PKG_FIXUP:=autoreconf
PKG_USE_MIPS16:=0
PKG_BUILD_PARALLEL:=1
include $(INCLUDE_DIR)/package.mk
define Package/ChinaDNS
SECTION:=net
CATEGORY:=Network
TITLE:=Protect yourself against DNS poisoning in China.
URL:=https://github.com/shadowsocks/ChinaDNS
endef
define Package/ChinaDNS/description
Protect yourself against DNS poisoning in China.
endef
define Package/ChinaDNS/conffiles
/etc/chinadns_chnroute.txt
endef
define Package/ChinaDNS/install
$(INSTALL_DIR) $(1)/etc
$(INSTALL_CONF) $(PKG_BUILD_DIR)/chnroute.txt $(1)/etc/chinadns_chnroute.txt
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/chinadns $(1)/usr/sbin
endef
$(eval $(call BuildPackage,ChinaDNS))

View File

@ -0,0 +1,86 @@
#
# Copyright (C) 2015-2016 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v3.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=gq-client
PKG_VERSION:=1.2.2
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)/temple
SECTION:=net
CATEGORY:=Network
TITLE:=GoQuiet
DEPENDS:=+libpthread
URL:=https://github.com/cbeuw/GoQuiet
endef
define Package/$(PKG_NAME)
$(call Package/$(PKG_NAME)/temple)
endef
define Package/gq-server
$(call Package/$(PKG_NAME)/temple)
endef
define Package/$(PKG_NAME)/description
GoQuiet-client
endef
define Package/gq-server/description
GoQuiet-server
endef
ifeq ($(ARCH),x86_64)
GOQUIET_NAME:=gq-client-linux-amd64-$(PKG_VERSION)
GOQUIETSERVER_NAME:=gq-server-linux-amd64-$(PKG_VERSION)
endif
ifeq ($(ARCH),i386)
GOQUIET_NAME:=gq-client-linux-386-$(PKG_VERSION)
GOQUIETSERVER_NAME:=gq-server-linux-386-$(PKG_VERSION)
endif
ifeq ($(ARCH),mipsel)
GOQUIET_NAME:=gq-client-linux-mips64-$(PKG_VERSION)
endif
ifeq ($(ARCH),mips)
GOQUIET_NAME:=gq-client-linux-mips-$(PKG_VERSION)
endif
ifeq ($(ARCH),arm)
GOQUIET_NAME:=gq-client-linux-arm-$(PKG_VERSION)
GOQUIETSERVER_NAME:=gq-server-linux-arm-$(PKG_VERSION)
endif
ifeq ($(ARCH),aarch64)
GOQUIET_NAME:=gq-client-linux-arm64-$(PKG_VERSION)
GOQUIETSERVER_NAME:=gq-server-linux-arm64-$(PKG_VERSION)
endif
define Build/Prepare
[ ! -f $(PKG_BUILD_DIR)/gq-client ] && wget -O $(PKG_BUILD_DIR)/gq-client https://github.com/cbeuw/GoQuiet/releases/download/v$(PKG_VERSION)/$(GOQUIET_NAME)
[ ! -f $(PKG_BUILD_DIR)/gq-server ] && wget -O $(PKG_BUILD_DIR)/gq-server https://github.com/cbeuw/GoQuiet/releases/download/v$(PKG_VERSION)/$(GOQUIETSERVER_NAME)
endef
define Build/Configure
chmod +x $(PKG_BUILD_DIR)/gq-client && upx --ultra-brute $(PKG_BUILD_DIR)/gq-client
chmod +x $(PKG_BUILD_DIR)/gq-server && upx --ultra-brute $(PKG_BUILD_DIR)/gq-server
endef
define Build/Compile
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/gq-client $(1)/usr/bin/
endef
define Package/gq-server/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/gq-server $(1)/usr/bin/
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
$(eval $(call BuildPackage,gq-server))

View File

@ -0,0 +1,72 @@
#
# Copyright (C) 2015-2016 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v3.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=UnblockNeteaseMusic-Go
PKG_VERSION:=snapshot
PKG_RELEASE:=r29-c39dcc6
ifeq ($(ARCH),x86_64)
PKG_ARCH_UnblockNeteaseMusic:=linux_amd64
endif
ifeq ($(ARCH),i386)
PKG_ARCH_UnblockNeteaseMusic:=linux_386
endif
ifeq ($(ARCH),mipsel)
PKG_ARCH_UnblockNeteaseMusic:=linux_mipsle
endif
ifeq ($(ARCH),mips)
PKG_ARCH_UnblockNeteaseMusic:=linux_mips
endif
ifeq ($(ARCH),arm)
ifeq ($(BOARD),kirkwood)
PKG_ARCH_UnblockNeteaseMusic:=linux_arm5
else ifeq ($(BOARD),bcm53xx)
PKG_ARCH_UnblockNeteaseMusic:=linux_arm5
else
PKG_ARCH_UnblockNeteaseMusic:=linux_arm7
endif
endif
ifeq ($(ARCH),aarch64)
PKG_ARCH_UnblockNeteaseMusic:=linux_arm64
endif
PKG_SOURCE:=UnblockNeteaseMusic_$(PKG_ARCH_UnblockNeteaseMusic).tar.gz
PKG_SOURCE_URL:=https://package.project-openwrt.eu.org/utils/UnblockNeteaseMusic-Go/$(PKG_RELEASE)/
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE)
PKG_HASH:=skip
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=net
CATEGORY:=Network
TITLE:=Revive unavailable songs for Netease Cloud Music (Golang)
URL:=https://github.com/cnsilvan/UnblockNeteaseMusic
endef
define Build/Prepare
tar -xzf $(DL_DIR)/$(PKG_SOURCE) -C $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
chmod 0755 $(PKG_BUILD_DIR)/UnblockNeteaseMusic
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/UnblockNeteaseMusic $(1)/usr/bin/UnblockNeteaseMusic
$(INSTALL_DIR) $(1)/usr/share/UnblockNeteaseMusic-Go
$(INSTALL_CONF) ./cert/server.crt $(1)/usr/share/UnblockNeteaseMusic-Go/server.crt
$(INSTALL_CONF) ./cert/server.key $(1)/usr/share/UnblockNeteaseMusic-Go/server.key
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDfTCCAmWgAwIBAgIJAKTlW9B59i1HMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV
BAYTAkNOMSQwIgYDVQQDDBtVbmJsb2NrTmV0ZWFzZU11c2ljIFJvb3QgQ0ExHTAb
BgNVBAoMFEdpdEh1Yi5jb20gQG5vbmRhbmVlMB4XDTE5MDUxODE2MDYxOFoXDTIw
MDUxNzE2MDYxOFowezELMAkGA1UEBhMCQ04xETAPBgNVBAcMCEhhbmd6aG91MSww
KgYDVQQKDCNOZXRFYXNlIChIYW5nemhvdSkgTmV0d29yayBDby4sIEx0ZDERMA8G
A1UECwwISVQgRGVwdC4xGDAWBgNVBAMMDyoubXVzaWMuMTYzLmNvbTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALobECypwEoe8VqM/FJvBRR3p2T+ZWdi
MSPrwfiRJr5p7OMtWBlLveCBV85+R3feidYbQTXlvVTdToY+GN6mFE1x6zG2dvLD
s4UuRnipmvGcFYhIRTX8J4AJiN8VMtW0TNXscRMudpz/FAVtsRrggRaThYg4f/rI
oAPMqKMsS4JoYhxs9ED6E6/tpj3XmSg1ekaXhgacYSYHeyxizZwoOFVCLH3TG5sF
sD6CYNnukYol8bR+VRpvHftIYss5Yz+DyyhYEAMJm1CfQo+xoGR3D0ozbT3hUnzm
fEoOhmSp3sALrFVE4iJSuajoh2/3xhmcyi3xZdWyq4F8hpb+URyaoW0CAwEAAaMt
MCswKQYDVR0RBCIwIIINbXVzaWMuMTYzLmNvbYIPKi5tdXNpYy4xNjMuY29tMA0G
CSqGSIb3DQEBCwUAA4IBAQB32SVz5jHUYv3ZG7SNF/LFJ904/LI8QlTe9R+Abb9z
bpXmQeo4pvNNOk3LgcTyuSIPQSHEFn32hk/MedB6Q2cKaGVKQq7Usne1jsV0JirG
wMx3PTcKPnX+XexRY8s6v6cNKSx5YlMQNFeH7p8MgKqdM/UX/dNCxT04X/ClmP1K
/rKqonXn4i3wmWprl7Q7Z1wqt0ygQRkNJKqdYKTu4oQcPON8/dRcseYdJzSoK2/G
H6cOJwKrRLzuUqQlphe6wyUsyTIbIJiFu1a1Gml6zB4lhLZhL89H2lYwdS8wWlc+
M+wYi+XTM/ylNHEIoKsOe2nscnwi/hTfHJOPPchHbEuM
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuhsQLKnASh7xWoz8Um8FFHenZP5lZ2IxI+vB+JEmvmns4y1Y
GUu94IFXzn5Hd96J1htBNeW9VN1Ohj4Y3qYUTXHrMbZ28sOzhS5GeKma8ZwViEhF
NfwngAmI3xUy1bRM1exxEy52nP8UBW2xGuCBFpOFiDh/+sigA8yooyxLgmhiHGz0
QPoTr+2mPdeZKDV6RpeGBpxhJgd7LGLNnCg4VUIsfdMbmwWwPoJg2e6RiiXxtH5V
Gm8d+0hiyzljP4PLKFgQAwmbUJ9Cj7GgZHcPSjNtPeFSfOZ8Sg6GZKnewAusVUTi
IlK5qOiHb/fGGZzKLfFl1bKrgXyGlv5RHJqhbQIDAQABAoIBAEmAvtalBMlBh1mY
LV/xcTQwPfDpeOtoILhrOOUPjxnNhD4FfrIe9BNjgmaQAXIadp4VjZ/X6PtHnOfw
RqpJNeOQhq/PvRMMsC59pF+rvQKH/wkgYhV8Ta2IFoLlQHqfB2nGRLKquzYumJ28
QSK4YMOl6CtxBTrrWiemAUTRDdGm8tARiipJH1SEJrS6d/NoRoJx2vixFgD2eS6X
bjnhGjIzvX/w5FWjctqj+SFITP1UI62b6DyWsPOkoosKNteK+Ulz+K6ZFvOx7day
XgUoTcVpwCVr2dVGhJtOrbKPcl1jYCYHJAHwzUZND4x4yftm1mnnsi3bthYqbtHQ
vxLE9YECgYEA9hiZxwiVvLjSe1xT/D75HbB8S1XSnwzpMmqgzStguxCQ0Qg5yiLI
UKRDY8UZvEDV4i2bQGy7mk8lFvX1q2z7Q30+dtT9r2N9a6ujMk5RMfo2BZg/poI6
yDWe2tKUg9cTwfgni4TutLOYkpz3VDPIQHs3k2mpNh7f+8X4RIybDqkCgYEAwZhp
uWMV38Bb0WytswHXL1dRuwBskKqALUBY61dtXkyBuocj8AuRRxfxfZpgJRrHFxDX
O9bQ2nxpVlwKsR6DJDUdxU3+kvwyPfseU5XUBey8WdkuAKD7cKZOHMhFVWccks0U
YJzykNrxB+rGTiwVKa0MOhipuJ7boerwwaN2SyUCgYBP9Ow5o4tq9q3EUNoksZ0k
zUuE+oxlCr/VlplKL9bM0HQMxlxoVWa59LTEfKyA4pvbUbAIfYtydlZ5oE5CdTUp
105tM4R88Jk2W1y5ooJ093OH29CKW/OXSvyi4hpIv592vRa0GOupoFRpBkDBhdWB
RcdnyMOmht+FIOwp8XkLiQKBgAUK3j4Y6ZnxXbLfvMp70soF4TgYs7s05a/IDEjc
9xlMrthX6sS22GrcocqeucBdqS/dnW2Ok9QNB4VbUl/4pnvL8mGQPYBAl2Jr5wdQ
ULxyxRkmAf+8MbBmdIRlZwDpdaIRO2Wk0OCbA0osgEvK9CYovrfIqqsHYDsgbnLs
ugkNAoGBAJok06BN05caPXXLQ2pMwI/7mjcZFjcOMxSloYi7LFkxlyvoTqReAeSa
yOb6W/7obS1X8ms/EAkqiyzJuPtNZJCW/nvV0iCoZ/NxLuyHnFaO344GBAweol+S
Jx0MY8KuDCyeGErc2xdz/yr3ld2PSTq71dhBluGyba2YX+peJ2Yv
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,82 @@
#
# Copyright (C) 2019 OpenWrt.org
#
# KFERMercer <KFER.Mercer@gmail.com>
#
# This is free software, licensed under the GNU General Public License v3.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=baidupcs-web
PKG_VERSION:=3.7.0
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=net
CATEGORY:=Network
TITLE:=BaiduPCS-Web is a web controller for BaiduPCS-Go
URL:=https://github.com/liuzhuoling2011/baidupcs-web
endef
define Package/$(PKG_NAME)/description
BaiduPCS-Web is a web controller for BaiduPCS-Go
endef
# 禁止OpenWrt对二进制文件进行strip
STRIP:=true
ifeq ($(ARCH),i386)
PKG_ARCH_BAIDUPCS-WEB:=86
endif
ifeq ($(ARCH),x86_64)
PKG_ARCH_BAIDUPCS-WEB:=amd64
endif
ifeq ($(ARCH),mipsel)
PKG_ARCH_BAIDUPCS-WEB:=mipsle
endif
ifeq ($(ARCH),mips)
PKG_ARCH_BAIDUPCS-WEB:=mipsle
endif
ifeq ($(ARCH),arm)
PKG_ARCH_BAIDUPCS-WEB:=arm
endif
ifeq ($(ARCH),aarch64)
PKG_ARCH_BAIDUPCS-WEB:=arm64
endif
PKG_SOURCE:=BaiduPCS-Go-$(PKG_VERSION)-linux-$(PKG_ARCH_BAIDUPCS-WEB).zip
PKG_SOURCE_URL:=https://github.com/liuzhuoling2011/baidupcs-web/releases/download/$(PKG_VERSION)/
UNZIP_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)/$(PKG_NAME)-unzip
PKG_HASH:=skip
define Build/Prepare
mkdir -vp $(UNZIP_DIR)
unzip -od $(UNZIP_DIR) $(DL_DIR)/$(PKG_SOURCE)
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(UNZIP_DIR)/*/BaiduPCS-Go $(1)/usr/bin/$(PKG_NAME)
chmod 755 $(1)/usr/bin/$(PKG_NAME)
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -0,0 +1,46 @@
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=tcp-bbr-bbrplus
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define KernelPackage/$(PKG_NAME)
SUBMENU:=Network Support
TITLE:=Modified bbr tcp congestion control
DEPENDS:=@LINUX_4_14
FILES:=$(PKG_BUILD_DIR)/tcp_bbr_bbrplus.ko
KCONFIG:=
endef
define KernelPackage/$(PKG_NAME)/description
Kernel module of modified BBR tcp congestion control
endef
EXTRA_KCONFIG:= \
EXTRA_CFLAGS:= \
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \
MAKE_OPTS:= \
ARCH="$(LINUX_KARCH)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
SUBDIRS="$(PKG_BUILD_DIR)" \
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
$(EXTRA_KCONFIG)
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) \
modules
endef
$(eval $(call KernelPackage,$(PKG_NAME)))

View File

@ -0,0 +1 @@
obj-m := tcp_bbr_bbrplus.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,47 @@
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=tcp-bbr-mod
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define KernelPackage/$(PKG_NAME)
SUBMENU:=Network Support
TITLE:=Modified bbr tcp congestion control
DEPENDS:=@LINUX_4_14
FILES:=$(PKG_BUILD_DIR)/tcp_bbr_mod.ko
AUTOLOAD:=$(call AutoLoad,99,tcp-bbr-mod)
KCONFIG:=
endef
define KernelPackage/$(PKG_NAME)/description
Kernel module of modified BBR tcp congestion control
endef
EXTRA_KCONFIG:= \
EXTRA_CFLAGS:= \
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \
MAKE_OPTS:= \
ARCH="$(LINUX_KARCH)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
SUBDIRS="$(PKG_BUILD_DIR)" \
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
$(EXTRA_KCONFIG)
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) \
modules
endef
$(eval $(call KernelPackage,$(PKG_NAME)))

View File

@ -0,0 +1 @@
obj-m := tcp_bbr_mod.o

View File

@ -0,0 +1,962 @@
/* Bottleneck Bandwidth and RTT (BBR) congestion control
*
* BBR congestion control computes the sending rate based on the delivery
* rate (throughput) estimated from ACKs. In a nutshell:
*
* On each ACK, update our model of the network path:
* bottleneck_bandwidth = windowed_max(delivered / elapsed, 10 round trips)
* min_rtt = windowed_min(rtt, 10 seconds)
* pacing_rate = pacing_gain * bottleneck_bandwidth
* cwnd = max(cwnd_gain * bottleneck_bandwidth * min_rtt, 4)
*
* The core algorithm does not react directly to packet losses or delays,
* although BBR may adjust the size of next send per ACK when loss is
* observed, or adjust the sending rate if it estimates there is a
* traffic policer, in order to keep the drop rate reasonable.
*
* Here is a state transition diagram for BBR:
*
* |
* V
* +---> STARTUP ----+
* | | |
* | V |
* | DRAIN ----+
* | | |
* | V |
* +---> PROBE_BW ----+
* | ^ | |
* | | | |
* | +----+ |
* | |
* +---- PROBE_RTT <--+
*
* A BBR flow starts in STARTUP, and ramps up its sending rate quickly.
* When it estimates the pipe is full, it enters DRAIN to drain the queue.
* In steady state a BBR flow only uses PROBE_BW and PROBE_RTT.
* A long-lived BBR flow spends the vast majority of its time remaining
* (repeatedly) in PROBE_BW, fully probing and utilizing the pipe's bandwidth
* in a fair manner, with a small, bounded queue. *If* a flow has been
* continuously sending for the entire min_rtt window, and hasn't seen an RTT
* sample that matches or decreases its min_rtt estimate for 10 seconds, then
* it briefly enters PROBE_RTT to cut inflight to a minimum value to re-probe
* the path's two-way propagation delay (min_rtt). When exiting PROBE_RTT, if
* we estimated that we reached the full bw of the pipe then we enter PROBE_BW;
* otherwise we enter STARTUP to try to fill the pipe.
*
* BBR is described in detail in:
* "BBR: Congestion-Based Congestion Control",
* Neal Cardwell, Yuchung Cheng, C. Stephen Gunn, Soheil Hassas Yeganeh,
* Van Jacobson. ACM Queue, Vol. 14 No. 5, September-October 2016.
*
* There is a public e-mail list for discussing BBR development and testing:
* https://groups.google.com/forum/#!forum/bbr-dev
*
* NOTE: BBR might be used with the fq qdisc ("man tc-fq") with pacing enabled,
* otherwise TCP stack falls back to an internal pacing using one high
* resolution timer per TCP socket and may use more resources.
*/
#include <linux/module.h>
#include <net/tcp.h>
#include <linux/inet_diag.h>
#include <linux/inet.h>
#include <linux/random.h>
#include <linux/win_minmax.h>
/* Scale factor for rate in pkt/uSec unit to avoid truncation in bandwidth
* estimation. The rate unit ~= (1500 bytes / 1 usec / 2^24) ~= 715 bps.
* This handles bandwidths from 0.06pps (715bps) to 256Mpps (3Tbps) in a u32.
* Since the minimum window is >=4 packets, the lower bound isn't
* an issue. The upper bound isn't an issue with existing technologies.
*/
#define BW_SCALE 24
#define BW_UNIT (1 << BW_SCALE)
#define BBR_SCALE 8 /* scaling factor for fractions in BBR (e.g. gains) */
#define BBR_UNIT (1 << BBR_SCALE)
/* BBR has the following modes for deciding how fast to send: */
enum bbr_mode {
BBR_STARTUP, /* ramp up sending rate rapidly to fill pipe */
BBR_DRAIN, /* drain any queue created during startup */
BBR_PROBE_BW, /* discover, share bw: pace around estimated bw */
BBR_PROBE_RTT, /* cut inflight to min to probe min_rtt */
};
/* BBR congestion control block */
struct bbr {
u32 min_rtt_us; /* min RTT in min_rtt_win_sec window */
u32 min_rtt_stamp; /* timestamp of min_rtt_us */
u32 probe_rtt_done_stamp; /* end time for BBR_PROBE_RTT mode */
struct minmax bw; /* Max recent delivery rate in pkts/uS << 24 */
u32 rtt_cnt; /* count of packet-timed rounds elapsed */
u32 next_rtt_delivered; /* scb->tx.delivered at end of round */
u64 cycle_mstamp; /* time of this cycle phase start */
u32 mode:3, /* current bbr_mode in state machine */
prev_ca_state:3, /* CA state on previous ACK */
packet_conservation:1, /* use packet conservation? */
restore_cwnd:1, /* decided to revert cwnd to old value */
round_start:1, /* start of packet-timed tx->ack round? */
tso_segs_goal:7, /* segments we want in each skb we send */
idle_restart:1, /* restarting after idle? */
probe_rtt_round_done:1, /* a BBR_PROBE_RTT round at 4 pkts? */
unused:5,
lt_is_sampling:1, /* taking long-term ("LT") samples now? */
lt_rtt_cnt:7, /* round trips in long-term interval */
lt_use_bw:1; /* use lt_bw as our bw estimate? */
u32 lt_bw; /* LT est delivery rate in pkts/uS << 24 */
u32 lt_last_delivered; /* LT intvl start: tp->delivered */
u32 lt_last_stamp; /* LT intvl start: tp->delivered_mstamp */
u32 lt_last_lost; /* LT intvl start: tp->lost */
u32 pacing_gain:10, /* current gain for setting pacing rate */
cwnd_gain:10, /* current gain for setting cwnd */
full_bw_reached:1, /* reached full bw in Startup? */
full_bw_cnt:2, /* number of rounds without large bw gains */
cycle_idx:3, /* current index in pacing_gain cycle array */
has_seen_rtt:1, /* have we seen an RTT sample yet? */
unused_b:5;
u32 prior_cwnd; /* prior cwnd upon entering loss recovery */
u32 full_bw; /* recent bw, to estimate if pipe is full */
};
#define CYCLE_LEN 8 /* number of phases in a pacing gain cycle */
/* Window length of bw filter (in rounds): */
static const int bbr_bw_rtts = CYCLE_LEN + 2;
/* Window length of min_rtt filter (in sec): */
static const u32 bbr_min_rtt_win_sec = 10;
/* Minimum time (in ms) spent at bbr_cwnd_min_target in BBR_PROBE_RTT mode: */
static const u32 bbr_probe_rtt_mode_ms = 100;
/* Skip TSO below the following bandwidth (bits/sec): */
static const int bbr_min_tso_rate = 1200000;
/* We use a high_gain value of 2/ln(2) because it's the smallest pacing gain
* that will allow a smoothly increasing pacing rate that will double each RTT
* and send the same number of packets per RTT that an un-paced, slow-starting
* Reno or CUBIC flow would:
*/
static const int bbr_high_gain = BBR_UNIT * 3000 / 1000 + 1;
/* The pacing gain of 1/high_gain in BBR_DRAIN is calculated to typically drain
* the queue created in BBR_STARTUP in a single round:
*/
static const int bbr_drain_gain = BBR_UNIT * 1000 / 3000;
/* The gain for deriving steady-state cwnd tolerates delayed/stretched ACKs: */
static const int bbr_cwnd_gain = BBR_UNIT * 2;
/* The pacing_gain values for the PROBE_BW gain cycle, to discover/share bw: */
static const int bbr_pacing_gain[] = {
BBR_UNIT * 6 / 4, /* probe for more available bw */
BBR_UNIT * 3 / 4, /* drain queue and/or yield bw to other flows */
BBR_UNIT * 5 / 4, BBR_UNIT * 5 / 4, BBR_UNIT * 5 / 4, /* cruise at 1.0*bw to utilize pipe, */
BBR_UNIT * 6 / 4, BBR_UNIT * 6 / 4, BBR_UNIT * 6 / 4 /* without creating excess queue... */
};
/* Randomize the starting gain cycling phase over N phases: */
static const u32 bbr_cycle_rand = 7;
/* Try to keep at least this many packets in flight, if things go smoothly. For
* smooth functioning, a sliding window protocol ACKing every other packet
* needs at least 4 packets in flight:
*/
static const u32 bbr_cwnd_min_target = 4;
/* To estimate if BBR_STARTUP mode (i.e. high_gain) has filled pipe... */
/* If bw has increased significantly (1.25x), there may be more bw available: */
static const u32 bbr_full_bw_thresh = BBR_UNIT * 5 / 4;
/* But after 3 rounds w/o significant bw growth, estimate pipe is full: */
static const u32 bbr_full_bw_cnt = 3;
/* "long-term" ("LT") bandwidth estimator parameters... */
/* The minimum number of rounds in an LT bw sampling interval: */
static const u32 bbr_lt_intvl_min_rtts = 4;
/* If lost/delivered ratio > 20%, interval is "lossy" and we may be policed: */
static const u32 bbr_lt_loss_thresh = 50;
/* If 2 intervals have a bw ratio <= 1/8, their bw is "consistent": */
static const u32 bbr_lt_bw_ratio = BBR_UNIT / 4;
/* If 2 intervals have a bw diff <= 4 Kbit/sec their bw is "consistent": */
static const u32 bbr_lt_bw_diff = 4000 / 4;
/* If we estimate we're policed, use lt_bw for this many round trips: */
static const u32 bbr_lt_bw_max_rtts = 48;
/* Do we estimate that STARTUP filled the pipe? */
static bool bbr_full_bw_reached(const struct sock *sk)
{
const struct bbr *bbr = inet_csk_ca(sk);
return bbr->full_bw_reached;
}
/* Return the windowed max recent bandwidth sample, in pkts/uS << BW_SCALE. */
static u32 bbr_max_bw(const struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
return minmax_get(&bbr->bw);
}
/* Return the estimated bandwidth of the path, in pkts/uS << BW_SCALE. */
static u32 bbr_bw(const struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
return bbr->lt_use_bw ? bbr->lt_bw : bbr_max_bw(sk);
}
/* Return rate in bytes per second, optionally with a gain.
* The order here is chosen carefully to avoid overflow of u64. This should
* work for input rates of up to 2.9Tbit/sec and gain of 2.89x.
*/
static u64 bbr_rate_bytes_per_sec(struct sock *sk, u64 rate, int gain)
{
rate *= tcp_mss_to_mtu(sk, tcp_sk(sk)->mss_cache);
rate *= gain;
rate >>= BBR_SCALE;
rate *= USEC_PER_SEC;
return rate >> BW_SCALE;
}
/* Convert a BBR bw and gain factor to a pacing rate in bytes per second. */
static u32 bbr_bw_to_pacing_rate(struct sock *sk, u32 bw, int gain)
{
u64 rate = bw;
rate = bbr_rate_bytes_per_sec(sk, rate, gain);
rate = min_t(u64, rate, sk->sk_max_pacing_rate);
return rate;
}
/* Initialize pacing rate to: high_gain * init_cwnd / RTT. */
static void bbr_init_pacing_rate_from_rtt(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u64 bw;
u32 rtt_us;
if (tp->srtt_us) { /* any RTT sample yet? */
rtt_us = max(tp->srtt_us >> 3, 1U);
bbr->has_seen_rtt = 1;
} else { /* no RTT sample yet */
rtt_us = USEC_PER_MSEC; /* use nominal default RTT */
}
bw = (u64)tp->snd_cwnd * BW_UNIT;
do_div(bw, rtt_us);
sk->sk_pacing_rate = bbr_bw_to_pacing_rate(sk, bw, bbr_high_gain);
}
/* Pace using current bw estimate and a gain factor. In order to help drive the
* network toward lower queues while maintaining high utilization and low
* latency, the average pacing rate aims to be slightly (~1%) lower than the
* estimated bandwidth. This is an important aspect of the design. In this
* implementation this slightly lower pacing rate is achieved implicitly by not
* including link-layer headers in the packet size used for the pacing rate.
*/
static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u32 rate = bbr_bw_to_pacing_rate(sk, bw, gain);
if (unlikely(!bbr->has_seen_rtt && tp->srtt_us))
bbr_init_pacing_rate_from_rtt(sk);
if (bbr_full_bw_reached(sk) || rate > sk->sk_pacing_rate)
sk->sk_pacing_rate = rate;
}
/* Return count of segments we want in the skbs we send, or 0 for default. */
static u32 bbr_tso_segs_goal(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
return bbr->tso_segs_goal;
}
static void bbr_set_tso_segs_goal(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u32 min_segs;
min_segs = sk->sk_pacing_rate < (bbr_min_tso_rate >> 3) ? 1 : 2;
bbr->tso_segs_goal = min(tcp_tso_autosize(sk, tp->mss_cache, min_segs),
0x7FU);
}
/* Save "last known good" cwnd so we can restore it after losses or PROBE_RTT */
static void bbr_save_cwnd(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
if (bbr->prev_ca_state < TCP_CA_Recovery && bbr->mode != BBR_PROBE_RTT)
bbr->prior_cwnd = tp->snd_cwnd; /* this cwnd is good enough */
else /* loss recovery or BBR_PROBE_RTT have temporarily cut cwnd */
bbr->prior_cwnd = max(bbr->prior_cwnd, tp->snd_cwnd);
}
static void bbr_cwnd_event(struct sock *sk, enum tcp_ca_event event)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
if (event == CA_EVENT_TX_START && tp->app_limited) {
bbr->idle_restart = 1;
/* Avoid pointless buffer overflows: pace at est. bw if we don't
* need more speed (we're restarting from idle and app-limited).
*/
if (bbr->mode == BBR_PROBE_BW)
bbr_set_pacing_rate(sk, bbr_bw(sk), BBR_UNIT);
}
}
/* Find target cwnd. Right-size the cwnd based on min RTT and the
* estimated bottleneck bandwidth:
*
* cwnd = bw * min_rtt * gain = BDP * gain
*
* The key factor, gain, controls the amount of queue. While a small gain
* builds a smaller queue, it becomes more vulnerable to noise in RTT
* measurements (e.g., delayed ACKs or other ACK compression effects). This
* noise may cause BBR to under-estimate the rate.
*
* To achieve full performance in high-speed paths, we budget enough cwnd to
* fit full-sized skbs in-flight on both end hosts to fully utilize the path:
* - one skb in sending host Qdisc,
* - one skb in sending host TSO/GSO engine
* - one skb being received by receiver host LRO/GRO/delayed-ACK engine
* Don't worry, at low rates (bbr_min_tso_rate) this won't bloat cwnd because
* in such cases tso_segs_goal is 1. The minimum cwnd is 4 packets,
* which allows 2 outstanding 2-packet sequences, to try to keep pipe
* full even with ACK-every-other-packet delayed ACKs.
*/
static u32 bbr_target_cwnd(struct sock *sk, u32 bw, int gain)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 cwnd;
u64 w;
/* If we've never had a valid RTT sample, cap cwnd at the initial
* default. This should only happen when the connection is not using TCP
* timestamps and has retransmitted all of the SYN/SYNACK/data packets
* ACKed so far. In this case, an RTO can cut cwnd to 1, in which
* case we need to slow-start up toward something safe: TCP_INIT_CWND.
*/
if (unlikely(bbr->min_rtt_us == ~0U)) /* no valid RTT samples yet? */
return TCP_INIT_CWND; /* be safe: cap at default initial cwnd*/
w = (u64)bw * bbr->min_rtt_us;
/* Apply a gain to the given value, then remove the BW_SCALE shift. */
cwnd = (((w * gain) >> BBR_SCALE) + BW_UNIT - 1) / BW_UNIT;
/* Allow enough full-sized skbs in flight to utilize end systems. */
cwnd += 3 * bbr->tso_segs_goal;
/* Reduce delayed ACKs by rounding up cwnd to the next even number. */
cwnd = (cwnd + 1) & ~1U;
return cwnd;
}
/* An optimization in BBR to reduce losses: On the first round of recovery, we
* follow the packet conservation principle: send P packets per P packets acked.
* After that, we slow-start and send at most 2*P packets per P packets acked.
* After recovery finishes, or upon undo, we restore the cwnd we had when
* recovery started (capped by the target cwnd based on estimated BDP).
*
* TODO(ycheng/ncardwell): implement a rate-based approach.
*/
static bool bbr_set_cwnd_to_recover_or_restore(
struct sock *sk, const struct rate_sample *rs, u32 acked, u32 *new_cwnd)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u8 prev_state = bbr->prev_ca_state, state = inet_csk(sk)->icsk_ca_state;
u32 cwnd = tp->snd_cwnd;
/* An ACK for P pkts should release at most 2*P packets. We do this
* in two steps. First, here we deduct the number of lost packets.
* Then, in bbr_set_cwnd() we slow start up toward the target cwnd.
*/
if (rs->losses > 0)
cwnd = max_t(s32, cwnd - rs->losses, 1);
if (state == TCP_CA_Recovery && prev_state != TCP_CA_Recovery) {
/* Starting 1st round of Recovery, so do packet conservation. */
bbr->packet_conservation = 1;
bbr->next_rtt_delivered = tp->delivered; /* start round now */
/* Cut unused cwnd from app behavior, TSQ, or TSO deferral: */
cwnd = tcp_packets_in_flight(tp) + acked;
} else if (prev_state >= TCP_CA_Recovery && state < TCP_CA_Recovery) {
/* Exiting loss recovery; restore cwnd saved before recovery. */
bbr->restore_cwnd = 1;
bbr->packet_conservation = 0;
}
bbr->prev_ca_state = state;
if (bbr->restore_cwnd) {
/* Restore cwnd after exiting loss recovery or PROBE_RTT. */
cwnd = max(cwnd, bbr->prior_cwnd);
bbr->restore_cwnd = 0;
}
if (bbr->packet_conservation) {
*new_cwnd = max(cwnd, tcp_packets_in_flight(tp) + acked);
return true; /* yes, using packet conservation */
}
*new_cwnd = cwnd;
return false;
}
/* Slow-start up toward target cwnd (if bw estimate is growing, or packet loss
* has drawn us down below target), or snap down to target if we're above it.
*/
static void bbr_set_cwnd(struct sock *sk, const struct rate_sample *rs,
u32 acked, u32 bw, int gain)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u32 cwnd = 0, target_cwnd = 0;
if (!acked)
return;
if (bbr_set_cwnd_to_recover_or_restore(sk, rs, acked, &cwnd))
goto done;
/* If we're below target cwnd, slow start cwnd toward target cwnd. */
target_cwnd = bbr_target_cwnd(sk, bw, gain);
if (bbr_full_bw_reached(sk)) /* only cut cwnd if we filled the pipe */
cwnd = min(cwnd + acked, target_cwnd);
else if (cwnd < target_cwnd || tp->delivered < TCP_INIT_CWND)
cwnd = cwnd + acked;
cwnd = max(cwnd, bbr_cwnd_min_target);
done:
tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); /* apply global cap */
if (bbr->mode == BBR_PROBE_RTT) /* drain queue, refresh min_rtt */
tp->snd_cwnd = min(tp->snd_cwnd, bbr_cwnd_min_target);
}
/* End cycle phase if it's time and/or we hit the phase's in-flight target. */
static bool bbr_is_next_cycle_phase(struct sock *sk,
const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bool is_full_length =
tcp_stamp_us_delta(tp->delivered_mstamp, bbr->cycle_mstamp) >
bbr->min_rtt_us;
u32 inflight, bw;
/* The pacing_gain of 1.0 paces at the estimated bw to try to fully
* use the pipe without increasing the queue.
*/
if (bbr->pacing_gain == BBR_UNIT)
return is_full_length; /* just use wall clock time */
inflight = rs->prior_in_flight; /* what was in-flight before ACK? */
bw = bbr_max_bw(sk);
/* A pacing_gain > 1.0 probes for bw by trying to raise inflight to at
* least pacing_gain*BDP; this may take more than min_rtt if min_rtt is
* small (e.g. on a LAN). We do not persist if packets are lost, since
* a path with small buffers may not hold that much.
*/
if (bbr->pacing_gain > BBR_UNIT)
return is_full_length &&
(rs->losses || /* perhaps pacing_gain*BDP won't fit */
inflight >= bbr_target_cwnd(sk, bw, bbr->pacing_gain));
/* A pacing_gain < 1.0 tries to drain extra queue we added if bw
* probing didn't find more bw. If inflight falls to match BDP then we
* estimate queue is drained; persisting would underutilize the pipe.
*/
return is_full_length ||
inflight <= bbr_target_cwnd(sk, bw, BBR_UNIT);
}
static void bbr_advance_cycle_phase(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1);
bbr->cycle_mstamp = tp->delivered_mstamp;
bbr->pacing_gain = bbr->lt_use_bw ? BBR_UNIT :
bbr_pacing_gain[bbr->cycle_idx];
}
/* Gain cycling: cycle pacing gain to converge to fair share of available bw. */
static void bbr_update_cycle_phase(struct sock *sk,
const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
if (bbr->mode == BBR_PROBE_BW && bbr_is_next_cycle_phase(sk, rs))
bbr_advance_cycle_phase(sk);
}
static void bbr_reset_startup_mode(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
bbr->mode = BBR_STARTUP;
bbr->pacing_gain = bbr_high_gain;
bbr->cwnd_gain = bbr_high_gain;
}
static void bbr_reset_probe_bw_mode(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
bbr->mode = BBR_PROBE_BW;
bbr->pacing_gain = BBR_UNIT;
bbr->cwnd_gain = bbr_cwnd_gain;
bbr->cycle_idx = CYCLE_LEN - 1 - prandom_u32_max(bbr_cycle_rand);
bbr_advance_cycle_phase(sk); /* flip to next phase of gain cycle */
}
static void bbr_reset_mode(struct sock *sk)
{
if (!bbr_full_bw_reached(sk))
bbr_reset_startup_mode(sk);
else
bbr_reset_probe_bw_mode(sk);
}
/* Start a new long-term sampling interval. */
static void bbr_reset_lt_bw_sampling_interval(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bbr->lt_last_stamp = div_u64(tp->delivered_mstamp, USEC_PER_MSEC);
bbr->lt_last_delivered = tp->delivered;
bbr->lt_last_lost = tp->lost;
bbr->lt_rtt_cnt = 0;
}
/* Completely reset long-term bandwidth sampling. */
static void bbr_reset_lt_bw_sampling(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
bbr->lt_bw = 0;
bbr->lt_use_bw = 0;
bbr->lt_is_sampling = false;
bbr_reset_lt_bw_sampling_interval(sk);
}
/* Long-term bw sampling interval is done. Estimate whether we're policed. */
static void bbr_lt_bw_interval_done(struct sock *sk, u32 bw)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 diff;
if (bbr->lt_bw) { /* do we have bw from a previous interval? */
/* Is new bw close to the lt_bw from the previous interval? */
diff = abs(bw - bbr->lt_bw);
if ((diff * BBR_UNIT <= bbr_lt_bw_ratio * bbr->lt_bw) ||
(bbr_rate_bytes_per_sec(sk, diff, BBR_UNIT) <=
bbr_lt_bw_diff)) {
/* All criteria are met; estimate we're policed. */
bbr->lt_bw = (bw + bbr->lt_bw) >> 1; /* avg 2 intvls */
bbr->lt_use_bw = 1;
bbr->pacing_gain = BBR_UNIT; /* try to avoid drops */
bbr->lt_rtt_cnt = 0;
return;
}
}
bbr->lt_bw = bw;
bbr_reset_lt_bw_sampling_interval(sk);
}
/* Token-bucket traffic policers are common (see "An Internet-Wide Analysis of
* Traffic Policing", SIGCOMM 2016). BBR detects token-bucket policers and
* explicitly models their policed rate, to reduce unnecessary losses. We
* estimate that we're policed if we see 2 consecutive sampling intervals with
* consistent throughput and high packet loss. If we think we're being policed,
* set lt_bw to the "long-term" average delivery rate from those 2 intervals.
*/
static void bbr_lt_bw_sampling(struct sock *sk, const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u32 lost, delivered;
u64 bw;
u32 t;
if (bbr->lt_use_bw) { /* already using long-term rate, lt_bw? */
if (bbr->mode == BBR_PROBE_BW && bbr->round_start &&
++bbr->lt_rtt_cnt >= bbr_lt_bw_max_rtts) {
bbr_reset_lt_bw_sampling(sk); /* stop using lt_bw */
bbr_reset_probe_bw_mode(sk); /* restart gain cycling */
}
return;
}
/* Wait for the first loss before sampling, to let the policer exhaust
* its tokens and estimate the steady-state rate allowed by the policer.
* Starting samples earlier includes bursts that over-estimate the bw.
*/
if (!bbr->lt_is_sampling) {
if (!rs->losses)
return;
bbr_reset_lt_bw_sampling_interval(sk);
bbr->lt_is_sampling = true;
}
/* To avoid underestimates, reset sampling if we run out of data. */
if (rs->is_app_limited) {
bbr_reset_lt_bw_sampling(sk);
return;
}
if (bbr->round_start)
bbr->lt_rtt_cnt++; /* count round trips in this interval */
if (bbr->lt_rtt_cnt < bbr_lt_intvl_min_rtts)
return; /* sampling interval needs to be longer */
if (bbr->lt_rtt_cnt > 4 * bbr_lt_intvl_min_rtts) {
bbr_reset_lt_bw_sampling(sk); /* interval is too long */
return;
}
/* End sampling interval when a packet is lost, so we estimate the
* policer tokens were exhausted. Stopping the sampling before the
* tokens are exhausted under-estimates the policed rate.
*/
if (!rs->losses)
return;
/* Calculate packets lost and delivered in sampling interval. */
lost = tp->lost - bbr->lt_last_lost;
delivered = tp->delivered - bbr->lt_last_delivered;
/* Is loss rate (lost/delivered) >= lt_loss_thresh? If not, wait. */
if (!delivered || (lost << BBR_SCALE) < bbr_lt_loss_thresh * delivered)
return;
/* Find average delivery rate in this sampling interval. */
t = div_u64(tp->delivered_mstamp, USEC_PER_MSEC) - bbr->lt_last_stamp;
if ((s32)t < 1)
return; /* interval is less than one ms, so wait */
/* Check if can multiply without overflow */
if (t >= ~0U / USEC_PER_MSEC) {
bbr_reset_lt_bw_sampling(sk); /* interval too long; reset */
return;
}
t *= USEC_PER_MSEC;
bw = (u64)delivered * BW_UNIT;
do_div(bw, t);
bbr_lt_bw_interval_done(sk, bw);
}
/* Estimate the bandwidth based on how fast packets are delivered */
static void bbr_update_bw(struct sock *sk, const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u64 bw;
bbr->round_start = 0;
if (rs->delivered < 0 || rs->interval_us <= 0)
return; /* Not a valid observation */
/* See if we've reached the next RTT */
if (!before(rs->prior_delivered, bbr->next_rtt_delivered)) {
bbr->next_rtt_delivered = tp->delivered;
bbr->rtt_cnt++;
bbr->round_start = 1;
bbr->packet_conservation = 0;
}
bbr_lt_bw_sampling(sk, rs);
/* Divide delivered by the interval to find a (lower bound) bottleneck
* bandwidth sample. Delivered is in packets and interval_us in uS and
* ratio will be <<1 for most connections. So delivered is first scaled.
*/
bw = (u64)rs->delivered * BW_UNIT;
do_div(bw, rs->interval_us);
/* If this sample is application-limited, it is likely to have a very
* low delivered count that represents application behavior rather than
* the available network rate. Such a sample could drag down estimated
* bw, causing needless slow-down. Thus, to continue to send at the
* last measured network rate, we filter out app-limited samples unless
* they describe the path bw at least as well as our bw model.
*
* So the goal during app-limited phase is to proceed with the best
* network rate no matter how long. We automatically leave this
* phase when app writes faster than the network can deliver :)
*/
if (!rs->is_app_limited || bw >= bbr_max_bw(sk)) {
/* Incorporate new sample into our max bw filter. */
minmax_running_max(&bbr->bw, bbr_bw_rtts, bbr->rtt_cnt, bw);
}
}
/* Estimate when the pipe is full, using the change in delivery rate: BBR
* estimates that STARTUP filled the pipe if the estimated bw hasn't changed by
* at least bbr_full_bw_thresh (25%) after bbr_full_bw_cnt (3) non-app-limited
* rounds. Why 3 rounds: 1: rwin autotuning grows the rwin, 2: we fill the
* higher rwin, 3: we get higher delivery rate samples. Or transient
* cross-traffic or radio noise can go away. CUBIC Hystart shares a similar
* design goal, but uses delay and inter-ACK spacing instead of bandwidth.
*/
static void bbr_check_full_bw_reached(struct sock *sk,
const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 bw_thresh;
if (bbr_full_bw_reached(sk) || !bbr->round_start || rs->is_app_limited)
return;
bw_thresh = (u64)bbr->full_bw * bbr_full_bw_thresh >> BBR_SCALE;
if (bbr_max_bw(sk) >= bw_thresh) {
bbr->full_bw = bbr_max_bw(sk);
bbr->full_bw_cnt = 0;
return;
}
++bbr->full_bw_cnt;
bbr->full_bw_reached = bbr->full_bw_cnt >= bbr_full_bw_cnt;
}
/* If pipe is probably full, drain the queue and then enter steady-state. */
static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
if (bbr->mode == BBR_STARTUP && bbr_full_bw_reached(sk)) {
bbr->mode = BBR_DRAIN; /* drain queue we created */
bbr->pacing_gain = bbr_drain_gain; /* pace slow to drain */
bbr->cwnd_gain = bbr_high_gain; /* maintain cwnd */
} /* fall through to check if in-flight is already small: */
if (bbr->mode == BBR_DRAIN &&
tcp_packets_in_flight(tcp_sk(sk)) <=
bbr_target_cwnd(sk, bbr_max_bw(sk), BBR_UNIT))
bbr_reset_probe_bw_mode(sk); /* we estimate queue is drained */
}
/* The goal of PROBE_RTT mode is to have BBR flows cooperatively and
* periodically drain the bottleneck queue, to converge to measure the true
* min_rtt (unloaded propagation delay). This allows the flows to keep queues
* small (reducing queuing delay and packet loss) and achieve fairness among
* BBR flows.
*
* The min_rtt filter window is 10 seconds. When the min_rtt estimate expires,
* we enter PROBE_RTT mode and cap the cwnd at bbr_cwnd_min_target=4 packets.
* After at least bbr_probe_rtt_mode_ms=200ms and at least one packet-timed
* round trip elapsed with that flight size <= 4, we leave PROBE_RTT mode and
* re-enter the previous mode. BBR uses 200ms to approximately bound the
* performance penalty of PROBE_RTT's cwnd capping to roughly 2% (200ms/10s).
*
* Note that flows need only pay 2% if they are busy sending over the last 10
* seconds. Interactive applications (e.g., Web, RPCs, video chunks) often have
* natural silences or low-rate periods within 10 seconds where the rate is low
* enough for long enough to drain its queue in the bottleneck. We pick up
* these min RTT measurements opportunistically with our min_rtt filter. :-)
*/
static void bbr_update_min_rtt(struct sock *sk, const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bool filter_expired;
/* Track min RTT seen in the min_rtt_win_sec filter window: */
filter_expired = after(tcp_jiffies32,
bbr->min_rtt_stamp + bbr_min_rtt_win_sec * HZ);
if (rs->rtt_us >= 0 &&
(rs->rtt_us <= bbr->min_rtt_us || filter_expired)) {
bbr->min_rtt_us = rs->rtt_us;
bbr->min_rtt_stamp = tcp_jiffies32;
}
if (bbr_probe_rtt_mode_ms > 0 && filter_expired &&
!bbr->idle_restart && bbr->mode != BBR_PROBE_RTT) {
bbr->mode = BBR_PROBE_RTT; /* dip, drain queue */
bbr->pacing_gain = BBR_UNIT;
bbr->cwnd_gain = BBR_UNIT;
bbr_save_cwnd(sk); /* note cwnd so we can restore it */
bbr->probe_rtt_done_stamp = 0;
}
if (bbr->mode == BBR_PROBE_RTT) {
/* Ignore low rate samples during this mode. */
tp->app_limited =
(tp->delivered + tcp_packets_in_flight(tp)) ? : 1;
/* Maintain min packets in flight for max(200 ms, 1 round). */
if (!bbr->probe_rtt_done_stamp &&
tcp_packets_in_flight(tp) <= bbr_cwnd_min_target) {
bbr->probe_rtt_done_stamp = tcp_jiffies32 +
msecs_to_jiffies(bbr_probe_rtt_mode_ms);
bbr->probe_rtt_round_done = 0;
bbr->next_rtt_delivered = tp->delivered;
} else if (bbr->probe_rtt_done_stamp) {
if (bbr->round_start)
bbr->probe_rtt_round_done = 1;
if (bbr->probe_rtt_round_done &&
after(tcp_jiffies32, bbr->probe_rtt_done_stamp)) {
bbr->min_rtt_stamp = tcp_jiffies32;
bbr->restore_cwnd = 1; /* snap to prior_cwnd */
bbr_reset_mode(sk);
}
}
}
bbr->idle_restart = 0;
}
static void bbr_update_model(struct sock *sk, const struct rate_sample *rs)
{
bbr_update_bw(sk, rs);
bbr_update_cycle_phase(sk, rs);
bbr_check_full_bw_reached(sk, rs);
bbr_check_drain(sk, rs);
bbr_update_min_rtt(sk, rs);
}
static void bbr_main(struct sock *sk, const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 bw;
bbr_update_model(sk, rs);
bw = bbr_bw(sk);
bbr_set_pacing_rate(sk, bw, bbr->pacing_gain);
bbr_set_tso_segs_goal(sk);
bbr_set_cwnd(sk, rs, rs->acked_sacked, bw, bbr->cwnd_gain);
}
static void bbr_init(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bbr->prior_cwnd = 0;
bbr->tso_segs_goal = 0; /* default segs per skb until first ACK */
bbr->rtt_cnt = 0;
bbr->next_rtt_delivered = 0;
bbr->prev_ca_state = TCP_CA_Open;
bbr->packet_conservation = 0;
bbr->probe_rtt_done_stamp = 0;
bbr->probe_rtt_round_done = 0;
bbr->min_rtt_us = tcp_min_rtt(tp);
bbr->min_rtt_stamp = tcp_jiffies32;
minmax_reset(&bbr->bw, bbr->rtt_cnt, 0); /* init max bw to 0 */
bbr->has_seen_rtt = 0;
bbr_init_pacing_rate_from_rtt(sk);
bbr->restore_cwnd = 0;
bbr->round_start = 0;
bbr->idle_restart = 0;
bbr->full_bw_reached = 0;
bbr->full_bw = 0;
bbr->full_bw_cnt = 0;
bbr->cycle_mstamp = 0;
bbr->cycle_idx = 0;
bbr_reset_lt_bw_sampling(sk);
bbr_reset_startup_mode(sk);
cmpxchg(&sk->sk_pacing_status, SK_PACING_NONE, SK_PACING_NEEDED);
}
static u32 bbr_sndbuf_expand(struct sock *sk)
{
/* Provision 3 * cwnd since BBR may slow-start even during recovery. */
return 3;
}
/* In theory BBR does not need to undo the cwnd since it does not
* always reduce cwnd on losses (see bbr_main()). Keep it for now.
*/
static u32 bbr_undo_cwnd(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
bbr->full_bw = 0; /* spurious slow-down; reset full pipe detection */
bbr->full_bw_cnt = 0;
bbr_reset_lt_bw_sampling(sk);
return tcp_sk(sk)->snd_cwnd;
}
/* Entering loss recovery, so save cwnd for when we exit or undo recovery. */
static u32 bbr_ssthresh(struct sock *sk)
{
bbr_save_cwnd(sk);
return TCP_INFINITE_SSTHRESH; /* BBR does not use ssthresh */
}
static size_t bbr_get_info(struct sock *sk, u32 ext, int *attr,
union tcp_cc_info *info)
{
if (ext & (1 << (INET_DIAG_BBRINFO - 1)) ||
ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u64 bw = bbr_bw(sk);
bw = bw * tp->mss_cache * USEC_PER_SEC >> BW_SCALE;
memset(&info->bbr, 0, sizeof(info->bbr));
info->bbr.bbr_bw_lo = (u32)bw;
info->bbr.bbr_bw_hi = (u32)(bw >> 32);
info->bbr.bbr_min_rtt = bbr->min_rtt_us;
info->bbr.bbr_pacing_gain = bbr->pacing_gain;
info->bbr.bbr_cwnd_gain = bbr->cwnd_gain;
*attr = INET_DIAG_BBRINFO;
return sizeof(info->bbr);
}
return 0;
}
static void bbr_set_state(struct sock *sk, u8 new_state)
{
struct bbr *bbr = inet_csk_ca(sk);
if (new_state == TCP_CA_Loss) {
struct rate_sample rs = { .losses = 1 };
bbr->prev_ca_state = TCP_CA_Loss;
bbr->full_bw = 0;
bbr->round_start = 1; /* treat RTO like end of a round */
bbr_lt_bw_sampling(sk, &rs);
}
}
static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = {
.flags = TCP_CONG_NON_RESTRICTED,
.name = "tcp_bbr_mod",
.owner = THIS_MODULE,
.init = bbr_init,
.cong_control = bbr_main,
.sndbuf_expand = bbr_sndbuf_expand,
.undo_cwnd = bbr_undo_cwnd,
.cwnd_event = bbr_cwnd_event,
.ssthresh = bbr_ssthresh,
.tso_segs_goal = bbr_tso_segs_goal,
.get_info = bbr_get_info,
.set_state = bbr_set_state,
};
static int __init bbr_register(void)
{
BUILD_BUG_ON(sizeof(struct bbr) > ICSK_CA_PRIV_SIZE);
return tcp_register_congestion_control(&tcp_bbr_cong_ops);
}
static void __exit bbr_unregister(void)
{
tcp_unregister_congestion_control(&tcp_bbr_cong_ops);
}
module_init(bbr_register);
module_exit(bbr_unregister);
MODULE_AUTHOR("Van Jacobson <vanj@google.com>");
MODULE_AUTHOR("Neal Cardwell <ncardwell@google.com>");
MODULE_AUTHOR("Yuchung Cheng <ycheng@google.com>");
MODULE_AUTHOR("Soheil Hassas Yeganeh <soheil@google.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("TCP BBR (Bottleneck Bandwidth and RTT)");

View File

@ -0,0 +1,47 @@
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=tcp-bbr-nanqinlang
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define KernelPackage/$(PKG_NAME)
SUBMENU:=Network Support
TITLE:=Modified bbr tcp congestion control
DEPENDS:=@LINUX_4_14
FILES:=$(PKG_BUILD_DIR)/tcp_bbr_nanqinlang.ko
AUTOLOAD:=$(call AutoLoad,99,tcp-bbr-nanqinlang)
KCONFIG:=
endef
define KernelPackage/$(PKG_NAME)/description
Kernel module of modified BBR tcp congestion control
endef
EXTRA_KCONFIG:= \
EXTRA_CFLAGS:= \
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \
MAKE_OPTS:= \
ARCH="$(LINUX_KARCH)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
SUBDIRS="$(PKG_BUILD_DIR)" \
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
$(EXTRA_KCONFIG)
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) \
modules
endef
$(eval $(call KernelPackage,$(PKG_NAME)))

View File

@ -0,0 +1 @@
obj-m := tcp_bbr_nanqinlang.o

View File

@ -0,0 +1,968 @@
/* tcp_nanqinlang
* Debian
* kernel v4.14
× New BBR Congestion Control
* Modified by (C) 2017 nanqinlang
*******************************************************************************
Bottleneck Bandwidth and RTT (BBR) congestion control
*
* BBR congestion control computes the sending rate based on the delivery
* rate (throughput) estimated from ACKs. In a nutshell:
*
* On each ACK, update our model of the network path:
* bottleneck_bandwidth = windowed_max(delivered / elapsed, 10 round trips)
* min_rtt = windowed_min(rtt, 10 seconds)
* pacing_rate = pacing_gain * bottleneck_bandwidth
* cwnd = max(cwnd_gain * bottleneck_bandwidth * min_rtt, 4)
*
* The core algorithm does not react directly to packet losses or delays,
* although BBR may adjust the size of next send per ACK when loss is
* observed, or adjust the sending rate if it estimates there is a
* traffic policer, in order to keep the drop rate reasonable.
*
* Here is a state transition diagram for BBR:
*
* |
* V
* +---> STARTUP ----+
* | | |
* | V |
* | DRAIN ----+
* | | |
* | V |
* +---> PROBE_BW ----+
* | ^ | |
* | | | |
* | +----+ |
* | |
* +---- PROBE_RTT <--+
*
* A BBR flow starts in STARTUP, and ramps up its sending rate quickly.
* When it estimates the pipe is full, it enters DRAIN to drain the queue.
* In steady state a BBR flow only uses PROBE_BW and PROBE_RTT.
* A long-lived BBR flow spends the vast majority of its time remaining
* (repeatedly) in PROBE_BW, fully probing and utilizing the pipe's bandwidth
* in a fair manner, with a small, bounded queue. *If* a flow has been
* continuously sending for the entire min_rtt window, and hasn't seen an RTT
* sample that matches or decreases its min_rtt estimate for 10 seconds, then
* it briefly enters PROBE_RTT to cut inflight to a minimum value to re-probe
* the path's two-way propagation delay (min_rtt). When exiting PROBE_RTT, if
* we estimated that we reached the full bw of the pipe then we enter PROBE_BW;
* otherwise we enter STARTUP to try to fill the pipe.
*
* BBR is described in detail in:
* "BBR: Congestion-Based Congestion Control",
* Neal Cardwell, Yuchung Cheng, C. Stephen Gunn, Soheil Hassas Yeganeh,
* Van Jacobson. ACM Queue, Vol. 14 No. 5, September-October 2016.
*
* There is a public e-mail list for discussing BBR development and testing:
* https://groups.google.com/forum/#!forum/bbr-dev
*
* NOTE: BBR might be used with the fq qdisc ("man tc-fq") with pacing enabled,
* otherwise TCP stack falls back to an internal pacing using one high
* resolution timer per TCP socket and may use more resources.
*/
#include <linux/module.h>
#include <linux/inet.h>
#include <linux/inet_diag.h>
#include <linux/random.h>
#include <linux/win_minmax.h>
#include <net/tcp.h>
/* Scale factor for rate in pkt/uSec unit to avoid truncation in bandwidth
* estimation. The rate unit ~= (1500 bytes / 1 usec / 2^24) ~= 715 bps.
* This handles bandwidths from 0.06pps (715bps) to 256Mpps (3Tbps) in a u32.
* Since the minimum window is >=4 packets, the lower bound isn't
* an issue. The upper bound isn't an issue with existing technologies.
*/
#define BW_SCALE 24
#define BW_UNIT (1 << BW_SCALE)
#define BBR_SCALE 8 /* scaling factor for fractions in BBR (e.g. gains) */
#define BBR_UNIT (1 << BBR_SCALE)
#define CYCLE_LEN 8 /* number of phases in a pacing gain cycle */
/* BBR has the following modes for deciding how fast to send: */
enum bbr_mode {
BBR_STARTUP, /* ramp up sending rate rapidly to fill pipe */
BBR_DRAIN, /* drain any queue created during startup */
BBR_PROBE_BW, /* discover, share bw: pace around estimated bw */
BBR_PROBE_RTT, /* cut inflight to min to probe min_rtt */
};
/* BBR congestion control block */
struct bbr {
u32 min_rtt_us; /* min RTT in min_rtt_win_sec window */
u32 min_rtt_stamp; /* timestamp of min_rtt_us */
u32 probe_rtt_done_stamp; /* end time for BBR_PROBE_RTT mode */
struct minmax bw; /* Max recent delivery rate in pkts/uS << 24 */
u32 rtt_cnt; /* count of packet-timed rounds elapsed */
u32 next_rtt_delivered; /* scb->tx.delivered at end of round */
u64 cycle_mstamp; /* time of this cycle phase start */
u32 mode:3, /* current bbr_mode in state machine */
prev_ca_state:3, /* CA state on previous ACK */
packet_conservation:1, /* use packet conservation? */
restore_cwnd:1, /* decided to revert cwnd to old value */
round_start:1, /* start of packet-timed tx->ack round? */
tso_segs_goal:7, /* segments we want in each skb we send */
idle_restart:1, /* restarting after idle? */
probe_rtt_round_done:1, /* a BBR_PROBE_RTT round at 4 pkts? */
unused:5,
lt_is_sampling:1, /* taking long-term ("LT") samples now? */
lt_rtt_cnt:7, /* round trips in long-term interval */
lt_use_bw:1; /* use lt_bw as our bw estimate? */
u32 lt_bw; /* LT est delivery rate in pkts/uS << 24 */
u32 lt_last_delivered; /* LT intvl start: tp->delivered */
u32 lt_last_stamp; /* LT intvl start: tp->delivered_mstamp */
u32 lt_last_lost; /* LT intvl start: tp->lost */
u32 pacing_gain:10, /* current gain for setting pacing rate */
cwnd_gain:10, /* current gain for setting cwnd */
full_bw_cnt:3, /* number of rounds without large bw gains */
cycle_idx:3, /* current index in pacing_gain cycle array */
has_seen_rtt:1, /* have we seen an RTT sample yet? */
unused_b:5;
u32 prior_cwnd; /* prior cwnd upon entering loss recovery */
u32 full_bw; /* recent bw, to estimate if pipe is full */
};
/* Window length of bw filter (in rounds): */
static const int bbr_bw_rtts = CYCLE_LEN + 2;
/* Window length of min_rtt filter (in sec): */
static const u32 bbr_min_rtt_win_sec = 10;
/* Minimum time (in ms) spent at bbr_cwnd_min_target in BBR_PROBE_RTT mode: */
static const u32 bbr_probe_rtt_mode_ms = 100;
/* Skip TSO below the following bandwidth (bits/sec): */
static const int bbr_min_tso_rate = 1200000;
/* We use a high_gain value of 2/ln(2) because it's the smallest pacing gain
* that will allow a smoothly increasing pacing rate that will double each RTT
* and send the same number of packets per RTT that an un-paced, slow-starting
* Reno or CUBIC flow would:
*/
static const int bbr_high_gain = BBR_UNIT * 3000 / 1000 + 1;
/* The pacing gain of 1/high_gain in BBR_DRAIN is calculated to typically drain
* the queue created in BBR_STARTUP in a single round:
*/
static const int bbr_drain_gain = BBR_UNIT * 1000 / 3000;
/* The gain for deriving steady-state cwnd tolerates delayed/stretched ACKs: */
static const int bbr_cwnd_gain = BBR_UNIT * 2;
/* The pacing_gain values for the PROBE_BW gain cycle, to discover/share bw: */
static const int bbr_pacing_gain[] = {
BBR_UNIT * 6 / 4, /* probe for more available bw */
BBR_UNIT * 3 / 4, /* drain queue and/or yield bw to other flows */
BBR_UNIT * 5 / 4, BBR_UNIT * 5 / 4, BBR_UNIT * 5 / 4, /* cruise at 1.0*bw to utilize pipe, */
BBR_UNIT * 6 / 4, BBR_UNIT * 6 / 4, BBR_UNIT * 6 / 4 /* without creating excess queue... */
};
/* Randomize the starting gain cycling phase over N phases: */
static const u32 bbr_cycle_rand = 7;
/* Try to keep at least this many packets in flight, if things go smoothly. For
* smooth functioning, a sliding window protocol ACKing every other packet
* needs at least 4 packets in flight:
*/
static const u32 bbr_cwnd_min_target = 4;
/* To estimate if BBR_STARTUP mode (i.e. high_gain) has filled pipe... */
/* If bw has increased significantly (1.25x), there may be more bw available: */
static const u32 bbr_full_bw_thresh = BBR_UNIT * 5 / 4;
/* But after 3 rounds w/o significant bw growth, estimate pipe is full: */
static const u32 bbr_full_bw_cnt = 3;
/* "long-term" ("LT") bandwidth estimator parameters... */
/* The minimum number of rounds in an LT bw sampling interval: */
static const u32 bbr_lt_intvl_min_rtts = 4;
/* If lost/delivered ratio > 20%, interval is "lossy" and we may be policed: */
static const u32 bbr_lt_loss_thresh = 50;
/* If 2 intervals have a bw ratio <= 1/8, their bw is "consistent": */
static const u32 bbr_lt_bw_ratio = BBR_UNIT / 4;
/* If 2 intervals have a bw diff <= 4 Kbit/sec their bw is "consistent": */
static const u32 bbr_lt_bw_diff = 4000 / 4;
/* If we estimate we're policed, use lt_bw for this many round trips: */
static const u32 bbr_lt_bw_max_rtts = 48;
/* Do we estimate that STARTUP filled the pipe? */
static bool bbr_full_bw_reached(const struct sock *sk)
{
const struct bbr *bbr = inet_csk_ca(sk);
return bbr->full_bw_cnt >= bbr_full_bw_cnt;
}
/* Return the windowed max recent bandwidth sample, in pkts/uS << BW_SCALE. */
static u32 bbr_max_bw(const struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
return minmax_get(&bbr->bw);
}
/* Return the estimated bandwidth of the path, in pkts/uS << BW_SCALE. */
static u32 bbr_bw(const struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
return bbr->lt_use_bw ? bbr->lt_bw : bbr_max_bw(sk);
}
/* Return rate in bytes per second, optionally with a gain.
* The order here is chosen carefully to avoid overflow of u64. This should
* work for input rates of up to 2.9Tbit/sec and gain of 2.89x.
*/
static u64 bbr_rate_bytes_per_sec(struct sock *sk, u64 rate, int gain)
{
rate *= tcp_mss_to_mtu(sk, tcp_sk(sk)->mss_cache);
rate *= gain;
rate >>= BBR_SCALE;
rate *= USEC_PER_SEC;
return rate >> BW_SCALE;
}
/* Convert a BBR bw and gain factor to a pacing rate in bytes per second. */
static u32 bbr_bw_to_pacing_rate(struct sock *sk, u32 bw, int gain)
{
u64 rate = bw;
rate = bbr_rate_bytes_per_sec(sk, rate, gain);
rate = min_t(u64, rate, sk->sk_max_pacing_rate);
return rate;
}
/* Initialize pacing rate to: high_gain * init_cwnd / RTT. */
static void bbr_init_pacing_rate_from_rtt(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u64 bw;
u32 rtt_us;
if (tp->srtt_us) { /* any RTT sample yet? */
rtt_us = max(tp->srtt_us >> 3, 1U);
bbr->has_seen_rtt = 1;
} else { /* no RTT sample yet */
rtt_us = USEC_PER_MSEC; /* use nominal default RTT */
}
bw = (u64)tp->snd_cwnd * BW_UNIT;
do_div(bw, rtt_us);
sk->sk_pacing_rate = bbr_bw_to_pacing_rate(sk, bw, bbr_high_gain);
}
/* Pace using current bw estimate and a gain factor. In order to help drive the
* network toward lower queues while maintaining high utilization and low
* latency, the average pacing rate aims to be slightly (~1%) lower than the
* estimated bandwidth. This is an important aspect of the design. In this
* implementation this slightly lower pacing rate is achieved implicitly by not
* including link-layer headers in the packet size used for the pacing rate.
*/
static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u32 rate = bbr_bw_to_pacing_rate(sk, bw, gain);
if (unlikely(!bbr->has_seen_rtt && tp->srtt_us))
bbr_init_pacing_rate_from_rtt(sk);
if (bbr_full_bw_reached(sk) || rate > sk->sk_pacing_rate)
sk->sk_pacing_rate = rate;
}
/* Return count of segments we want in the skbs we send, or 0 for default. */
static u32 bbr_tso_segs_goal(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
return bbr->tso_segs_goal;
}
static void bbr_set_tso_segs_goal(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u32 min_segs;
min_segs = sk->sk_pacing_rate < (bbr_min_tso_rate >> 3) ? 1 : 2;
bbr->tso_segs_goal = min(tcp_tso_autosize(sk, tp->mss_cache, min_segs),
0x7FU);
}
/* Save "last known good" cwnd so we can restore it after losses or PROBE_RTT */
static void bbr_save_cwnd(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
if (bbr->prev_ca_state < TCP_CA_Recovery && bbr->mode != BBR_PROBE_RTT)
bbr->prior_cwnd = tp->snd_cwnd; /* this cwnd is good enough */
else /* loss recovery or BBR_PROBE_RTT have temporarily cut cwnd */
bbr->prior_cwnd = max(bbr->prior_cwnd, tp->snd_cwnd);
}
static void bbr_cwnd_event(struct sock *sk, enum tcp_ca_event event)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
if (event == CA_EVENT_TX_START && tp->app_limited) {
bbr->idle_restart = 1;
/* Avoid pointless buffer overflows: pace at est. bw if we don't
* need more speed (we're restarting from idle and app-limited).
*/
if (bbr->mode == BBR_PROBE_BW)
bbr_set_pacing_rate(sk, bbr_bw(sk), BBR_UNIT);
}
}
/* Find target cwnd. Right-size the cwnd based on min RTT and the
* estimated bottleneck bandwidth:
*
* cwnd = bw * min_rtt * gain = BDP * gain
*
* The key factor, gain, controls the amount of queue. While a small gain
* builds a smaller queue, it becomes more vulnerable to noise in RTT
* measurements (e.g., delayed ACKs or other ACK compression effects). This
* noise may cause BBR to under-estimate the rate.
*
* To achieve full performance in high-speed paths, we budget enough cwnd to
* fit full-sized skbs in-flight on both end hosts to fully utilize the path:
* - one skb in sending host Qdisc,
* - one skb in sending host TSO/GSO engine
* - one skb being received by receiver host LRO/GRO/delayed-ACK engine
* Don't worry, at low rates (bbr_min_tso_rate) this won't bloat cwnd because
* in such cases tso_segs_goal is 1. The minimum cwnd is 4 packets,
* which allows 2 outstanding 2-packet sequences, to try to keep pipe
* full even with ACK-every-other-packet delayed ACKs.
*/
static u32 bbr_target_cwnd(struct sock *sk, u32 bw, int gain)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 cwnd;
u64 w;
/* If we've never had a valid RTT sample, cap cwnd at the initial
* default. This should only happen when the connection is not using TCP
* timestamps and has retransmitted all of the SYN/SYNACK/data packets
* ACKed so far. In this case, an RTO can cut cwnd to 1, in which
* case we need to slow-start up toward something safe: TCP_INIT_CWND.
*/
if (unlikely(bbr->min_rtt_us == ~0U)) /* no valid RTT samples yet? */
return TCP_INIT_CWND; /* be safe: cap at default initial cwnd*/
w = (u64)bw * bbr->min_rtt_us;
/* Apply a gain to the given value, then remove the BW_SCALE shift. */
cwnd = (((w * gain) >> BBR_SCALE) + BW_UNIT - 1) / BW_UNIT;
/* Allow enough full-sized skbs in flight to utilize end systems. */
cwnd += 3 * bbr->tso_segs_goal;
/* Reduce delayed ACKs by rounding up cwnd to the next even number. */
cwnd = (cwnd + 1) & ~1U;
return cwnd;
}
/* An optimization in BBR to reduce losses: On the first round of recovery, we
* follow the packet conservation principle: send P packets per P packets acked.
* After that, we slow-start and send at most 2*P packets per P packets acked.
* After recovery finishes, or upon undo, we restore the cwnd we had when
* recovery started (capped by the target cwnd based on estimated BDP).
*
* TODO(ycheng/ncardwell): implement a rate-based approach.
*/
static bool bbr_set_cwnd_to_recover_or_restore(
struct sock *sk, const struct rate_sample *rs, u32 acked, u32 *new_cwnd)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u8 prev_state = bbr->prev_ca_state, state = inet_csk(sk)->icsk_ca_state;
u32 cwnd = tp->snd_cwnd;
/* An ACK for P pkts should release at most 2*P packets. We do this
* in two steps. First, here we deduct the number of lost packets.
* Then, in bbr_set_cwnd() we slow start up toward the target cwnd.
*/
if (rs->losses > 0)
cwnd = max_t(s32, cwnd - rs->losses, 1);
if (state == TCP_CA_Recovery && prev_state != TCP_CA_Recovery) {
/* Starting 1st round of Recovery, so do packet conservation. */
bbr->packet_conservation = 1;
bbr->next_rtt_delivered = tp->delivered; /* start round now */
/* Cut unused cwnd from app behavior, TSQ, or TSO deferral: */
cwnd = tcp_packets_in_flight(tp) + acked;
} else if (prev_state >= TCP_CA_Recovery && state < TCP_CA_Recovery) {
/* Exiting loss recovery; restore cwnd saved before recovery. */
bbr->restore_cwnd = 1;
bbr->packet_conservation = 0;
}
bbr->prev_ca_state = state;
if (bbr->restore_cwnd) {
/* Restore cwnd after exiting loss recovery or PROBE_RTT. */
cwnd = max(cwnd, bbr->prior_cwnd);
bbr->restore_cwnd = 0;
}
if (bbr->packet_conservation) {
*new_cwnd = max(cwnd, tcp_packets_in_flight(tp) + acked);
return true; /* yes, using packet conservation */
}
*new_cwnd = cwnd;
return false;
}
/* Slow-start up toward target cwnd (if bw estimate is growing, or packet loss
* has drawn us down below target), or snap down to target if we're above it.
*/
static void bbr_set_cwnd(struct sock *sk, const struct rate_sample *rs,
u32 acked, u32 bw, int gain)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u32 cwnd = 0, target_cwnd = 0;
if (!acked)
return;
if (bbr_set_cwnd_to_recover_or_restore(sk, rs, acked, &cwnd))
goto done;
/* If we're below target cwnd, slow start cwnd toward target cwnd. */
target_cwnd = bbr_target_cwnd(sk, bw, gain);
if (bbr_full_bw_reached(sk)) /* only cut cwnd if we filled the pipe */
cwnd = min(cwnd + acked, target_cwnd);
else if (cwnd < target_cwnd || tp->delivered < TCP_INIT_CWND)
cwnd = cwnd + acked;
cwnd = max(cwnd, bbr_cwnd_min_target);
done:
tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); /* apply global cap */
if (bbr->mode == BBR_PROBE_RTT) /* drain queue, refresh min_rtt */
tp->snd_cwnd = min(tp->snd_cwnd, bbr_cwnd_min_target);
}
/* End cycle phase if it's time and/or we hit the phase's in-flight target. */
static bool bbr_is_next_cycle_phase(struct sock *sk,
const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bool is_full_length =
tcp_stamp_us_delta(tp->delivered_mstamp, bbr->cycle_mstamp) >
bbr->min_rtt_us;
u32 inflight, bw;
/* The pacing_gain of 1.0 paces at the estimated bw to try to fully
* use the pipe without increasing the queue.
*/
if (bbr->pacing_gain == BBR_UNIT)
return is_full_length; /* just use wall clock time */
inflight = rs->prior_in_flight; /* what was in-flight before ACK? */
bw = bbr_max_bw(sk);
/* A pacing_gain > 1.0 probes for bw by trying to raise inflight to at
* least pacing_gain*BDP; this may take more than min_rtt if min_rtt is
* small (e.g. on a LAN). We do not persist if packets are lost, since
* a path with small buffers may not hold that much.
*/
if (bbr->pacing_gain > BBR_UNIT)
return is_full_length &&
(rs->losses || /* perhaps pacing_gain*BDP won't fit */
inflight >= bbr_target_cwnd(sk, bw, bbr->pacing_gain));
/* A pacing_gain < 1.0 tries to drain extra queue we added if bw
* probing didn't find more bw. If inflight falls to match BDP then we
* estimate queue is drained; persisting would underutilize the pipe.
*/
return is_full_length ||
inflight <= bbr_target_cwnd(sk, bw, BBR_UNIT);
}
static void bbr_advance_cycle_phase(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1);
bbr->cycle_mstamp = tp->delivered_mstamp;
bbr->pacing_gain = bbr_pacing_gain[bbr->cycle_idx];
}
/* Gain cycling: cycle pacing gain to converge to fair share of available bw. */
static void bbr_update_cycle_phase(struct sock *sk,
const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
if ((bbr->mode == BBR_PROBE_BW) && !bbr->lt_use_bw &&
bbr_is_next_cycle_phase(sk, rs))
bbr_advance_cycle_phase(sk);
}
static void bbr_reset_startup_mode(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
bbr->mode = BBR_STARTUP;
bbr->pacing_gain = bbr_high_gain;
bbr->cwnd_gain = bbr_high_gain;
}
static void bbr_reset_probe_bw_mode(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
bbr->mode = BBR_PROBE_BW;
bbr->pacing_gain = BBR_UNIT;
bbr->cwnd_gain = bbr_cwnd_gain;
bbr->cycle_idx = CYCLE_LEN - 1 - prandom_u32_max(bbr_cycle_rand);
bbr_advance_cycle_phase(sk); /* flip to next phase of gain cycle */
}
static void bbr_reset_mode(struct sock *sk)
{
if (!bbr_full_bw_reached(sk))
bbr_reset_startup_mode(sk);
else
bbr_reset_probe_bw_mode(sk);
}
/* Start a new long-term sampling interval. */
static void bbr_reset_lt_bw_sampling_interval(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bbr->lt_last_stamp = div_u64(tp->delivered_mstamp, USEC_PER_MSEC);
bbr->lt_last_delivered = tp->delivered;
bbr->lt_last_lost = tp->lost;
bbr->lt_rtt_cnt = 0;
}
/* Completely reset long-term bandwidth sampling. */
static void bbr_reset_lt_bw_sampling(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
bbr->lt_bw = 0;
bbr->lt_use_bw = 0;
bbr->lt_is_sampling = false;
bbr_reset_lt_bw_sampling_interval(sk);
}
/* Long-term bw sampling interval is done. Estimate whether we're policed. */
static void bbr_lt_bw_interval_done(struct sock *sk, u32 bw)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 diff;
if (bbr->lt_bw) { /* do we have bw from a previous interval? */
/* Is new bw close to the lt_bw from the previous interval? */
diff = abs(bw - bbr->lt_bw);
if ((diff * BBR_UNIT <= bbr_lt_bw_ratio * bbr->lt_bw) ||
(bbr_rate_bytes_per_sec(sk, diff, BBR_UNIT) <=
bbr_lt_bw_diff)) {
/* All criteria are met; estimate we're policed. */
bbr->lt_bw = (bw + bbr->lt_bw) >> 1; /* avg 2 intvls */
bbr->lt_use_bw = 1;
bbr->pacing_gain = BBR_UNIT; /* try to avoid drops */
bbr->lt_rtt_cnt = 0;
return;
}
}
bbr->lt_bw = bw;
bbr_reset_lt_bw_sampling_interval(sk);
}
/* Token-bucket traffic policers are common (see "An Internet-Wide Analysis of
* Traffic Policing", SIGCOMM 2016). BBR detects token-bucket policers and
* explicitly models their policed rate, to reduce unnecessary losses. We
* estimate that we're policed if we see 2 consecutive sampling intervals with
* consistent throughput and high packet loss. If we think we're being policed,
* set lt_bw to the "long-term" average delivery rate from those 2 intervals.
*/
static void bbr_lt_bw_sampling(struct sock *sk, const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u32 lost, delivered;
u64 bw;
u32 t;
if (bbr->lt_use_bw) { /* already using long-term rate, lt_bw? */
if (bbr->mode == BBR_PROBE_BW && bbr->round_start &&
++bbr->lt_rtt_cnt >= bbr_lt_bw_max_rtts) {
bbr_reset_lt_bw_sampling(sk); /* stop using lt_bw */
bbr_reset_probe_bw_mode(sk); /* restart gain cycling */
}
return;
}
/* Wait for the first loss before sampling, to let the policer exhaust
* its tokens and estimate the steady-state rate allowed by the policer.
* Starting samples earlier includes bursts that over-estimate the bw.
*/
if (!bbr->lt_is_sampling) {
if (!rs->losses)
return;
bbr_reset_lt_bw_sampling_interval(sk);
bbr->lt_is_sampling = true;
}
/* To avoid underestimates, reset sampling if we run out of data. */
if (rs->is_app_limited) {
bbr_reset_lt_bw_sampling(sk);
return;
}
if (bbr->round_start)
bbr->lt_rtt_cnt++; /* count round trips in this interval */
if (bbr->lt_rtt_cnt < bbr_lt_intvl_min_rtts)
return; /* sampling interval needs to be longer */
if (bbr->lt_rtt_cnt > 4 * bbr_lt_intvl_min_rtts) {
bbr_reset_lt_bw_sampling(sk); /* interval is too long */
return;
}
/* End sampling interval when a packet is lost, so we estimate the
* policer tokens were exhausted. Stopping the sampling before the
* tokens are exhausted under-estimates the policed rate.
*/
if (!rs->losses)
return;
/* Calculate packets lost and delivered in sampling interval. */
lost = tp->lost - bbr->lt_last_lost;
delivered = tp->delivered - bbr->lt_last_delivered;
/* Is loss rate (lost/delivered) >= lt_loss_thresh? If not, wait. */
if (!delivered || (lost << BBR_SCALE) < bbr_lt_loss_thresh * delivered)
return;
/* Find average delivery rate in this sampling interval. */
t = div_u64(tp->delivered_mstamp, USEC_PER_MSEC) - bbr->lt_last_stamp;
if ((s32)t < 1)
return; /* interval is less than one ms, so wait */
/* Check if can multiply without overflow */
if (t >= ~0U / USEC_PER_MSEC) {
bbr_reset_lt_bw_sampling(sk); /* interval too long; reset */
return;
}
t *= USEC_PER_MSEC;
bw = (u64)delivered * BW_UNIT;
do_div(bw, t);
bbr_lt_bw_interval_done(sk, bw);
}
/* Estimate the bandwidth based on how fast packets are delivered */
static void bbr_update_bw(struct sock *sk, const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u64 bw;
bbr->round_start = 0;
if (rs->delivered < 0 || rs->interval_us <= 0)
return; /* Not a valid observation */
/* See if we've reached the next RTT */
if (!before(rs->prior_delivered, bbr->next_rtt_delivered)) {
bbr->next_rtt_delivered = tp->delivered;
bbr->rtt_cnt++;
bbr->round_start = 1;
bbr->packet_conservation = 0;
}
bbr_lt_bw_sampling(sk, rs);
/* Divide delivered by the interval to find a (lower bound) bottleneck
* bandwidth sample. Delivered is in packets and interval_us in uS and
* ratio will be <<1 for most connections. So delivered is first scaled.
*/
bw = (u64)rs->delivered * BW_UNIT;
do_div(bw, rs->interval_us);
/* If this sample is application-limited, it is likely to have a very
* low delivered count that represents application behavior rather than
* the available network rate. Such a sample could drag down estimated
* bw, causing needless slow-down. Thus, to continue to send at the
* last measured network rate, we filter out app-limited samples unless
* they describe the path bw at least as well as our bw model.
*
* So the goal during app-limited phase is to proceed with the best
* network rate no matter how long. We automatically leave this
* phase when app writes faster than the network can deliver :)
*/
if (!rs->is_app_limited || bw >= bbr_max_bw(sk)) {
/* Incorporate new sample into our max bw filter. */
minmax_running_max(&bbr->bw, bbr_bw_rtts, bbr->rtt_cnt, bw);
}
}
/* Estimate when the pipe is full, using the change in delivery rate: BBR
* estimates that STARTUP filled the pipe if the estimated bw hasn't changed by
* at least bbr_full_bw_thresh (25%) after bbr_full_bw_cnt (3) non-app-limited
* rounds. Why 3 rounds: 1: rwin autotuning grows the rwin, 2: we fill the
* higher rwin, 3: we get higher delivery rate samples. Or transient
* cross-traffic or radio noise can go away. CUBIC Hystart shares a similar
* design goal, but uses delay and inter-ACK spacing instead of bandwidth.
*/
static void bbr_check_full_bw_reached(struct sock *sk,
const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 bw_thresh;
if (bbr_full_bw_reached(sk) || !bbr->round_start || rs->is_app_limited)
return;
bw_thresh = (u64)bbr->full_bw * bbr_full_bw_thresh >> BBR_SCALE;
if (bbr_max_bw(sk) >= bw_thresh) {
bbr->full_bw = bbr_max_bw(sk);
bbr->full_bw_cnt = 0;
return;
}
++bbr->full_bw_cnt;
}
/* If pipe is probably full, drain the queue and then enter steady-state. */
static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
if (bbr->mode == BBR_STARTUP && bbr_full_bw_reached(sk)) {
bbr->mode = BBR_DRAIN; /* drain queue we created */
bbr->pacing_gain = bbr_drain_gain; /* pace slow to drain */
bbr->cwnd_gain = bbr_high_gain; /* maintain cwnd */
} /* fall through to check if in-flight is already small: */
if (bbr->mode == BBR_DRAIN &&
tcp_packets_in_flight(tcp_sk(sk)) <=
bbr_target_cwnd(sk, bbr_max_bw(sk), BBR_UNIT))
bbr_reset_probe_bw_mode(sk); /* we estimate queue is drained */
}
/* The goal of PROBE_RTT mode is to have BBR flows cooperatively and
* periodically drain the bottleneck queue, to converge to measure the true
* min_rtt (unloaded propagation delay). This allows the flows to keep queues
* small (reducing queuing delay and packet loss) and achieve fairness among
* BBR flows.
*
* The min_rtt filter window is 10 seconds. When the min_rtt estimate expires,
* we enter PROBE_RTT mode and cap the cwnd at bbr_cwnd_min_target=4 packets.
* After at least bbr_probe_rtt_mode_ms=200ms and at least one packet-timed
* round trip elapsed with that flight size <= 4, we leave PROBE_RTT mode and
* re-enter the previous mode. BBR uses 200ms to approximately bound the
* performance penalty of PROBE_RTT's cwnd capping to roughly 2% (200ms/10s).
*
* Note that flows need only pay 2% if they are busy sending over the last 10
* seconds. Interactive applications (e.g., Web, RPCs, video chunks) often have
* natural silences or low-rate periods within 10 seconds where the rate is low
* enough for long enough to drain its queue in the bottleneck. We pick up
* these min RTT measurements opportunistically with our min_rtt filter. :-)
*/
static void bbr_update_min_rtt(struct sock *sk, const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bool filter_expired;
/* Track min RTT seen in the min_rtt_win_sec filter window: */
filter_expired = after(tcp_jiffies32,
bbr->min_rtt_stamp + bbr_min_rtt_win_sec * HZ);
if (rs->rtt_us >= 0 &&
(rs->rtt_us <= bbr->min_rtt_us || filter_expired)) {
bbr->min_rtt_us = rs->rtt_us;
bbr->min_rtt_stamp = tcp_jiffies32;
}
if (bbr_probe_rtt_mode_ms > 0 && filter_expired &&
!bbr->idle_restart && bbr->mode != BBR_PROBE_RTT) {
bbr->mode = BBR_PROBE_RTT; /* dip, drain queue */
bbr->pacing_gain = BBR_UNIT;
bbr->cwnd_gain = BBR_UNIT;
bbr_save_cwnd(sk); /* note cwnd so we can restore it */
bbr->probe_rtt_done_stamp = 0;
}
if (bbr->mode == BBR_PROBE_RTT) {
/* Ignore low rate samples during this mode. */
tp->app_limited =
(tp->delivered + tcp_packets_in_flight(tp)) ? : 1;
/* Maintain min packets in flight for max(200 ms, 1 round). */
if (!bbr->probe_rtt_done_stamp &&
tcp_packets_in_flight(tp) <= bbr_cwnd_min_target) {
bbr->probe_rtt_done_stamp = tcp_jiffies32 +
msecs_to_jiffies(bbr_probe_rtt_mode_ms);
bbr->probe_rtt_round_done = 0;
bbr->next_rtt_delivered = tp->delivered;
} else if (bbr->probe_rtt_done_stamp) {
if (bbr->round_start)
bbr->probe_rtt_round_done = 1;
if (bbr->probe_rtt_round_done &&
after(tcp_jiffies32, bbr->probe_rtt_done_stamp)) {
bbr->min_rtt_stamp = tcp_jiffies32;
bbr->restore_cwnd = 1; /* snap to prior_cwnd */
bbr_reset_mode(sk);
}
}
}
bbr->idle_restart = 0;
}
static void bbr_update_model(struct sock *sk, const struct rate_sample *rs)
{
bbr_update_bw(sk, rs);
bbr_update_cycle_phase(sk, rs);
bbr_check_full_bw_reached(sk, rs);
bbr_check_drain(sk, rs);
bbr_update_min_rtt(sk, rs);
}
static void bbr_main(struct sock *sk, const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 bw;
bbr_update_model(sk, rs);
bw = bbr_bw(sk);
bbr_set_pacing_rate(sk, bw, bbr->pacing_gain);
bbr_set_tso_segs_goal(sk);
bbr_set_cwnd(sk, rs, rs->acked_sacked, bw, bbr->cwnd_gain);
}
static void bbr_init(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bbr->prior_cwnd = 0;
bbr->tso_segs_goal = 0; /* default segs per skb until first ACK */
bbr->rtt_cnt = 0;
bbr->next_rtt_delivered = 0;
bbr->prev_ca_state = TCP_CA_Open;
bbr->packet_conservation = 0;
bbr->probe_rtt_done_stamp = 0;
bbr->probe_rtt_round_done = 0;
bbr->min_rtt_us = tcp_min_rtt(tp);
bbr->min_rtt_stamp = tcp_jiffies32;
minmax_reset(&bbr->bw, bbr->rtt_cnt, 0); /* init max bw to 0 */
bbr->has_seen_rtt = 0;
bbr_init_pacing_rate_from_rtt(sk);
bbr->restore_cwnd = 0;
bbr->round_start = 0;
bbr->idle_restart = 0;
bbr->full_bw = 0;
bbr->full_bw_cnt = 0;
bbr->cycle_mstamp = 0;
bbr->cycle_idx = 0;
bbr_reset_lt_bw_sampling(sk);
bbr_reset_startup_mode(sk);
cmpxchg(&sk->sk_pacing_status, SK_PACING_NONE, SK_PACING_NEEDED);
}
static u32 bbr_sndbuf_expand(struct sock *sk)
{
/* Provision 3 * cwnd since BBR may slow-start even during recovery. */
return 3;
}
/* In theory BBR does not need to undo the cwnd since it does not
* always reduce cwnd on losses (see bbr_main()). Keep it for now.
*/
static u32 bbr_undo_cwnd(struct sock *sk)
{
return tcp_sk(sk)->snd_cwnd;
}
/* Entering loss recovery, so save cwnd for when we exit or undo recovery. */
static u32 bbr_ssthresh(struct sock *sk)
{
bbr_save_cwnd(sk);
return TCP_INFINITE_SSTHRESH; /* BBR does not use ssthresh */
}
static size_t bbr_get_info(struct sock *sk, u32 ext, int *attr,
union tcp_cc_info *info)
{
if (ext & (1 << (INET_DIAG_BBRINFO - 1)) ||
ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u64 bw = bbr_bw(sk);
bw = bw * tp->mss_cache * USEC_PER_SEC >> BW_SCALE;
memset(&info->bbr, 0, sizeof(info->bbr));
info->bbr.bbr_bw_lo = (u32)bw;
info->bbr.bbr_bw_hi = (u32)(bw >> 32);
info->bbr.bbr_min_rtt = bbr->min_rtt_us;
info->bbr.bbr_pacing_gain = bbr->pacing_gain;
info->bbr.bbr_cwnd_gain = bbr->cwnd_gain;
*attr = INET_DIAG_BBRINFO;
return sizeof(info->bbr);
}
return 0;
}
static void bbr_set_state(struct sock *sk, u8 new_state)
{
struct bbr *bbr = inet_csk_ca(sk);
if (new_state == TCP_CA_Loss) {
struct rate_sample rs = { .losses = 1 };
bbr->prev_ca_state = TCP_CA_Loss;
bbr->full_bw = 0;
bbr->round_start = 1; /* treat RTO like end of a round */
bbr_lt_bw_sampling(sk, &rs);
}
}
static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = {
.flags = TCP_CONG_NON_RESTRICTED,
.name = "nanqinlang",
.owner = THIS_MODULE,
.init = bbr_init,
.cong_control = bbr_main,
.sndbuf_expand = bbr_sndbuf_expand,
.undo_cwnd = bbr_undo_cwnd,
.cwnd_event = bbr_cwnd_event,
.ssthresh = bbr_ssthresh,
.tso_segs_goal = bbr_tso_segs_goal,
.get_info = bbr_get_info,
.set_state = bbr_set_state,
};
static int __init bbr_register(void)
{
BUILD_BUG_ON(sizeof(struct bbr) > ICSK_CA_PRIV_SIZE);
return tcp_register_congestion_control(&tcp_bbr_cong_ops);
}
static void __exit bbr_unregister(void)
{
tcp_unregister_congestion_control(&tcp_bbr_cong_ops);
}
module_init(bbr_register);
module_exit(bbr_unregister);
MODULE_AUTHOR("Van Jacobson <vanj@google.com>");
MODULE_AUTHOR("Neal Cardwell <ncardwell@google.com>");
MODULE_AUTHOR("Yuchung Cheng <ycheng@google.com>");
MODULE_AUTHOR("Soheil Hassas Yeganeh <soheil@google.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("TCP BBR (Bottleneck Bandwidth and RTT)");
MODULE_AUTHOR("Nanqinlang <https://sometimesnaive.org>");

View File

@ -0,0 +1,46 @@
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=tcp-bbr-tsunami
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define KernelPackage/$(PKG_NAME)
SUBMENU:=Network Support
TITLE:=Modified bbr tcp congestion control
DEPENDS:=@LINUX_4_14
FILES:=$(PKG_BUILD_DIR)/tcp_bbr_tsunami.ko
KCONFIG:=
endef
define KernelPackage/$(PKG_NAME)/description
Kernel module of modified BBR tcp congestion control
endef
EXTRA_KCONFIG:= \
EXTRA_CFLAGS:= \
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \
MAKE_OPTS:= \
ARCH="$(LINUX_KARCH)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
SUBDIRS="$(PKG_BUILD_DIR)" \
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
$(EXTRA_KCONFIG)
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) \
modules
endef
$(eval $(call KernelPackage,$(PKG_NAME)))

View File

@ -0,0 +1 @@
obj-m := tcp_bbr_tsunami.o

View File

@ -0,0 +1,905 @@
/* Bottleneck Bandwidth and RTT (BBR) congestion control
*
* BBR congestion control computes the sending rate based on the delivery
* rate (throughput) estimated from ACKs. In a nutshell:
*
* On each ACK, update our model of the network path:
* bottleneck_bandwidth = windowed_max(delivered / elapsed, 10 round trips)
* min_rtt = windowed_min(rtt, 10 seconds)
* pacing_rate = pacing_gain * bottleneck_bandwidth
* cwnd = max(cwnd_gain * bottleneck_bandwidth * min_rtt, 4)
*
* The core algorithm does not react directly to packet losses or delays,
* although BBR may adjust the size of next send per ACK when loss is
* observed, or adjust the sending rate if it estimates there is a
* traffic policer, in order to keep the drop rate reasonable.
*
* BBR is described in detail in:
* "BBR: Congestion-Based Congestion Control",
* Neal Cardwell, Yuchung Cheng, C. Stephen Gunn, Soheil Hassas Yeganeh,
* Van Jacobson. ACM Queue, Vol. 14 No. 5, September-October 2016.
*
* There is a public e-mail list for discussing BBR development and testing:
* https://groups.google.com/forum/#!forum/bbr-dev
*
* NOTE: BBR *must* be used with the fq qdisc ("man tc-fq") with pacing enabled,
* since pacing is integral to the BBR design and implementation.
* BBR without pacing would not function properly, and may incur unnecessary
* high packet loss rates.
*/
#include <linux/module.h>
#include <net/tcp.h>
#include <linux/inet_diag.h>
#include <linux/inet.h>
#include <linux/random.h>
#include <linux/win_minmax.h>
/* Scale factor for rate in pkt/uSec unit to avoid truncation in bandwidth
* estimation. The rate unit ~= (1500 bytes / 1 usec / 2^24) ~= 715 bps.
* This handles bandwidths from 0.06pps (715bps) to 256Mpps (3Tbps) in a u32.
* Since the minimum window is >=4 packets, the lower bound isn't
* an issue. The upper bound isn't an issue with existing technologies.
*/
#define BW_SCALE 24
#define BW_UNIT (1 << BW_SCALE)
#define BBR_SCALE 8 /* scaling factor for fractions in BBR (e.g. gains) */
#define BBR_UNIT (1 << BBR_SCALE)
/* BBR has the following modes for deciding how fast to send: */
enum bbr_mode {
BBR_STARTUP, /* ramp up sending rate rapidly to fill pipe */
BBR_DRAIN, /* drain any queue created during startup */
BBR_PROBE_BW, /* discover, share bw: pace around estimated bw */
BBR_PROBE_RTT, /* cut cwnd to min to probe min_rtt */
};
/* BBR congestion control block */
struct bbr {
u32 min_rtt_us; /* min RTT in min_rtt_win_sec window */
//deprecated u32 rtt_us;
u32 min_rtt_stamp; /* timestamp of min_rtt_us */
u32 probe_rtt_done_stamp; /* end time for BBR_PROBE_RTT mode */
//deprecated struct minmax max_rtt;
struct minmax bw; /* Max recent delivery rate in pkts/uS << 24 */
u32 rtt_cnt; /* count of packet-timed rounds elapsed */
u32 next_rtt_delivered; /* scb->tx.delivered at end of round */
struct skb_mstamp cycle_mstamp; /* time of this cycle phase start */
u32 mode:3, /* current bbr_mode in state machine */
prev_ca_state:3, /* CA state on previous ACK */
packet_conservation:1, /* use packet conservation? */
restore_cwnd:1, /* decided to revert cwnd to old value */
round_start:1, /* start of packet-timed tx->ack round? */
tso_segs_goal:7, /* segments we want in each skb we send */
idle_restart:1, /* restarting after idle? */
probe_rtt_round_done:1, /* a BBR_PROBE_RTT round at 4 pkts? */
unused:5,
lt_is_sampling:1, /* taking long-term ("LT") samples now? */
lt_rtt_cnt:7, /* round trips in long-term interval */
lt_use_bw:1; /* use lt_bw as our bw estimate? */
u32 lt_bw; /* LT est delivery rate in pkts/uS << 24 */
u32 lt_last_delivered; /* LT intvl start: tp->delivered */
u32 lt_last_stamp; /* LT intvl start: tp->delivered_mstamp */
u32 lt_last_lost; /* LT intvl start: tp->lost */
u32 pacing_gain:10, /* current gain for setting pacing rate */
cwnd_gain:10, /* current gain for setting cwnd */
full_bw_cnt:3, /* number of rounds without large bw gains */
cycle_idx:3, /* current index in pacing_gain cycle array */
unused_b:6;
u32 prior_cwnd; /* prior cwnd upon entering loss recovery */
u32 full_bw; /* recent bw, to estimate if pipe is full */
};
#define CYCLE_LEN 8 /* number of phases in a pacing gain cycle */
/* Window length of bw filter (in rounds): */
static const int bbr_bw_rtts = CYCLE_LEN + 7;
/* Window length of min_rtt filter (in sec): */
static const u32 bbr_min_rtt_win_sec = 20;
/* Minimum time (in ms) spent at bbr_cwnd_min_target in BBR_PROBE_RTT mode: */
static const u32 bbr_probe_rtt_mode_ms = 200;
/* Skip TSO below the following bandwidth (bits/sec): */
static const int bbr_min_tso_rate = 1200000;
/* We use a high_gain value of 2/ln(2) because it's the smallest pacing gain
* that will allow a smoothly increasing pacing rate that will double each RTT
* and send the same number of packets per RTT that an un-paced, slow-starting
* Reno or CUBIC flow would:
*/
static const int bbr_high_gain = BBR_UNIT * 2885 / 1000 + 1;
/* The pacing gain of 1/high_gain in BBR_DRAIN is calculated to typically drain
* the queue created in BBR_STARTUP in a single round:
*/
static const int bbr_drain_gain = BBR_UNIT * 1200 / 2885;
/* The gain for deriving steady-state cwnd tolerates delayed/stretched ACKs: */
static const int bbr_cwnd_gain = BBR_UNIT * 2;
/* The pacing_gain values for the PROBE_BW gain cycle, to discover/share bw: */
static const int bbr_pacing_gain[] = {
BBR_UNIT * 3 / 2, /* probe for more available bw */
BBR_UNIT * 3 / 4, /* drain queue and/or yield bw to other flows */
BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, /* cruise at 1.0*bw to utilize pipe, */
BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8 /* without creating excess queue... */
};
/* Randomize the starting gain cycling phase over N phases: */
static const u32 bbr_cycle_rand = 7;
/* Try to keep at least this many packets in flight, if things go smoothly. For
* smooth functioning, a sliding window protocol ACKing every other packet
* needs at least 4 packets in flight:
*/
static const u32 bbr_cwnd_min_target = 4;
/* To estimate if BBR_STARTUP mode (i.e. high_gain) has filled pipe... */
/* If bw has increased significantly (1.25x), there may be more bw available: */
static const u32 bbr_full_bw_thresh = BBR_UNIT * 5 / 4;
/* But after 3 rounds w/o significant bw growth, estimate pipe is full: */
static const u32 bbr_full_bw_cnt = 3;
/* "long-term" ("LT") bandwidth estimator parameters... */
/* The minimum number of rounds in an LT bw sampling interval: */
static const u32 bbr_lt_intvl_min_rtts = 4;
/* If lost/delivered ratio > 20%, interval is "lossy" and we may be policed: */
static const u32 bbr_lt_loss_thresh = 50;
/* If 2 intervals have a bw ratio <= 1/8, their bw is "consistent": */
static const u32 bbr_lt_bw_ratio = BBR_UNIT / 8;
/* If 2 intervals have a bw diff <= 4 Kbit/sec their bw is "consistent": */
static const u32 bbr_lt_bw_diff = 4000 / 8;
/* If we estimate we're policed, use lt_bw for this many round trips: */
static const u32 bbr_lt_bw_max_rtts = 48;
/* Do we estimate that STARTUP filled the pipe? */
static bool bbr_full_bw_reached(const struct sock *sk)
{
const struct bbr *bbr = inet_csk_ca(sk);
return bbr->full_bw_cnt >= bbr_full_bw_cnt;
}
/* Return the windowed max recent bandwidth sample, in pkts/uS << BW_SCALE. */
static u32 bbr_max_bw(const struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
return minmax_get(&bbr->bw);
}
/* Return the estimated bandwidth of the path, in pkts/uS << BW_SCALE. */
static u32 bbr_bw(const struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
return bbr->lt_use_bw ? bbr->lt_bw : bbr_max_bw(sk);
}
/* Return rate in bytes per second, optionally with a gain.
* The order here is chosen carefully to avoid overflow of u64. This should
* work for input rates of up to 2.9Tbit/sec and gain of 2.89x.
*/
static u64 bbr_rate_bytes_per_sec(struct sock *sk, u64 rate, int gain)
{
rate *= tcp_mss_to_mtu(sk, tcp_sk(sk)->mss_cache);
rate *= gain;
rate >>= BBR_SCALE;
rate *= USEC_PER_SEC;
return rate >> BW_SCALE;
}
/* Pace using current bw estimate and a gain factor. In order to help drive the
* network toward lower queues while maintaining high utilization and low
* latency, the average pacing rate aims to be slightly (~1%) lower than the
* estimated bandwidth. This is an important aspect of the design. In this
* implementation this slightly lower pacing rate is achieved implicitly by not
* including link-layer headers in the packet size used for the pacing rate.
*/
static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain)
{
struct bbr *bbr = inet_csk_ca(sk);
u64 rate = bw;
rate = bbr_rate_bytes_per_sec(sk, rate, gain);
rate = min_t(u64, rate, sk->sk_max_pacing_rate);
if (bbr->mode != BBR_STARTUP || rate > sk->sk_pacing_rate)
sk->sk_pacing_rate = rate;
}
/* Return count of segments we want in the skbs we send, or 0 for default. */
static u32 bbr_tso_segs_goal(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
return bbr->tso_segs_goal;
}
static void bbr_set_tso_segs_goal(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u32 min_segs;
min_segs = sk->sk_pacing_rate < (bbr_min_tso_rate >> 3) ? 1 : 2;
bbr->tso_segs_goal = min(tcp_tso_autosize(sk, tp->mss_cache, min_segs),
0x7FU);
}
/* Save "last known good" cwnd so we can restore it after losses or PROBE_RTT */
static void bbr_save_cwnd(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
if (bbr->prev_ca_state < TCP_CA_Recovery && bbr->mode != BBR_PROBE_RTT)
bbr->prior_cwnd = tp->snd_cwnd; /* this cwnd is good enough */
else /* loss recovery or BBR_PROBE_RTT have temporarily cut cwnd */
bbr->prior_cwnd = max(bbr->prior_cwnd, tp->snd_cwnd);
}
static void bbr_cwnd_event(struct sock *sk, enum tcp_ca_event event)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
if (event == CA_EVENT_TX_START && tp->app_limited) {
bbr->idle_restart = 1;
/* Avoid pointless buffer overflows: pace at est. bw if we don't
* need more speed (we're restarting from idle and app-limited).
*/
if (bbr->mode == BBR_PROBE_BW)
bbr_set_pacing_rate(sk, bbr_bw(sk), BBR_UNIT);
}
}
/* Find target cwnd. Right-size the cwnd based on min RTT and the
* estimated bottleneck bandwidth:
*
* cwnd = bw * min_rtt * gain = BDP * gain
*
* The key factor, gain, controls the amount of queue. While a small gain
* builds a smaller queue, it becomes more vulnerable to noise in RTT
* measurements (e.g., delayed ACKs or other ACK compression effects). This
* noise may cause BBR to under-estimate the rate.
*
* To achieve full performance in high-speed paths, we budget enough cwnd to
* fit full-sized skbs in-flight on both end hosts to fully utilize the path:
* - one skb in sending host Qdisc,
* - one skb in sending host TSO/GSO engine
* - one skb being received by receiver host LRO/GRO/delayed-ACK engine
* Don't worry, at low rates (bbr_min_tso_rate) this won't bloat cwnd because
* in such cases tso_segs_goal is 1. The minimum cwnd is 4 packets,
* which allows 2 outstanding 2-packet sequences, to try to keep pipe
* full even with ACK-every-other-packet delayed ACKs.
*/
static u32 bbr_target_cwnd(struct sock *sk, u32 bw, int gain)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 cwnd;
u64 w;
/* If we've never had a valid RTT sample, cap cwnd at the initial
* default. This should only happen when the connection is not using TCP
* timestamps and has retransmitted all of the SYN/SYNACK/data packets
* ACKed so far. In this case, an RTO can cut cwnd to 1, in which
* case we need to slow-start up toward something safe: TCP_INIT_CWND.
*/
if (unlikely(bbr->min_rtt_us == ~0U)) /* no valid RTT samples yet? */
return TCP_INIT_CWND; /* be safe: cap at default initial cwnd*/
w = (u64)bw * bbr->min_rtt_us;
/* Apply a gain to the given value, then remove the BW_SCALE shift. */
cwnd = (((w * gain) >> BBR_SCALE) + BW_UNIT - 1) / BW_UNIT;
/* Allow enough full-sized skbs in flight to utilize end systems. */
cwnd += 3 * bbr->tso_segs_goal;
/* Reduce delayed ACKs by rounding up cwnd to the next even number. */
cwnd = (cwnd + 1) & ~1U;
return cwnd;
}
/* An optimization in BBR to reduce losses: On the first round of recovery, we
* follow the packet conservation principle: send P packets per P packets acked.
* After that, we slow-start and send at most 2*P packets per P packets acked.
* After recovery finishes, or upon undo, we restore the cwnd we had when
* recovery started (capped by the target cwnd based on estimated BDP).
*
* TODO(ycheng/ncardwell): implement a rate-based approach.
*/
static bool bbr_set_cwnd_to_recover_or_restore(
struct sock *sk, const struct rate_sample *rs, u32 acked, u32 *new_cwnd)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u8 prev_state = bbr->prev_ca_state, state = inet_csk(sk)->icsk_ca_state;
u32 cwnd = tp->snd_cwnd;
/* An ACK for P pkts should release at most 2*P packets. We do this
* in two steps. First, here we deduct the number of lost packets.
* Then, in bbr_set_cwnd() we slow start up toward the target cwnd.
*/
if (rs->losses > 0)
cwnd = max_t(s32, cwnd - rs->losses, 1);
if (state == TCP_CA_Recovery && prev_state != TCP_CA_Recovery) {
/* Starting 1st round of Recovery, so do packet conservation. */
bbr->packet_conservation = 1;
bbr->next_rtt_delivered = tp->delivered; /* start round now */
/* Cut unused cwnd from app behavior, TSQ, or TSO deferral: */
cwnd = tcp_packets_in_flight(tp) + acked;
} else if (prev_state >= TCP_CA_Recovery && state < TCP_CA_Recovery) {
/* Exiting loss recovery; restore cwnd saved before recovery. */
bbr->restore_cwnd = 1;
bbr->packet_conservation = 0;
}
bbr->prev_ca_state = state;
if (bbr->restore_cwnd) {
/* Restore cwnd after exiting loss recovery or PROBE_RTT. */
cwnd = max(cwnd, bbr->prior_cwnd);
bbr->restore_cwnd = 0;
}
if (bbr->packet_conservation) {
*new_cwnd = max(cwnd, tcp_packets_in_flight(tp) + acked);
return true; /* yes, using packet conservation */
}
*new_cwnd = cwnd;
return false;
}
/* Slow-start up toward target cwnd (if bw estimate is growing, or packet loss
* has drawn us down below target), or snap down to target if we're above it.
*/
static void bbr_set_cwnd(struct sock *sk, const struct rate_sample *rs,
u32 acked, u32 bw, int gain)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u32 cwnd = 0, target_cwnd = 0;
if (!acked)
return;
if (bbr_set_cwnd_to_recover_or_restore(sk, rs, acked, &cwnd))
goto done;
/* If we're below target cwnd, slow start cwnd toward target cwnd. */
target_cwnd = bbr_target_cwnd(sk, bw, gain);
if (bbr_full_bw_reached(sk)) /* only cut cwnd if we filled the pipe */
cwnd = min(cwnd + acked, target_cwnd);
else if (cwnd < target_cwnd || tp->delivered < TCP_INIT_CWND)
cwnd = cwnd + acked;
cwnd = max(cwnd, bbr_cwnd_min_target);
done:
tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); /* apply global cap */
if (bbr->mode == BBR_PROBE_RTT) /* drain queue, refresh min_rtt */
tp->snd_cwnd = max(tp->snd_cwnd >> 1, bbr_cwnd_min_target);
}
/* End cycle phase if it's time and/or we hit the phase's in-flight target. */
static bool bbr_is_next_cycle_phase(struct sock *sk,
const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bool is_full_length =
skb_mstamp_us_delta(&tp->delivered_mstamp, &bbr->cycle_mstamp) >
bbr->min_rtt_us;
u32 inflight, bw;
/* The pacing_gain of 1.0 paces at the estimated bw to try to fully
* use the pipe without increasing the queue.
*/
if (bbr->pacing_gain == BBR_UNIT)
return is_full_length; /* just use wall clock time */
inflight = rs->prior_in_flight; /* what was in-flight before ACK? */
bw = bbr_max_bw(sk);
/* A pacing_gain > 1.0 probes for bw by trying to raise inflight to at
* least pacing_gain*BDP; this may take more than min_rtt if min_rtt is
* small (e.g. on a LAN). We do not persist if packets are lost, since
* a path with small buffers may not hold that much.
*/
if (bbr->pacing_gain > BBR_UNIT)
return is_full_length &&
(rs->losses || /* perhaps pacing_gain*BDP won't fit */
inflight >= bbr_target_cwnd(sk, bw, bbr->pacing_gain));
/* A pacing_gain < 1.0 tries to drain extra queue we added if bw
* probing didn't find more bw. If inflight falls to match BDP then we
* estimate queue is drained; persisting would underutilize the pipe.
*/
return is_full_length ||
inflight <= bbr_target_cwnd(sk, bw, BBR_UNIT);
}
static void bbr_advance_cycle_phase(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1);
bbr->cycle_mstamp = tp->delivered_mstamp;
bbr->pacing_gain = bbr_pacing_gain[bbr->cycle_idx];
}
/* Gain cycling: cycle pacing gain to converge to fair share of available bw. */
static void bbr_update_cycle_phase(struct sock *sk,
const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
if ((bbr->mode == BBR_PROBE_BW) && !bbr->lt_use_bw &&
bbr_is_next_cycle_phase(sk, rs))
bbr_advance_cycle_phase(sk);
}
static void bbr_reset_startup_mode(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
bbr->mode = BBR_STARTUP;
bbr->pacing_gain = bbr_high_gain;
bbr->cwnd_gain = bbr_high_gain;
}
static void bbr_reset_probe_bw_mode(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
bbr->mode = BBR_PROBE_BW;
bbr->pacing_gain = BBR_UNIT;
bbr->cwnd_gain = bbr_cwnd_gain;
bbr->cycle_idx = CYCLE_LEN - 1 - prandom_u32_max(bbr_cycle_rand);
bbr_advance_cycle_phase(sk); /* flip to next phase of gain cycle */
}
static void bbr_reset_mode(struct sock *sk)
{
if (!bbr_full_bw_reached(sk))
bbr_reset_startup_mode(sk);
else
bbr_reset_probe_bw_mode(sk);
}
/* Start a new long-term sampling interval. */
static void bbr_reset_lt_bw_sampling_interval(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
bbr->lt_last_stamp = tp->delivered_mstamp.stamp_jiffies;
bbr->lt_last_delivered = tp->delivered;
bbr->lt_last_lost = tp->lost;
bbr->lt_rtt_cnt = 0;
}
/* Completely reset long-term bandwidth sampling. */
static void bbr_reset_lt_bw_sampling(struct sock *sk)
{
struct bbr *bbr = inet_csk_ca(sk);
bbr->lt_bw = 0;
bbr->lt_use_bw = 0;
bbr->lt_is_sampling = false;
bbr_reset_lt_bw_sampling_interval(sk);
}
/* Long-term bw sampling interval is done. Estimate whether we're policed. */
static void bbr_lt_bw_interval_done(struct sock *sk, u32 bw)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 diff;
if (bbr->lt_bw) { /* do we have bw from a previous interval? */
/* Is new bw close to the lt_bw from the previous interval? */
diff = abs(bw - bbr->lt_bw);
if ((diff * BBR_UNIT <= bbr_lt_bw_ratio * bbr->lt_bw) ||
(bbr_rate_bytes_per_sec(sk, diff, BBR_UNIT) <=
bbr_lt_bw_diff)) {
/* All criteria are met; estimate we're policed. */
bbr->lt_bw = (bw + bbr->lt_bw) >> 1; /* avg 2 intvls */
bbr->lt_use_bw = 1;
bbr->pacing_gain = BBR_UNIT; /* try to avoid drops */
bbr->lt_rtt_cnt = 0;
return;
}
}
bbr->lt_bw = bw;
bbr_reset_lt_bw_sampling_interval(sk);
}
/* Token-bucket traffic policers are common (see "An Internet-Wide Analysis of
* Traffic Policing", SIGCOMM 2016). BBR detects token-bucket policers and
* explicitly models their policed rate, to reduce unnecessary losses. We
* estimate that we're policed if we see 2 consecutive sampling intervals with
* consistent throughput and high packet loss. If we think we're being policed,
* set lt_bw to the "long-term" average delivery rate from those 2 intervals.
*/
static void bbr_lt_bw_sampling(struct sock *sk, const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u32 lost, delivered;
u64 bw;
s32 t;
if (bbr->lt_use_bw) { /* already using long-term rate, lt_bw? */
if (bbr->mode == BBR_PROBE_BW && bbr->round_start &&
++bbr->lt_rtt_cnt >= bbr_lt_bw_max_rtts) {
bbr_reset_lt_bw_sampling(sk); /* stop using lt_bw */
bbr_reset_probe_bw_mode(sk); /* restart gain cycling */
}
return;
}
/* Wait for the first loss before sampling, to let the policer exhaust
* its tokens and estimate the steady-state rate allowed by the policer.
* Starting samples earlier includes bursts that over-estimate the bw.
*/
if (!bbr->lt_is_sampling) {
if (!rs->losses)
return;
bbr_reset_lt_bw_sampling_interval(sk);
bbr->lt_is_sampling = true;
}
/* To avoid underestimates, reset sampling if we run out of data. */
if (rs->is_app_limited) {
bbr_reset_lt_bw_sampling(sk);
return;
}
if (bbr->round_start)
bbr->lt_rtt_cnt++; /* count round trips in this interval */
if (bbr->lt_rtt_cnt < bbr_lt_intvl_min_rtts)
return; /* sampling interval needs to be longer */
if (bbr->lt_rtt_cnt > 4 * bbr_lt_intvl_min_rtts) {
bbr_reset_lt_bw_sampling(sk); /* interval is too long */
return;
}
/* End sampling interval when a packet is lost, so we estimate the
* policer tokens were exhausted. Stopping the sampling before the
* tokens are exhausted under-estimates the policed rate.
*/
if (!rs->losses)
return;
/* Calculate packets lost and delivered in sampling interval. */
lost = tp->lost - bbr->lt_last_lost;
delivered = tp->delivered - bbr->lt_last_delivered;
/* Is loss rate (lost/delivered) >= lt_loss_thresh? If not, wait. */
if (!delivered || (lost << BBR_SCALE) < bbr_lt_loss_thresh * delivered)
return;
/* Find average delivery rate in this sampling interval. */
t = (s32)(tp->delivered_mstamp.stamp_jiffies - bbr->lt_last_stamp);
if (t < 1)
return; /* interval is less than one jiffy, so wait */
t = jiffies_to_usecs(t);
/* Interval long enough for jiffies_to_usecs() to return a bogus 0? */
if (t < 1) {
bbr_reset_lt_bw_sampling(sk); /* interval too long; reset */
return;
}
bw = (u64)delivered * BW_UNIT;
do_div(bw, t);
bbr_lt_bw_interval_done(sk, bw);
}
/* Estimate the bandwidth based on how fast packets are delivered */
static void bbr_update_bw(struct sock *sk, const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u64 bw;
bbr->round_start = 0;
if (rs->delivered < 0 || rs->interval_us <= 0)
return; /* Not a valid observation */
/* See if we've reached the next RTT */
if (!before(rs->prior_delivered, bbr->next_rtt_delivered)) {
bbr->next_rtt_delivered = tp->delivered;
bbr->rtt_cnt++;
bbr->round_start = 1;
bbr->packet_conservation = 0;
}
bbr_lt_bw_sampling(sk, rs);
/* Divide delivered by the interval to find a (lower bound) bottleneck
* bandwidth sample. Delivered is in packets and interval_us in uS and
* ratio will be <<1 for most connections. So delivered is first scaled.
*/
bw = (u64)rs->delivered * BW_UNIT;
do_div(bw, rs->interval_us);
/* If this sample is application-limited, it is likely to have a very
* low delivered count that represents application behavior rather than
* the available network rate. Such a sample could drag down estimated
* bw, causing needless slow-down. Thus, to continue to send at the
* last measured network rate, we filter out app-limited samples unless
* they describe the path bw at least as well as our bw model.
*
* So the goal during app-limited phase is to proceed with the best
* network rate no matter how long. We automatically leave this
* phase when app writes faster than the network can deliver :)
*/
if (!rs->is_app_limited || bw >= bbr_max_bw(sk)) {
/* Incorporate new sample into our max bw filter. */
minmax_running_max(&bbr->bw, bbr_bw_rtts, bbr->rtt_cnt, bw);
}
}
/* Estimate when the pipe is full, using the change in delivery rate: BBR
* estimates that STARTUP filled the pipe if the estimated bw hasn't changed by
* at least bbr_full_bw_thresh (25%) after bbr_full_bw_cnt (3) non-app-limited
* rounds. Why 3 rounds: 1: rwin autotuning grows the rwin, 2: we fill the
* higher rwin, 3: we get higher delivery rate samples. Or transient
* cross-traffic or radio noise can go away. CUBIC Hystart shares a similar
* design goal, but uses delay and inter-ACK spacing instead of bandwidth.
*/
static void bbr_check_full_bw_reached(struct sock *sk,
const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 bw_thresh;
if (bbr_full_bw_reached(sk) || !bbr->round_start || rs->is_app_limited)
return;
bw_thresh = (u64)bbr->full_bw * bbr_full_bw_thresh >> BBR_SCALE;
if (bbr_max_bw(sk) >= bw_thresh) {
bbr->full_bw = bbr_max_bw(sk);
bbr->full_bw_cnt = 0;
return;
}
++bbr->full_bw_cnt;
}
/* If pipe is probably full, drain the queue and then enter steady-state. */
static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
if (bbr->mode == BBR_STARTUP && bbr_full_bw_reached(sk)) {
bbr->mode = BBR_DRAIN; /* drain queue we created */
bbr->pacing_gain = bbr_drain_gain; /* pace slow to drain */
bbr->cwnd_gain = bbr_high_gain; /* maintain cwnd */
} /* fall through to check if in-flight is already small: */
if (bbr->mode == BBR_DRAIN &&
tcp_packets_in_flight(tcp_sk(sk)) <=
bbr_target_cwnd(sk, bbr_max_bw(sk), BBR_UNIT))
bbr_reset_probe_bw_mode(sk); /* we estimate queue is drained */
}
/* The goal of PROBE_RTT mode is to have BBR flows cooperatively and
* periodically drain the bottleneck queue, to converge to measure the true
* min_rtt (unloaded propagation delay). This allows the flows to keep queues
* small (reducing queuing delay and packet loss) and achieve fairness among
* BBR flows.
*
* The min_rtt filter window is 10 seconds. When the min_rtt estimate expires,
* we enter PROBE_RTT mode and cap the cwnd at bbr_cwnd_min_target=4 packets.
* After at least bbr_probe_rtt_mode_ms=200ms and at least one packet-timed
* round trip elapsed with that flight size <= 4, we leave PROBE_RTT mode and
* re-enter the previous mode. BBR uses 200ms to approximately bound the
* performance penalty of PROBE_RTT's cwnd capping to roughly 2% (200ms/10s).
*
* Note that flows need only pay 2% if they are busy sending over the last 10
* seconds. Interactive applications (e.g., Web, RPCs, video chunks) often have
* natural silences or low-rate periods within 10 seconds where the rate is low
* enough for long enough to drain its queue in the bottleneck. We pick up
* these min RTT measurements opportunistically with our min_rtt filter. :-)
*/
static void bbr_update_min_rtt(struct sock *sk, const struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
//deprecated u32 rtt_prior = 0;
bool filter_expired;
/* Track min RTT seen in the min_rtt_win_sec filter window: */
filter_expired = after(tcp_time_stamp,
bbr->min_rtt_stamp + bbr_min_rtt_win_sec * HZ);
if (rs->rtt_us >= 0 &&
(rs->rtt_us <= bbr->min_rtt_us || filter_expired)) {
bbr->min_rtt_us = rs->rtt_us;
bbr->min_rtt_stamp = tcp_time_stamp;
//deprecated bbr->rtt_us = rs->rtt_us;
}
//deprecated bbr->rtt_us = rs->rtt_us;
//deprecated rtt_prior = minmax_get(&bbr->max_rtt);
//deprecated bbr->rtt_us = min(bbr->rtt_us, rtt_prior);
//deprecated minmax_running_max(&bbr->max_rtt, bbr_bw_rtts, bbr->rtt_cnt, rs->rtt_us);
if (bbr_probe_rtt_mode_ms > 0 && filter_expired &&
!bbr->idle_restart && bbr->mode != BBR_PROBE_RTT) {
bbr->mode = BBR_PROBE_RTT; /* dip, drain queue */
bbr->pacing_gain = BBR_UNIT;
bbr->cwnd_gain = BBR_UNIT;
bbr_save_cwnd(sk); /* note cwnd so we can restore it */
bbr->probe_rtt_done_stamp = 0;
}
if (bbr->mode == BBR_PROBE_RTT) {
/* Ignore low rate samples during this mode. */
tp->app_limited =
(tp->delivered + tcp_packets_in_flight(tp)) ? : 1;
/* Maintain min packets in flight for max(200 ms, 1 round). */
if (!bbr->probe_rtt_done_stamp &&
tcp_packets_in_flight(tp) <= bbr_cwnd_min_target) {
bbr->probe_rtt_done_stamp = tcp_time_stamp +
msecs_to_jiffies(bbr_probe_rtt_mode_ms >> 1);
bbr->probe_rtt_round_done = 0;
bbr->next_rtt_delivered = tp->delivered;
} else if (bbr->probe_rtt_done_stamp) {
if (bbr->round_start)
bbr->probe_rtt_round_done = 1;
if (bbr->probe_rtt_round_done &&
after(tcp_time_stamp, bbr->probe_rtt_done_stamp)) {
bbr->min_rtt_stamp = tcp_time_stamp;
bbr->restore_cwnd = 1; /* snap to prior_cwnd */
bbr_reset_mode(sk);
}
}
}
bbr->idle_restart = 0;
}
static void bbr_update_model(struct sock *sk, const struct rate_sample *rs)
{
bbr_update_bw(sk, rs);
bbr_update_cycle_phase(sk, rs);
bbr_check_full_bw_reached(sk, rs);
bbr_check_drain(sk, rs);
bbr_update_min_rtt(sk, rs);
}
static void bbr_main(struct sock *sk, const struct rate_sample *rs)
{
struct bbr *bbr = inet_csk_ca(sk);
u32 bw;
bbr_update_model(sk, rs);
bw = bbr_bw(sk);
bbr_set_pacing_rate(sk, bw, bbr->pacing_gain);
bbr_set_tso_segs_goal(sk);
bbr_set_cwnd(sk, rs, rs->acked_sacked, bw, bbr->cwnd_gain);
}
static void bbr_init(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u64 bw;
bbr->prior_cwnd = 0;
bbr->tso_segs_goal = 0; /* default segs per skb until first ACK */
bbr->rtt_cnt = 0;
bbr->next_rtt_delivered = 0;
bbr->prev_ca_state = TCP_CA_Open;
bbr->packet_conservation = 0;
bbr->probe_rtt_done_stamp = 0;
bbr->probe_rtt_round_done = 0;
bbr->min_rtt_us = tcp_min_rtt(tp);
bbr->min_rtt_stamp = tcp_time_stamp;
minmax_reset(&bbr->bw, bbr->rtt_cnt, 0); /* init max bw to 0 */
/* Initialize pacing rate to: high_gain * init_cwnd / RTT. */
bw = (u64)tp->snd_cwnd * BW_UNIT;
do_div(bw, (tp->srtt_us >> 3) ? : USEC_PER_MSEC);
sk->sk_pacing_rate = 0; /* force an update of sk_pacing_rate */
bbr_set_pacing_rate(sk, bw, bbr_high_gain);
bbr->restore_cwnd = 0;
bbr->round_start = 0;
bbr->idle_restart = 0;
bbr->full_bw = 0;
bbr->full_bw_cnt = 0;
bbr->cycle_mstamp.v64 = 0;
bbr->cycle_idx = 0;
bbr_reset_lt_bw_sampling(sk);
bbr_reset_startup_mode(sk);
}
static u32 bbr_sndbuf_expand(struct sock *sk)
{
/* Provision 3 * cwnd since BBR may slow-start even during recovery. */
return 3;
}
/* In theory BBR does not need to undo the cwnd since it does not
* always reduce cwnd on losses (see bbr_main()). Keep it for now.
*/
static u32 bbr_undo_cwnd(struct sock *sk)
{
return tcp_sk(sk)->snd_cwnd;
}
/* Entering loss recovery, so save cwnd for when we exit or undo recovery. */
static u32 bbr_ssthresh(struct sock *sk)
{
bbr_save_cwnd(sk);
return TCP_INFINITE_SSTHRESH; /* BBR does not use ssthresh */
}
static size_t bbr_get_info(struct sock *sk, u32 ext, int *attr,
union tcp_cc_info *info)
{
if (ext & (1 << (INET_DIAG_BBRINFO - 1)) ||
ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_csk_ca(sk);
u64 bw = bbr_bw(sk);
bw = bw * tp->mss_cache * USEC_PER_SEC >> BW_SCALE;
memset(&info->bbr, 0, sizeof(info->bbr));
info->bbr.bbr_bw_lo = (u32)bw;
info->bbr.bbr_bw_hi = (u32)(bw >> 32);
info->bbr.bbr_min_rtt = bbr->min_rtt_us;
info->bbr.bbr_pacing_gain = bbr->pacing_gain;
info->bbr.bbr_cwnd_gain = bbr->cwnd_gain;
*attr = INET_DIAG_BBRINFO;
return sizeof(info->bbr);
}
return 0;
}
static void bbr_set_state(struct sock *sk, u8 new_state)
{
struct bbr *bbr = inet_csk_ca(sk);
if (new_state == TCP_CA_Loss) {
struct rate_sample rs = { .losses = 1 };
bbr->prev_ca_state = TCP_CA_Loss;
bbr->full_bw = 0;
bbr->round_start = 1; /* treat RTO like end of a round */
bbr_lt_bw_sampling(sk, &rs);
}
}
static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = {
.flags = TCP_CONG_NON_RESTRICTED,
.name = "tsunami",
.owner = THIS_MODULE,
.init = bbr_init,
.cong_control = bbr_main,
.sndbuf_expand = bbr_sndbuf_expand,
.undo_cwnd = bbr_undo_cwnd,
.cwnd_event = bbr_cwnd_event,
.ssthresh = bbr_ssthresh,
.tso_segs_goal = bbr_tso_segs_goal,
.get_info = bbr_get_info,
.set_state = bbr_set_state,
};
static int __init bbr_register(void)
{
BUILD_BUG_ON(sizeof(struct bbr) > ICSK_CA_PRIV_SIZE);
return tcp_register_congestion_control(&tcp_bbr_cong_ops);
}
static void __exit bbr_unregister(void)
{
tcp_unregister_congestion_control(&tcp_bbr_cong_ops);
}
module_init(bbr_register);
module_exit(bbr_unregister);
MODULE_AUTHOR("Van Jacobson <vanj@google.com>");
MODULE_AUTHOR("Neal Cardwell <ncardwell@google.com>");
MODULE_AUTHOR("Yuchung Cheng <ycheng@google.com>");
MODULE_AUTHOR("Soheil Hassas Yeganeh <soheil@google.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("TCP BBR (Bottleneck Bandwidth and RTT)");

View File

@ -0,0 +1,48 @@
#
# Copyright (C) 2010 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=cups-bjnp
PKG_VERSION:=2.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=@SF/cups-bjnp
PKG_MD5SUM:=8a337091b65abfdd70191129efefb9b3
PKG_BUILD_DEPENDS:=cups
include $(INCLUDE_DIR)/package.mk
define Package/cups-bjnp
SECTION:=net
CATEGORY:=Network
TITLE:=BJNP protocol backend for CUPS
URL:=http://sourceforge.net/projects/cups-bjnp/
DEPENDS:=cups
SUBMENU:=Printing
endef
define Package/cups-bjnp/description
CUPS backend for the canon printers using the proprietary USB over
IP BJNP protocol. This backend allows Cups to print over the network
to a Canon printer. It currently supports Cups 1.2 and Cups 1.3 and
is designed by reverse engineering.
endef
CONFIGURE_ARGS += --with-cupsbackenddir=$(STAGING_DIR)/usr/include/cups
TARGET_CFLAGS+=-Wl,-rpath-link=$(STAGING_DIR)/usr/lib
define Package/cups-bjnp/install
$(INSTALL_DIR) $(1)/usr/lib/cups/backend
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bjnp $(1)/usr/lib/cups/backend
endef
$(eval $(call BuildPackage,cups-bjnp))

View File

@ -0,0 +1,321 @@
#
# Copyright (C) 2006-2016 OpenWrt.org
# Copyright (C) 2016 lede-project.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=cups
PKG_VERSION:=2.1.4
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-source.tar.gz
PKG_SOURCE_URL:=http://www.cups.org/software/$(PKG_VERSION)
PKG_MD5SUM:=9f9bf6e3b9c20a3519b4dc409666d6e7
TARGET_LDFLAGS+=-Wl,-rpath-link=$(STAGING_DIR)/usr/lib
include $(INCLUDE_DIR)/package.mk
define Package/cups/Default
URL:=http://www.cups.org/
SUBMENU:=Printing
endef
define Package/cups
$(call Package/cups/Default)
SECTION:=net
CATEGORY:=Network
DEPENDS:=+libcups +libcupsmime +libcupscgi +libcupsppdc +libusb-1.0
TITLE:=Common UNIX Printing System (daemon)
endef
define Package/cups/description
Common UNIX Printing System (daemon)
endef
define Package/cups/conffiles
/etc/cups/classes.conf
/etc/cups/cupsd.conf
/etc/cups/printers.conf
endef
define Package/cups-bsd
$(call Package/cups/Default)
SECTION:=net
CATEGORY:=Network
DEPENDS:=+libcups
TITLE:=Common UNIX Printing System - BSD commands (old)
endef
define Package/cups-bsd/description
Common UNIX Printing System - BSD commands (old)
endef
define Package/cups-client
$(call Package/cups/Default)
SECTION:=net
CATEGORY:=Network
DEPENDS:=+libcups +libcupsimage +libcupsmime
TITLE:=Common UNIX Printing System - Client commands
endef
define Package/cups-client/conffiles
/etc/cups/client.conf
endef
define Package/cups-client/description
Common UNIX Printing System - Client commands
endef
define Package/cups-filters
$(call Package/cups/Default)
SECTION:=net
CATEGORY:=Network
DEPENDS:=+libcupsimage
TITLE:=Common UNIX Printing System - Filter
endef
define Package/cups-filters/description
Common UNIX Printing System - Filter
endef
define Package/cups-ppdc
$(call Package/cups/Default)
SECTION:=net
CATEGORY:=Network
DEPENDS:=+libcupsppdc
TITLE:=Common UNIX Printing System - PPDC utils
endef
define Package/cups-ppdc/description
Common UNIX Printing System - PPDC utils
endef
define Package/libcups
$(call Package/cups/Default)
SECTION:=libs
CATEGORY:=Libraries
DEPENDS:=+zlib +libpthread +libpng +libjpeg
TITLE:=Common UNIX Printing System - Core library
endef
define Package/libcups/description
Common UNIX Printing System - Core library
endef
define Package/libcupscgi
$(call Package/cups/Default)
SECTION:=libs
CATEGORY:=Libraries
DEPENDS:=+libcups
TITLE:=Common UNIX Printing System - CGI library
endef
define Package/libcupscgi/description
Common UNIX Printing System - CGI library
endef
define Package/libcupsimage
$(call Package/cups/Default)
SECTION:=libs
CATEGORY:=Libraries
DEPENDS:=+libcups
TITLE:=Common UNIX Printing System - Image library
endef
define Package/libcupsimage/description
Common UNIX Printing System - Image library
endef
define Package/libcupsmime
$(call Package/cups/Default)
SECTION:=libs
CATEGORY:=Libraries
DEPENDS:=+libcups
TITLE:=Common UNIX Printing System - MIME library
endef
define Package/libcupsmime/description
Common UNIX Printing System - MIME library
endef
define Package/libcupsppdc
$(call Package/cups/Default)
SECTION:=libs
CATEGORY:=Libraries
DEPENDS:=+libcups +libstdcpp
TITLE:=Common UNIX Printing System - PPDC library
endef
define Package/libcupsppdc/description
Common UNIX Printing System - PPDC library
endef
define Build/Configure
$(call Build/Configure/Default, \
--with-cups-user="nobody" \
--with-cups-group="nogroup" \
--with-system-groups="root" \
--with-pdftops="none" \
--without-perl \
--without-python \
--without-php \
--enable-default-shared \
--enable-shared \
--enable-image \
--enable-libusb \
--disable-acl \
--disable-dnssd \
--disable-dbus \
--disable-avahi \
--disable-launchd \
--disable-ldap \
--disable-pam \
--disable-slp \
--disable-gnutls \
--disable-openssl \
--disable-cdsassl \
--disable-ssl \
--disable-gssapi \
--disable-tiff, \
UNAME="Linux" \
LIBS="$(TARGET_LDFLAGS) -lz -lpng -ljpeg" \
)
endef
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) \
$(TARGET_CONFIGURE_OPTS) \
DSTROOT="$(PKG_INSTALL_DIR)" \
STRIP="/bin/true" \
all install
endef
define Package/cups/install
rm -rf $(1)/etc/cups
$(INSTALL_DIR) $(1)/etc/cups
$(CP) $(PKG_INSTALL_DIR)/etc/cups/* $(1)/etc/cups/
rm -rf $(1)/etc/cups/certs
ln -sf /tmp $(1)/etc/cups/certs
rm -f $(1)/usr/bin/cups-config
$(INSTALL_DIR) $(1)/usr/lib/cups
$(CP) $(PKG_INSTALL_DIR)/usr/lib/cups/backend $(1)/usr/lib/cups
$(CP) $(PKG_INSTALL_DIR)/usr/lib/cups/cgi-bin $(1)/usr/lib/cups
$(CP) $(PKG_INSTALL_DIR)/usr/lib/cups/daemon $(1)/usr/lib/cups
$(CP) $(PKG_INSTALL_DIR)/usr/lib/cups/driver $(1)/usr/lib/cups
$(CP) $(PKG_INSTALL_DIR)/usr/lib/cups/monitor $(1)/usr/lib/cups
$(INSTALL_DIR) $(1)/usr/lib/cups/filter
$(CP) $(PKG_INSTALL_DIR)/usr/lib/cups/filter/{commandtops,pstops} \
$(PKG_INSTALL_DIR)/usr/lib/cups/filter/gziptoany \
$(1)/usr/lib/cups/filter
$(INSTALL_DIR) $(1)/usr/share/cups/templates
$(CP) $(PKG_INSTALL_DIR)/usr/share/cups/templates/*.tmpl \
$(1)/usr/share/cups/templates/
$(INSTALL_DIR) $(1)/usr/share/cups/mime
$(CP) $(PKG_INSTALL_DIR)/usr/share/cups/mime/* $(1)/usr/share/cups/mime/
### Do install docs: cups web interface relies on them
$(INSTALL_DIR) $(1)/usr/share/doc/cups
$(CP) $(PKG_INSTALL_DIR)/usr/share/doc/cups/*.*html \
$(1)/usr/share/doc/cups/
$(CP) $(PKG_INSTALL_DIR)/usr/share/doc/cups/*.css \
$(1)/usr/share/doc/cups/
$(CP) $(PKG_INSTALL_DIR)/usr/share/doc/cups/*.txt \
$(1)/usr/share/doc/cups/
$(CP) $(PKG_INSTALL_DIR)/usr/share/doc/cups/images \
$(1)/usr/share/doc/cups/
$(INSTALL_DIR) $(1)/usr/sbin
# overwrite default config with our own
$(CP) ./files/etc/cups/* $(1)/etc/cups/
# install initscript with priority 60
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/cupsd.init $(1)/etc/init.d/cupsd
$(INSTALL_BIN) \
$(PKG_INSTALL_DIR)/usr/sbin/{cupsctl,cupsd} \
$(1)/usr/sbin/
# needed for cups to find usb printers per http://wiki.openwrt.org/doc/howto/cups.server
chmod 700 $(1)/usr/lib/cups/backend/usb
endef
define Package/cups-bsd/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/{lprm,lpq,lpr} $(1)/usr/bin/
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/lpc $(1)/usr/sbin/
endef
define Package/cups-client/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) \
$(PKG_INSTALL_DIR)/usr/bin/{lp,cancel,cupstestppd,cupstestdsc} \
$(PKG_INSTALL_DIR)/usr/bin/{ipptool,lpoptions,lpstat} \
$(1)/usr/bin/
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) \
$(PKG_INSTALL_DIR)/usr/sbin/{cupsaccept,cupsaddsmb,cupsfilter} \
$(PKG_INSTALL_DIR)/usr/sbin/{lpadmin,lpinfo,lpmove} \
$(1)/usr/sbin/
(cd $(1)/usr/sbin; ln -sf cupsaccept accept; ln -sf cupsaccept cupsenable; ln -sf cupsaccept cupsdisable; ln -sf cupsaccept reject; ln -sf cupsaccept cupsreject;)
endef
define Package/cups-filters/install
$(INSTALL_DIR) $(1)/usr/lib/cups/filter
$(CP) $(PKG_INSTALL_DIR)/usr/lib/cups/filter/{commandtops,gziptoany,pstops,rastertoepson,rastertohp,rastertolabel,rastertopwg} \
$(1)/usr/lib/cups/filter
(cd $(1)/usr/lib/cups/filter; ln -sf rastertolabel rastertodymo;)
endef
define Package/cups-ppdc/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) \
$(PKG_INSTALL_DIR)/usr/bin/{ppdc,ppdhtml,ppdi,ppdmerge,ppdpo} \
$(1)/usr/bin/
endef
define Package/libcups/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libcups.so* $(1)/usr/lib/
endef
define Package/libcupscgi/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libcupscgi.so* $(1)/usr/lib/
endef
define Package/libcupsimage/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libcupsimage.so* $(1)/usr/lib/
endef
define Package/libcupsmime/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libcupsmime.so* $(1)/usr/lib/
endef
define Package/libcupsppdc/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libcupsppdc.so* $(1)/usr/lib/
endef
define Build/InstallDev
$(INSTALL_DIR) $(2)/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/cups-config $(2)/bin/
$(INSTALL_DIR) $(1)/usr/include
$(CP) $(PKG_INSTALL_DIR)/usr/include/cups $(1)/usr/include/
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libcups*.so* $(1)/usr/lib/
endef
$(eval $(call BuildPackage,cups))
$(eval $(call BuildPackage,libcups))
$(eval $(call BuildPackage,libcupscgi))
$(eval $(call BuildPackage,libcupsimage))
$(eval $(call BuildPackage,libcupsmime))
$(eval $(call BuildPackage,libcupsppdc))
$(eval $(call BuildPackage,cups-bsd))
$(eval $(call BuildPackage,cups-client))
$(eval $(call BuildPackage,cups-filters))
$(eval $(call BuildPackage,cups-ppdc))

View File

@ -0,0 +1,19 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2006-2011 OpenWrt.org
START=50
start() {
mkdir -m 0755 -p /var/cache/cups
mkdir -m 0755 -p /var/cups
mkdir -m 0755 -p /var/spool/cups/tmp
service_start /usr/sbin/cupsd
}
stop() {
service_stop /usr/sbin/cupsd
}
reload() {
service_reload /usr/sbin/cupsd
}

View File

@ -0,0 +1,7 @@
########################################################################
# #
# This is a sample class configuration file. This file is included #
# from the main configuration file (cups.conf) and lists all of the #
# printer classes known to the system. #
# #
########################################################################

View File

@ -0,0 +1,9 @@
########################################################################
# #
# This is the CUPS client configuration file. This file is used to #
# define client-specific parameters, such as the default server or #
# default encryption settings. #
# #
########################################################################
Encryption Never

View File

@ -0,0 +1,58 @@
########################################################################
# #
# This is the CUPS configuration file. If you are familiar with #
# Apache or any of the other popular web servers, we've followed the #
# same format. Any configuration variable used here has the same #
# semantics as the corresponding variable in Apache. If we need #
# different functionality then a different name is used to avoid #
# confusion... #
# #
########################################################################
AccessLog syslog
ErrorLog syslog
LogLevel info
PageLog syslog
PreserveJobHistory No
PreserveJobFiles No
AutoPurgeJobs Yes
MaxJobs 25
MaxPrinterHistory 10
#Printcap /etc/printcap
#PrintcapFormat BSD
RequestRoot /var/cups
#RemoteRoot remroot
#User nobody
#Group nogroup
# root permissions required to make cups work with the usb backend
User root
Group root
RIPCache auto
TempDir /var/cups
Port 631
HostNameLookups On
KeepAlive On
# No: "BrowseOrder" "BrowseAllow" "BrowseRemoteProtocols"
Browsing Yes
BrowsingWebIF Yes
BrowseLocalProtocols DNSSD
DefaultShared Yes
WebInterface no
Listen /var/run/cups/cups.sock
ServerAlias *
<Location />
Order Allow,Deny
Allow From 127.0.0.1
Allow From 192.168.1.0/24
</Location>
<Location /admin>
AuthType Basic
AuthClass System
Order Allow,Deny
Allow From 127.0.0.1
Allow From 192.168.1.0/24
</Location>

View File

@ -0,0 +1,23 @@
<DefaultPrinter USB>
Info USB Printer
Location
DeviceURI usb:/dev/usb/lp0
State Idle
Accepting Yes
JobSheets none none
QuotaPeriod 0
PageLimit 0
KLimit 0
</Printer>
<Printer LP>
Info Parallel Port Printer
Location
DeviceURI parallel:/dev/printers/0
State Idle
Accepting Yes
JobSheets none none
QuotaPeriod 0
PageLimit 0
KLimit 0
</Printer>

View File

@ -0,0 +1,11 @@
--- a/configure
+++ b/configure
@@ -5896,7 +5896,7 @@ fi
case "$COMPONENTS" in
all)
- BUILDDIRS="filter backend berkeley cgi-bin monitor notifier ppdc scheduler systemv conf data desktop locale man doc examples templates"
+ BUILDDIRS="filter backend berkeley cgi-bin monitor ppdc scheduler systemv conf data desktop locale doc examples templates"
;;
core)

View File

@ -0,0 +1,11 @@
--- a/configure
+++ b/configure
@@ -1966,7 +1966,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
-uname=`uname`
+uname=${UNAME:-`uname`}
uversion=`uname -r | sed -e '1,$s/^[^0-9]*\([0-9]*\)\.\([0-9]*\).*/\1\2/'`
uarch=`uname -m`

View File

@ -0,0 +1,33 @@
--- a/config-scripts/cups-directories.m4
+++ b/config-scripts/cups-directories.m4
@@ -104,7 +104,7 @@ if test "$libdir" = "\${exec_prefix}/lib
case "$uname" in
Linux*)
if test -d /usr/lib64 -a ! -d /usr/lib64/fakeroot; then
- libdir="$exec_prefix/lib64"
+ libdir="$exec_prefix/lib"
fi
;;
esac
--- a/configure
+++ b/configure
@@ -5998,7 +5998,7 @@ if test "$libdir" = "\${exec_prefix}/lib
case "$uname" in
Linux*)
if test -d /usr/lib64 -a ! -d /usr/lib64/fakeroot; then
- libdir="$exec_prefix/lib64"
+ libdir="$exec_prefix/lib"
fi
;;
esac
--- a/cups-config.in
+++ b/cups-config.in
@@ -50,7 +50,7 @@ else
CFLAGS="$CFLAGS -I$includedir"
fi
- if test $libdir != /usr/lib -a $libdir != /usr/lib32 -a $libdir != /usr/lib64; then
+ if test $libdir != /usr/lib -a $libdir != /usr/lib -a $libdir != /usr/lib; then
LDFLAGS="$LDFLAGS -L$libdir"
fi
fi

View File

@ -0,0 +1,17 @@
--- a/ppdc/Makefile
+++ b/ppdc/Makefile
@@ -238,13 +238,7 @@ framedhelp:
genstrings: genstrings.o libcupsppdc.a ../cups/$(LIBCUPSSTATIC) \
sample.drv ../data/media.defs
- echo Linking $@...
- $(CXX) $(ARCHFLAGS) $(LDFLAGS) -o genstrings genstrings.o \
- libcupsppdc.a ../cups/$(LIBCUPSSTATIC) $(LIBGSSAPI) $(SSLLIBS) \
- $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
- echo Generating localization strings...
- ./genstrings >sample.c
-
+ echo Not Linking $@...
#
# ppdc, the PPD compiler.

View File

@ -0,0 +1,77 @@
#
# Copyright (C) 2014-2018 OpenWrt-dist
# Copyright (C) 2019 [CTCGFW] Project OpenWRT
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=filebrowser
PKG_VERSION:=snapshot
PKG_RELEASE:=6ce44f7
PKG_LICENSE:=GPLv3
PKG_MAINTAINER:=[CTCGFW] Project OpenWRT
ifeq ($(ARCH),x86_64)
PKG_ARCH_filebrowser:=linux_amd64
endif
ifeq ($(ARCH),i386)
PKG_ARCH_filebrowser:=linux_386
endif
ifeq ($(ARCH),mipsel)
PKG_ARCH_filebrowser:=linux_mipsle
endif
ifeq ($(ARCH),mips)
PKG_ARCH_filebrowser:=linux_mips
endif
ifeq ($(ARCH),arm)
ifeq ($(BOARD),kirkwood)
PKG_ARCH_filebrowser:=linux_arm5
else ifeq ($(BOARD),bcm53xx)
PKG_ARCH_filebrowser:=linux_arm5
else
PKG_ARCH_filebrowser:=linux_arm7
endif
endif
ifeq ($(ARCH),aarch64)
PKG_ARCH_filebrowser:=linux_arm64
endif
PKG_SOURCE:=filebrowser_$(PKG_ARCH_filebrowser).tar.gz
PKG_SOURCE_URL:=https://package.project-openwrt.eu.org/utils/filebrowser/$(PKG_RELEASE)/
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE)
PKG_HASH:=skip
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=utils
CATEGORY:=Utilities
TITLE:=OpenWrt Support for FileBrowser.
URL:=https://github.com/filebrowser/filebrowser
endef
define Package/$(PKG_NAME)/description
OpenWrt Support for FileBrowser.
endef
define Build/Prepare
tar -xzf $(DL_DIR)/$(PKG_SOURCE) -C $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
chmod 0755 $(PKG_BUILD_DIR)/filebrowser
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/filebrowser $(1)/usr/bin/filebrowser
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -0,0 +1,50 @@
# Copyright 2019 Shun Li <riverscn@gmail.com>
# Licensed to the public under the GNU General Public License v3.
include $(TOPDIR)/rules.mk
PKG_NAME:=iptvhelper
PKG_VERSION:=0.1.1
PKG_RELEASE:=1
PKG_MAINTAINER:=Shun Li <riverscn@gmail.com>
PKG_LICENSE:=GPL-3.0
include $(INCLUDE_DIR)/package.mk
define Package/iptvhelper
SECTION:=net
CATEGORY:=Network
SUBMENU:=Routing and Redirection
DEPENDS:= \
+ipset \
+iptables
TITLE:=Scripts for configure IPTV easily
MAINTAINER:=Shun Li <riverscn@gmail.com>
PKGARCH:=all
endef
define Package/iptvhelper/description
Scripts for configure IPTV easily
endef
define Package/iptvhelper/conffiles
/etc/config/iptvhelper
/etc/firewall.iptvhelper
endef
define Build/Compile
endef
define Package/iptvhelper/postinst
#!/bin/sh
endef
define Package/iptvhelper/postrm
#!/bin/sh
endef
define Package/iptvhelper/install
$(CP) ./files/* $(1)
endef
$(eval $(call BuildPackage,iptvhelper))

View File

@ -0,0 +1,8 @@
config tvbox 'default'
option disabled '1'
option respawn '1'
option mac '00:00:00:00:00:00'
option ipset '1'
option dns_redir '1'

View File

@ -0,0 +1,2 @@
#!/bin/sh
test -s "/etc/init.d/iptvhelper" && /etc/init.d/iptvhelper reload

View File

@ -0,0 +1,127 @@
#!/bin/sh /etc/rc.common
# Copyright 2019 Shun Li <riverscn@gmail.com>
# Licensed to the public under the GNU General Public License v3.
START=95
USE_PROCD=1
_ipt() {
cmd="$(echo "$@" | sed 's|-A|-D|g;s|-I|-D|g')"
while iptables $cmd &>/dev/null; do
:
done
iptables $@
}
destroy_iptv_ipset() {
for ip_set in $(ipset list | awk '/iptvhelper/ {print $2}'); do ipset destroy $ip_set; done
}
clear_iptv_rules() {
iptables-save --counters | grep -v "iptvhelper-rule" | iptables-restore --counters
}
append_arg() {
local cfg="$1"
local var="$2"
local opt="$3"
local def="$4"
local val
config_get val "$cfg" "$var"
[ -n "$val" -o -n "$def" ] && procd_append_param command $opt "${val:-$def}"
}
append_bool() {
local cfg="$1"
local var="$2"
local opt="$3"
local def="$4"
local val
config_get_bool val "$cfg" "$var" "$def"
[ "$val" = 1 ] && procd_append_param command "$opt"
}
start_instance() {
local cfg="$1"
local aux
config_get_bool aux "$cfg" 'disabled' '0'
[ "$aux" = 1 ] && return 1
logger "iptvhelper.$cfg: instance starting"
local IPTV_MAC
local IPTV_DNS_REDIR
local IPTV_IPSET
config_get IPTV_MAC $cfg mac
config_get IPTV_DNS_REDIR $cfg dns_redir
config_get IPTV_IPSET $cfg ipset
logger "iptvhelper.$cfg: topbox mac=$IPTV_MAC"
if [[ $IPTV_DNS_REDIR == '1' ]]; then
logger "iptvhelper.$cfg: topbox DNS redierct enabled"
_ipt -t nat -A PREROUTING -m mac --mac-source $IPTV_MAC -m comment --comment "iptvhelper-rule" -p udp --dport 53 -j REDIRECT --to-ports 53
fi
if [[ $IPTV_IPSET='1' ]]; then
logger "iptvhelper.$cfg: topbox ipset enabled"
if ! ipset -q list iptvhelper_$cfg >/dev/null; then
ipset create iptvhelper_$cfg nethash
fi
_ipt -t nat -I PREROUTING -m mac --mac-source $IPTV_MAC -m comment --comment "iptvhelper-rule" -m set ! --match-set iptvhelper_$cfg dst -m conntrack --ctstate NEW -j LOG --log-prefix "iptvhelper.$cfg:"
procd_open_instance
procd_set_param command /usr/sbin/iptvhelper.sh $cfg
config_get_bool aux "$cfg" 'respawn' '0'
[ "$aux" = 1 ] && procd_set_param respawn
procd_set_param pidfile /var/run/iptvhelper_$cfg.pid
procd_close_instance
fi
}
service_triggers() {
procd_add_reload_trigger "iptvhelper"
}
start_service() {
logger "iptvhelper: starting"
echo "iptvhelper: starting"
config_load iptvhelper
config_foreach start_instance tvbox
if uci -q show firewall >/dev/null; then
if ! uci -q get firewall.iptvhelper >/dev/null; then
uci -q batch <<-EOF >/dev/null
set firewall.iptvhelper=include
set firewall.iptvhelper.type='script'
set firewall.iptvhelper.path='/etc/firewall.iptvhelper'
set firewall.iptvhelper.family='any'
set firewall.iptvhelper.reload='1'
commit firewall
EOF
fi
fi
}
stop_service() {
if uci -q show firewall >/dev/null; then
if uci -q get firewall.iptvhelper >/dev/null; then
uci delete firewall.iptvhelper
uci commit firewall
fi
fi
for pid in $(ps | grep -v awk | awk '/iptvhelper.sh/ {print $1}'); do kill -9 $pid; done
for pid in $(ps | grep -v awk | awk '/logread -e iptvhelper/ {print $1}'); do kill -9 $pid; done
clear_iptv_rules
destroy_iptv_ipset
logger "iptvhelper: stopped"
echo "iptvhelper: stopped"
}
reload_service() {
logger "iptvhelper: reloading"
echo "iptvhelper: reloading"
stop
start
}

View File

@ -0,0 +1,24 @@
#!/bin/sh
# Copyright 2019 Shun Li <riverscn@gmail.com>
# Licensed to the public under the GNU General Public License v3.
add_to_set() {
local ip=$1
local cfg=$2
local subnet=$ip/24
ping -W1 -c1 $ip &>/dev/null && return
if ! ipset -q test iptvhelper_$cfg $subnet; then
ipset add iptvhelper_$cfg $subnet
echo added $subnet to set iptvhelper_$cfg
fi
}
echo $1
logread -e "iptvhelper\.$1" -f |
while read line; do
ip=$(echo "$line" | sed -r 's|.*DST=([0-9.]+).*|\1|')
echo requested $ip
add_to_set $ip $1 &
done

View File

@ -0,0 +1,75 @@
# Copyright (C) 2018-2019 Lienol
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-adguardhome
PKG_VERSION:=1.8
PKG_RELEASE:=11
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/luci-app-adguardhome
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=LuCI app for adguardhome
PKG_MAINTAINER:=<https://github.com/rufengsuixing/luci-app-adguardhome>
PKGARCH:=all
DEPENDS:=+!wget&&!curl:wget
endef
define Package/luci-app-adguardhome/description
LuCI support for adguardhome
endef
define Build/Prepare
endef
define Build/Compile
endef
define Package/luci-app-adguardhome/conffiles
/usr/share/AdGuardHome/links.txt
/etc/config/AdGuardHome
endef
define Package/luci-app-adguardhome/install
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
cp -pR ./luasrc/* $(1)/usr/lib/lua/luci
$(INSTALL_DIR) $(1)/
cp -pR ./root/* $(1)/
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
po2lmo ./po/zh-cn/AdGuardHome.po $(1)/usr/lib/lua/luci/i18n/AdGuardHome.zh-cn.lmo
endef
define Package/luci-app-adguardhome/postinst
#!/bin/sh
/etc/init.d/AdGuardHome enable >/dev/null 2>&1
enable=$(uci get AdGuardHome.AdGuardHome.enabled 2>/dev/null)
if [ "$enable" == "1" ]; then
/etc/init.d/AdGuardHome reload
fi
rm -f /tmp/luci-indexcache
rm -f /tmp/luci-modulecache/*
exit 0
endef
define Package/luci-app-adguardhome/prerm
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
/etc/init.d/AdGuardHome disable
/etc/init.d/AdGuardHome stop
uci -q batch <<-EOF >/dev/null 2>&1
delete ucitrack.@AdGuardHome[-1]
commit ucitrack
EOF
fi
exit 0
endef
$(eval $(call BuildPackage,luci-app-adguardhome))

View File

@ -0,0 +1,127 @@
module("luci.controller.AdGuardHome",package.seeall)
local fs=require"nixio.fs"
local http=require"luci.http"
local uci=require"luci.model.uci".cursor()
function index()
entry({"admin", "services", "AdGuardHome"},alias("admin", "services", "AdGuardHome", "base"),_("AdGuard Home"), 10).dependent = true
entry({"admin","services","AdGuardHome","base"},cbi("AdGuardHome/base"),_("Base Setting"),1).leaf = true
entry({"admin","services","AdGuardHome","log"},form("AdGuardHome/log"),_("Log"),2).leaf = true
entry({"admin","services","AdGuardHome","manual"},cbi("AdGuardHome/manual"),_("Manual Config"),3).leaf = true
entry({"admin","services","AdGuardHome","status"},call("act_status")).leaf=true
entry({"admin", "services", "AdGuardHome", "check"}, call("check_update"))
entry({"admin", "services", "AdGuardHome", "doupdate"}, call("do_update"))
entry({"admin", "services", "AdGuardHome", "getlog"}, call("get_log"))
entry({"admin", "services", "AdGuardHome", "dodellog"}, call("do_dellog"))
entry({"admin", "services", "AdGuardHome", "reloadconfig"}, call("reload_config"))
entry({"admin", "services", "AdGuardHome", "gettemplateconfig"}, call("get_template_config"))
end
function get_template_config()
local b
local d=""
for cnt in io.lines("/tmp/resolv.conf.auto") do
b=string.match (cnt,"^[^#]*nameserver%s+([^%s]+)$")
if (b~=nil) then
d=d.." - "..b.."\n"
end
end
local f=io.open("/usr/share/AdGuardHome/AdGuardHome_template.yaml", "r+")
local tbl = {}
local a=""
while (1) do
a=f:read("*l")
if (a=="#bootstrap_dns") then
a=d
elseif (a=="#upstream_dns") then
a=d
elseif (a==nil) then
break
end
table.insert(tbl, a)
end
f:close()
http.prepare_content("text/plain; charset=utf-8")
http.write(table.concat(tbl, "\n"))
end
function reload_config()
fs.remove("/tmp/AdGuardHometmpconfig.yaml")
http.prepare_content("application/json")
http.write('')
end
function act_status()
local e={}
local binpath=uci:get("AdGuardHome","AdGuardHome","binpath")
e.running=luci.sys.call("pgrep "..binpath.." >/dev/null")==0
e.redirect=(fs.readfile("/var/run/AdGredir")=="1")
http.prepare_content("application/json")
http.write_json(e)
end
function do_update()
fs.writefile("/var/run/lucilogpos","0")
http.prepare_content("application/json")
http.write('')
local arg
if luci.http.formvalue("force") == "1" then
arg="force"
else
arg=""
end
if fs.access("/var/run/update_core") then
if arg=="force" then
luci.sys.exec("kill $(pgrep /usr/share/AdGuardHome/update_core.sh) ; sh /usr/share/AdGuardHome/update_core.sh "..arg.." >/tmp/AdGuardHome_update.log 2>&1 &")
end
else
luci.sys.exec("sh /usr/share/AdGuardHome/update_core.sh "..arg.." >/tmp/AdGuardHome_update.log 2>&1 &")
end
end
function get_log()
local logfile=uci:get("AdGuardHome","AdGuardHome","logfile")
if (logfile==nil) then
http.write("no log available\n")
return
elseif (logfile=="syslog") then
if not fs.access("/var/run/AdGuardHomesyslog") then
luci.sys.exec("(/usr/share/AdGuardHome/getsyslog.sh &); sleep 1;")
end
logfile="/tmp/AdGuardHometmp.log"
fs.writefile("/var/run/AdGuardHomesyslog","1")
elseif not fs.access(logfile) then
http.write("")
return
end
http.prepare_content("text/plain; charset=utf-8")
local fdp
if fs.access("/var/run/lucilogreload") then
fdp=0
fs.remove("/var/run/lucilogreload")
else
fdp=tonumber(fs.readfile("/var/run/lucilogpos")) or 0
end
local f=io.open(logfile, "r+")
f:seek("set",fdp)
local a=f:read(2048000) or ""
fdp=f:seek()
fs.writefile("/var/run/lucilogpos",tostring(fdp))
f:close()
http.write(a)
end
function do_dellog()
local logfile=uci:get("AdGuardHome","AdGuardHome","logfile")
fs.writefile(logfile,"")
http.prepare_content("application/json")
http.write('')
end
function check_update()
http.prepare_content("text/plain; charset=utf-8")
local fdp=tonumber(fs.readfile("/var/run/lucilogpos")) or 0
local f=io.open("/tmp/AdGuardHome_update.log", "r+")
f:seek("set",fdp)
local a=f:read(2048000) or ""
fdp=f:seek()
fs.writefile("/var/run/lucilogpos",tostring(fdp))
f:close()
if fs.access("/var/run/update_core") then
http.write(a)
else
http.write(a.."\0")
end
end

View File

@ -0,0 +1,304 @@
require("luci.sys")
require("luci.util")
require("io")
local m,s,o,o1
local fs=require"nixio.fs"
local uci=require"luci.model.uci".cursor()
local configpath=uci:get("AdGuardHome","AdGuardHome","configpath") or "/etc/AdGuardHome.yaml"
local binpath=uci:get("AdGuardHome","AdGuardHome","binpath") or "/usr/bin/AdGuardHome/AdGuardHome"
httpport=uci:get("AdGuardHome","AdGuardHome","httpport") or "3000"
m = Map("AdGuardHome", "AdGuard Home")
m.description = translate("Free and open source, powerful network-wide ads & trackers blocking DNS server.")
m:section(SimpleSection).template = "AdGuardHome/AdGuardHome_status"
s = m:section(TypedSection, "AdGuardHome")
s.anonymous=true
s.addremove=false
---- enable
o = s:option(Flag, "enabled", translate("Enable"))
o.default = 0
o.optional = false
---- httpport
o =s:option(Value,"httpport",translate("Browser management port"))
o.placeholder=3000
o.default=3000
o.datatype="port"
o.optional = false
o.description = translate("<input type=\"button\" style=\"width:210px;border-color:Teal; text-align:center;font-weight:bold;color:Green;\" value=\"AdGuardHome Web:"..httpport.."\" onclick=\"window.open('http://'+window.location.hostname+':"..httpport.."/')\"/>")
---- update warning not safe
local binmtime=uci:get("AdGuardHome","AdGuardHome","binmtime") or "0"
local e=""
if not fs.access(configpath) then
e=e.." "..translate("no config")
end
if not fs.access(binpath) then
e=e.." "..translate("no core")
else
local version=uci:get("AdGuardHome","AdGuardHome","version")
local testtime=fs.stat(binpath,"mtime")
if testtime~=tonumber(binmtime) or version==nil then
local tmp=luci.sys.exec(binpath.." -c /dev/null --check-config 2>&1| grep -m 1 -E 'v[0-9.]+' -o")
version=string.sub(tmp, 1, -2)
if version=="" then version="core error" end
uci:set("AdGuardHome","AdGuardHome","version",version)
uci:set("AdGuardHome","AdGuardHome","binmtime",testtime)
uci:save("AdGuardHome")
end
e=version..e
end
o=s:option(Button,"restart",translate("Update"))
o.inputtitle=translate("Update core version")
o.template = "AdGuardHome/AdGuardHome_check"
o.showfastconfig=(not fs.access(configpath))
o.description=string.format(translate("core version:").."<strong><font id=\"updateversion\" color=\"green\">%s </font></strong>",e)
---- port warning not safe
local port=luci.sys.exec("awk '/ port:/{printf($2);exit;}' "..configpath.." 2>nul")
if (port=="") then port="?" end
---- Redirect
o = s:option(ListValue, "redirect", port..translate("Redirect"), translate("AdGuardHome redirect mode"))
o.placeholder = "none"
o:value("none", translate("none"))
o:value("dnsmasq-upstream", translate("Run as dnsmasq upstream server"))
o:value("redirect", translate("Redirect 53 port to AdGuardHome"))
o:value("exchange", translate("Use port 53 replace dnsmasq"))
o.default = "none"
o.optional = true
---- bin path
o = s:option(Value, "binpath", translate("Bin Path"), translate("AdGuardHome Bin path if no bin will auto download"))
o.default = "/usr/bin/AdGuardHome/AdGuardHome"
o.datatype = "string"
o.optional = false
o.rmempty=false
o.validate=function(self, value)
if value=="" then return nil end
if fs.stat(value,"type")=="dir" then
fs.rmdir(value)
end
if fs.stat(value,"type")=="dir" then
if (m.message) then
m.message =m.message.."\nerror!bin path is a dir"
else
m.message ="error!bin path is a dir"
end
return nil
end
return value
end
--- upx
o = s:option(ListValue, "upxflag", translate("use upx to compress bin after download"))
o:value("", translate("none"))
o:value("-1", translate("compress faster"))
o:value("-9", translate("compress better"))
o:value("--best", translate("compress best(can be slow for big files)"))
o:value("--brute", translate("try all available compression methods & filters [slow]"))
o:value("--ultra-brute", translate("try even more compression variants [very slow]"))
o.default = ""
o.description=translate("bin use less space,but may have compatibility issues")
o.rmempty = true
---- config path
o = s:option(Value, "configpath", translate("Config Path"), translate("AdGuardHome config path"))
o.default = "/etc/AdGuardHome.yaml"
o.datatype = "string"
o.optional = false
o.rmempty=false
o.validate=function(self, value)
if value==nil then return nil end
if fs.stat(value,"type")=="dir" then
fs.rmdir(value)
end
if fs.stat(value,"type")=="dir" then
if m.message then
m.message =m.message.."\nerror!config path is a dir"
else
m.message ="error!config path is a dir"
end
return nil
end
return value
end
---- work dir
o = s:option(Value, "workdir", translate("Work dir"), translate("AdGuardHome work dir include rules,audit log and database"))
o.default = "/usr/bin/AdGuardHome"
o.datatype = "string"
o.optional = false
o.rmempty=false
o.validate=function(self, value)
if value=="" then return nil end
if fs.stat(value,"type")=="reg" then
if m.message then
m.message =m.message.."\nerror!work dir is a file"
else
m.message ="error!work dir is a file"
end
return nil
end
if string.sub(value, -1)=="/" then
return string.sub(value, 1, -2)
else
return value
end
end
---- log file
o = s:option(Value, "logfile", translate("Runtime log file"), translate("AdGuardHome runtime Log file if 'syslog': write to system log;if empty no log"))
o.datatype = "string"
o.rmempty = true
o.validate=function(self, value)
if fs.stat(value,"type")=="dir" then
fs.rmdir(value)
end
if fs.stat(value,"type")=="dir" then
if m.message then
m.message =m.message.."\nerror!log file is a dir"
else
m.message ="error!log file is a dir"
end
return nil
end
return value
end
---- debug
o = s:option(Flag, "verbose", translate("Verbose log"))
o.default = 0
o.optional = true
---- gfwlist
local a=luci.sys.call("grep -m 1 -q programadd "..configpath)
if (a==0) then
a="Added"
else
a="Not added"
end
o=s:option(Button,"gfwdel",translate("Del gfwlist"),translate(a))
o.optional = true
o.inputtitle=translate("Del")
o.write=function()
luci.sys.exec("sh /usr/share/AdGuardHome/gfw2adg.sh del 2>&1")
luci.http.redirect(luci.dispatcher.build_url("admin","services","AdGuardHome"))
end
o=s:option(Button,"gfwadd",translate("Add gfwlist"),translate(a))
o.optional = true
o.inputtitle=translate("Add")
o.write=function()
luci.sys.exec("sh /usr/share/AdGuardHome/gfw2adg.sh 2>&1")
luci.http.redirect(luci.dispatcher.build_url("admin","services","AdGuardHome"))
end
o = s:option(Value, "gfwupstream", translate("Gfwlist upstream dns server"), translate("Gfwlist domain upstream dns service")..translate(a))
o.default = "tcp://208.67.220.220:5353"
o.datatype = "string"
o.optional = true
---- chpass
o = s:option(Value, "hashpass", translate("Change browser management password"), translate("Press load culculate model and culculate finally save/apply"))
o.default = ""
o.datatype = "string"
o.template = "AdGuardHome/AdGuardHome_chpass"
o.optional = true
---- upgrade protect
o = s:option(MultiValue, "upprotect", translate("Keep files when system upgrade"))
o:value("$binpath",translate("core bin"))
o:value("$configpath",translate("config file"))
o:value("$logfile",translate("log file"))
o:value("$workdir/data/sessions.db",translate("sessions.db"))
o:value("$workdir/data/stats.db",translate("stats.db"))
o:value("$workdir/data/querylog.json",translate("querylog.json"))
o:value("$workdir/data/filters",translate("filters"))
o.widget = "checkbox"
o.default = nil
o.optional=true
---- wait net on boot
o = s:option(Flag, "waitonboot", translate("On boot when network ok restart"))
o.default = 1
o.optional = true
---- backup workdir on shutdown
local workdir=uci:get("AdGuardHome","AdGuardHome","workdir") or "/usr/bin/AdGuardHome"
o = s:option(MultiValue, "backupfile", translate("Backup workdir files when shutdown"))
o1 = s:option(Value, "backupwdpath", translate("Backup workdir path"))
local name
o:value("filters","filters")
o:value("stats.db","stats.db")
o:value("querylog.json","querylog.json")
o:value("sessions.db","sessions.db")
o1:depends ("backupfile", "filters")
o1:depends ("backupfile", "stats.db")
o1:depends ("backupfile", "querylog.json")
o1:depends ("backupfile", "sessions.db")
for name in fs.glob(workdir.."/data/*")
do
name=fs.basename (name)
if name~="filters" and name~="stats.db" and name~="querylog.json" and name~="sessions.db" then
o:value(name,name)
o1:depends ("backupfile", name)
end
end
o.widget = "checkbox"
o.default = nil
o.optional=false
o.description=translate("Will be restore when workdir/data is empty")
----backup workdir path
o1.default = "/usr/bin/AdGuardHome"
o1.datatype = "string"
o1.optional = false
o1.validate=function(self, value)
if fs.stat(value,"type")=="reg" then
if m.message then
m.message =m.message.."\nerror!backup dir is a file"
else
m.message ="error!backup dir is a file"
end
return nil
end
if string.sub(value,-1)=="/" then
return string.sub(value, 1, -2)
else
return value
end
end
----Crontab
o = s:option(MultiValue, "crontab", translate("Crontab task"),translate("Please change time and args in crontab"))
o:value("autoupdate",translate("Auto update core"))
o:value("cutquerylog",translate("Auto tail querylog"))
o:value("cutruntimelog",translate("Auto tail runtime log"))
o:value("autohost",translate("Auto update ipv6 hosts and restart adh"))
o:value("autogfw",translate("Auto update gfwlist and restart adh"))
o.widget = "checkbox"
o.default = nil
o.optional=true
----downloadpath
o = s:option(TextValue, "downloadlinks",translate("Download links for update"))
o.optional = false
o.rows = 4
o.wrap = "soft"
o.cfgvalue = function(self, section)
return fs.readfile("/usr/share/AdGuardHome/links.txt")
end
o.write = function(self, section, value)
fs.writefile("/usr/share/AdGuardHome/links.txt", value:gsub("\r\n", "\n"))
end
fs.writefile("/var/run/lucilogpos","0")
function m.on_commit(map)
if (fs.access("/var/run/AdGserverdis")) then
io.popen("/etc/init.d/AdGuardHome reload &")
return
end
local ucitracktest=uci:get("AdGuardHome","AdGuardHome","ucitracktest")
if ucitracktest=="1" then
return
elseif ucitracktest=="0" then
io.popen("/etc/init.d/AdGuardHome reload &")
else
if (fs.access("/var/run/AdGlucitest")) then
uci:set("AdGuardHome","AdGuardHome","ucitracktest","0")
io.popen("/etc/init.d/AdGuardHome reload &")
else
fs.writefile("/var/run/AdGlucitest","")
if (ucitracktest=="2") then
uci:set("AdGuardHome","AdGuardHome","ucitracktest","1")
else
uci:set("AdGuardHome","AdGuardHome","ucitracktest","2")
end
end
uci:save("AdGuardHome")
end
end
return m

View File

@ -0,0 +1,16 @@
local fs=require"nixio.fs"
local uci=require"luci.model.uci".cursor()
local f,t
f=SimpleForm("logview")
f.reset = false
f.submit = false
t=f:field(TextValue,"conf")
t.rmempty=true
t.rows=20
t.template="AdGuardHome/log"
t.readonly="readonly"
local logfile=uci:get("AdGuardHome","AdGuardHome","logfile") or ""
t.timereplace=(logfile~="syslog" and logfile~="" )
t.pollcheck=logfile~=""
fs.writefile("/var/run/lucilogreload","")
return f

View File

@ -0,0 +1,97 @@
local m, s, o
local fs = require "nixio.fs"
local uci=require"luci.model.uci".cursor()
local sys=require"luci.sys"
require("string")
require("io")
require("table")
function gen_template_config()
local b
local d=""
for cnt in io.lines("/tmp/resolv.conf.auto") do
b=string.match (cnt,"^[^#]*nameserver%s+([^%s]+)$")
if (b~=nil) then
d=d.." - "..b.."\n"
end
end
local f=io.open("/usr/share/AdGuardHome/AdGuardHome_template.yaml", "r+")
local tbl = {}
local a=""
while (1) do
a=f:read("*l")
if (a=="#bootstrap_dns") then
a=d
elseif (a=="#upstream_dns") then
a=d
elseif (a==nil) then
break
end
table.insert(tbl, a)
end
f:close()
return table.concat(tbl, "\n")
end
m = Map("AdGuardHome")
local configpath = uci:get("AdGuardHome","AdGuardHome","configpath")
local binpath = uci:get("AdGuardHome","AdGuardHome","binpath")
s = m:section(TypedSection, "AdGuardHome")
s.anonymous=true
s.addremove=false
--- config
o = s:option(TextValue, "escconf")
o.rows = 66
o.wrap = "off"
o.rmempty = true
o.cfgvalue = function(self, section)
return fs.readfile("/tmp/AdGuardHometmpconfig.yaml") or fs.readfile(configpath) or gen_template_config() or ""
end
o.validate=function(self, value)
fs.writefile("/tmp/AdGuardHometmpconfig.yaml", value:gsub("\r\n", "\n"))
if fs.access(binpath) then
if (sys.call(binpath.." -c /tmp/AdGuardHometmpconfig.yaml --check-config 2> /tmp/AdGuardHometest.log")==0) then
return value
end
else
return value
end
luci.http.redirect(luci.dispatcher.build_url("admin","services","AdGuardHome","manual"))
return nil
end
o.write = function(self, section, value)
fs.move("/tmp/AdGuardHometmpconfig.yaml",configpath)
end
o.remove = function(self, section, value)
fs.writefile(configpath, "")
end
--- js and reload button
o = s:option(DummyValue, "")
o.anonymous=true
o.template = "AdGuardHome/yamleditor"
if not fs.access(binpath) then
o.description=translate("WARNING!!! no bin found apply config will not be test")
end
--- log
if (fs.access("/tmp/AdGuardHometmpconfig.yaml")) then
local c=fs.readfile("/tmp/AdGuardHometest.log")
if (c~="") then
o = s:option(TextValue, "")
o.readonly=true
o.rows = 5
o.rmempty = true
o.name=""
o.cfgvalue = function(self, section)
return fs.readfile("/tmp/AdGuardHometest.log")
end
end
end
function m.on_commit(map)
local ucitracktest=uci:get("AdGuardHome","AdGuardHome","ucitracktest")
if ucitracktest=="1" then
return
elseif ucitracktest=="0" then
io.popen("/etc/init.d/AdGuardHome reload &")
else
fs.writefile("/var/run/AdGlucitest","")
end
end
return m

View File

@ -0,0 +1,78 @@
<%+cbi/valueheader%>
<%local fs=require"nixio.fs"%>
<input type="button" class="cbi-button cbi-button-apply" id="apply_update_button" value="<%:Update core version%>" onclick=" return apply_update() "/>
<input type="button" class="cbi-button cbi-button-apply" id="apply_forceupdate_button" value="<%:Force update%>" onclick=" return apply_forceupdate()" style="display:none"/>
<% if self.showfastconfig then %>
<input type="button" class="cbi-button cbi-button-apply" id="to_configpage" value="<%:Fast config%>" onclick="location.href='<%=url([[admin]], [[services]], [[AdGuardHome]], [[manual]])%>'"/>
<%end%>
<div id="logview" style="display:none">
<input type="checkbox" id="reversetag" value="reverse" onclick=" return reverselog()" style="vertical-align:middle;height: auto;"><%:reverse%></input>
<textarea id="cbid.logview.1.conf" class="cbi-input-textarea" style="width: 100%;display:block;" data-update="change" rows="5" cols="60" readonly="readonly" > </textarea>
</div>
<script type="text/javascript">//<![CDATA[
var updatebtn = document.getElementById('apply_update_button');
var forceupdatebtn = document.getElementById('apply_forceupdate_button');
var islogreverse = false;
function apply_forceupdate(){
XHR.get('<%=url([[admin]], [[services]], [[AdGuardHome]], [[doupdate]])%>',{ force: 1 },function(x, data){}
);
updatebtn.disabled = true;
poll_check();
return
}
function reverselog(){
var lv = document.getElementById('cbid.logview.1.conf');
lv.innerHTML=lv.innerHTML.split('\n').reverse().join('\n')
if (islogreverse){
islogreverse=false;
}else{
islogreverse=true;
}
return
}
function apply_update(){
XHR.get('<%=url([[admin]], [[services]], [[AdGuardHome]], [[doupdate]])%>',null,function(x, data){}
);
updatebtn.disabled = true;
updatebtn.value = '<%:Check...%>';
forceupdatebtn.style.display="inline"
poll_check();
return
}
function poll_check(){
var tag = document.getElementById('logview');
tag.style.display="block"
XHR.poll(3, '<%=url([[admin]], [[services]], [[AdGuardHome]], [[check]])%>', null,
function(x, data) {
var lv = document.getElementById('cbid.logview.1.conf');
if (x.responseText && lv) {
if (x.responseText=="\u0000"){
for(j = 0,len=this.XHR._q.length; j < len; j++) {
if (this.XHR._q[j].url == '<%=url([[admin]], [[services]], [[AdGuardHome]], [[check]])%>'){
this.XHR._q.splice(j,1);
updatebtn.disabled = false;
updatebtn.value = '<%:Updated%>';
break;
}
}
return
}
if (islogreverse){
lv.innerHTML = x.responseText.split('\n').reverse().join('\n')+lv.innerHTML;
}else{
lv.innerHTML += x.responseText;
}
}
}
);}
<% if fs.access("/var/run/update_core") then %>
updatebtn.disabled = true;
updatebtn.value = '<%:Check...%>';
forceupdatebtn.style.display="inline"
poll_check();
<%elseif fs.access("/var/run/update_core_error") then %>
poll_check();
<%end%>
//]]>
</script>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,49 @@
<%+cbi/valueheader%>
<script type="text/javascript">//<![CDATA[
function chpass(btn)
{
btn.disabled = true;
btn.value = '<%:loading...%>';
if (typeof bcryptloaded == 'undefined' ){
var theHead = document.getElementsByTagName('head').item(0);
//创建脚本的dom对象实例
var myScript = document.createElement('script');
myScript.src = '<%=resource%>/twin-bcrypt.min.js'; //指定脚本路径
myScript.type = 'text/javascript'; //指定脚本类型
myScript.defer = true; //程序下载完后再解析和执行
theHead.appendChild(myScript);
bcryptloaded=1;
btn.value = '<%:Culculate%>';
btn.disabled = false;
return
}
var lv = document.getElementById('cbid.AdGuardHome.AdGuardHome.hashpass');
if (lv.value != ""){
var hash = TwinBcrypt.hashSync(lv.value);
lv.value=hash;
btn.value = '<%:Please save/apply%>';
}else{
btn.value = '<%:is empty%>';
btn.disabled = false;
}
}
//]]>
</script>
<input data-update="change"<%=
attr("id", cbid) ..
attr("name", cbid) ..
attr("type", self.password and "password" or "text") ..
attr("class", self.password and "cbi-input-password" or "cbi-input-text") ..
attr("value", self:cfgvalue(section) or self.default) ..
ifattr(self.size, "size") ..
ifattr(self.placeholder, "placeholder") ..
ifattr(self.readonly, "readonly") ..
ifattr(self.maxlength, "maxlength") ..
ifattr(self.datatype, "data-type", self.datatype) ..
ifattr(self.datatype, "data-optional", self.optional or self.rmempty) ..
ifattr(self.combobox_manual, "data-manual", self.combobox_manual) ..
ifattr(#self.keylist > 0, "data-choices", { self.keylist, self.vallist })
%> />
<% if self.password then %><img src="<%=resource%>/cbi/reload.gif" style="vertical-align:middle" title="<%:Reveal/hide password%>" onclick="var e = document.getElementById('<%=cbid%>'); e.type = (e.type=='password') ? 'text' : 'password';" /><% end %>
<input type="button" class="cbi-button cbi-button-apply" id="cbid.AdGuardHome.AdGuardHome.applychpass" value="<%:Load culculate model%>" onclick="return chpass(this)"/>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,27 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(3, '<%=url([[admin]], [[services]], [[AdGuardHome]], [[status]])%>', null,
function(x, data) {
var tb = document.getElementById('AdGuardHome_status');
if (data && tb) {
if (data.running) {
tb.innerHTML = '<em><b><font color=green>AdGuardHome <%:RUNNING%></font></b></em>';
} else {
tb.innerHTML = '<em><b><font color=red>AdGuardHome <%:NOT RUNNING%></font></b></em>';
}
if (data.redirect)
{
tb.innerHTML+='<em><b><font color=green><%:Redirected%></font></b></em>'
} else {
tb.innerHTML+='<em><b><font color=red><%:Not redirect%></font></b></em>'
}
}
}
);
//]]>
</script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="AdGuardHome_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>

View File

@ -0,0 +1,110 @@
<%+cbi/valueheader%>
<input type="checkbox" name="NAME" value="reverse" onclick=" return reverselog()" style="vertical-align:middle;height: auto;" checked><%:reverse%></input>
<%if self.timereplace then%>
<input type="checkbox" name="NAME" value="localtime" onclick=" return chlogtime()" style="vertical-align:middle;height: auto;" checked><%:localtime%></input><br>
<%end%>
<textarea id="cbid.logview.1.conf" class="cbi-input-textarea" style="width: 100%;display:inline" data-update="change" rows="32" cols="60" readonly="readonly" > </textarea>
<input type="button" class="cbi-button cbi-button-apply" id="apply_update_button" value="<%:dellog%>" onclick=" return apply_del_log() "/>
<input type="button" class="cbi-button cbi-button-apply" value="<%:download log%>" style=" display:inline;" onclick=" return download_log()" />
<script type="text/javascript">//<![CDATA[
var islogreverse = true;
var isutc2local = <%=tostring(self.timereplace)%>;
function createAndDownloadFile(fileName, content) {
var aTag = document.createElement('a');
var blob = new Blob([content]);
aTag.download = fileName;
aTag.href = URL.createObjectURL(blob);
aTag.click();
URL.revokeObjectURL(blob);
}
function download_log(){
var lv = document.getElementById('cbid.logview.1.conf');
var dt = new Date();
var timestamp = (dt.getMonth()+1)+"-"+dt.getDate()+"-"+dt.getHours()+"_"+dt.getMinutes();
createAndDownloadFile("AdGuardHome"+timestamp+".log",lv.innerHTML)
return
}
function apply_del_log(){
XHR.get('<%=url([[admin]], [[services]], [[AdGuardHome]], [[dodellog]])%>',null,function(x, data){
var lv = document.getElementById('cbid.logview.1.conf');
lv.innerHTML="";
}
);
return
}
function chlogtime(){
var lv = document.getElementById('cbid.logview.1.conf');
if (isutc2local){
lv.innerHTML=line_toUTC(lv.innerHTML).join('\n');
isutc2local=false;
}else{
lv.innerHTML=line_tolocal(lv.innerHTML).join('\n');
isutc2local=true;
}
return
}
function reverselog(){
var lv = document.getElementById('cbid.logview.1.conf');
lv.innerHTML=lv.innerHTML.split('\n').reverse().join('\n')
if (islogreverse){
islogreverse=false;
}else{
islogreverse=true;
}
return
}
function p(s) {
return s < 10 ? '0' + s: s;
}
function line_tolocal(str){
var strt=new Array();
str.trim().split('\n').forEach(function(v, i) {
var dt = new Date(v.substring(0,19)+" UTC");
if (dt != "Invalid Date"){
strt[i]=dt.getFullYear()+"/"+p(dt.getMonth()+1)+"/"+p(dt.getDate())+" "+p(dt.getHours())+":"+p(dt.getMinutes())+":"+p(dt.getSeconds())+v.substring(19);
}else{
strt[i]=v;}})
return strt
}
function line_toUTC(str){
var strt=new Array();
str.trim().split('\n').forEach(function(v, i) {
var dt = new Date(v.substring(0,19))
if (dt != "Invalid Date"){
strt[i]=dt.getUTCFullYear()+"/"+p(dt.getUTCMonth()+1)+"/"+p(dt.getUTCDate())+" "+p(dt.getUTCHours())+":"+p(dt.getUTCMinutes())+":"+p(dt.getUTCSeconds())+v.substring(19);
}else{
strt[i]=v;}})
return strt
}
function poll_check(){
XHR.poll(3, '<%=url([[admin]], [[services]], [[AdGuardHome]], [[getlog]])%>', null,
function(x, data) {
var lv = document.getElementById('cbid.logview.1.conf');
if (x.responseText && lv) {
if (isutc2local)
{
var lines=line_tolocal(x.responseText);
if (islogreverse){
lv.innerHTML = lines.reverse().join('\n')+lv.innerHTML;
}else{
lv.innerHTML += lines.join('\n');
}
}else{
if (islogreverse){
lv.innerHTML = x.responseText.split('\n').reverse().join('\n')+lv.innerHTML;
}else{
lv.innerHTML += x.responseText;
}
}
}
}
);}
<%if self.pollcheck then%>
poll_check();
<%else%>
var lv = document.getElementById('cbid.logview.1.conf');
lv.innerHTML="<%:Please add log path in config to enable log%>"
<%end%>
//]]>
</script>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,39 @@
<%+cbi/valueheader%>
<script src="/luci-static/resources/codemirror/lib/codemirror.js"></script>
<link rel="stylesheet" href="/luci-static/resources/codemirror/lib/codemirror.css"/>
<script src="/luci-static/resources/codemirror/mode/yaml/yaml.js"></script>
<link rel="stylesheet" href="/luci-static/resources/codemirror/theme/dracula.css"/>
<link rel="stylesheet" href="/luci-static/resources/codemirror/addon/fold/foldgutter.css"/>
<script src="/luci-static/resources/codemirror/addon/fold/foldcode.js"></script>
<script src="/luci-static/resources/codemirror/addon/fold/foldgutter.js"></script>
<script src="/luci-static/resources/codemirror/addon/fold/indent-fold.js"></script>
<script type="text/javascript">//<![CDATA[
var editor = CodeMirror.fromTextArea(document.getElementById("cbid.AdGuardHome.AdGuardHome.escconf"), {
mode: "text/yaml", //实现groovy代码高亮
styleActiveLine: true,
lineNumbers: true, //显示行号
theme: "dracula", //设置主题
lineWrapping: true, //代码折叠
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
matchBrackets: true //括号匹配
}
);
function reload_config(){
XHR.get('<%=url([[admin]], [[services]], [[AdGuardHome]], [[reloadconfig]])%>', null,
function(x, data) {
location.reload();
});}
function use_template(){
XHR.get('<%=url([[admin]], [[services]], [[AdGuardHome]], [[gettemplateconfig]])%>', null,
function(x, data) {
editor.setValue(x.responseText)
});}
//]]>
</script>
<%fs=require"nixio.fs"%>
<%if fs.access("/tmp/AdGuardHometmpconfig.yaml") then%>
<input type="button" id="apply_update_button" value="<%:Reload Config%>" onclick=" return reload_config() "/>
<%end%>
<input type="button" id="template_button" value="<%:Use template%>" onclick=" return use_template() "/>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,266 @@
#/cgi-bin/luci/admin/services/AdGuardHome
msgid "Base Setting"
msgstr "基础设置"
msgid "Log"
msgstr "日志"
msgid "Manual Config"
msgstr "手动设置"
msgid "Free and open source, powerful network-wide ads & trackers blocking DNS server."
msgstr "免费开源功能强大的全网络广告和跟踪程序拦截DNS服务器"
msgid "RUNNING"
msgstr "运行中"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "Redirected"
msgstr "已重定向"
msgid "Not redirect"
msgstr "未重定向"
msgid "Collecting data..."
msgstr "获取数据中..."
msgid "Enable"
msgstr "启用"
msgid "Browser management port"
msgstr "网页管理端口"
msgid "Update"
msgstr "更新"
#button change
msgid "Update core version"
msgstr "更新核心版本"
msgid "Check..."
msgstr "检查中..."
msgid "Updated"
msgstr "已更新"
#button hide
msgid "Force update"
msgstr "强制更新"
msgid "Fast config"
msgstr "快速配置"
#
msgid "core version:"
msgstr "核心版本:"
#description change
msgid "no config"
msgstr "没有配置文件"
msgid "no core"
msgstr "没有核心"
#
msgid "Redirect"
msgstr "重定向"
#inlist
msgid "none"
msgstr "无"
msgid "Run as dnsmasq upstream server"
msgstr "作为dnsmasq的上游服务器"
msgid "Redirect 53 port to AdGuardHome"
msgstr "重定向53端口到AdGuardHome"
msgid "Use port 53 replace dnsmasq"
msgstr "使用53端口替换dnsmasq"
#
msgid "AdGuardHome redirect mode"
msgstr "AdGuardHome重定向模式"
msgid "Bin Path"
msgstr "执行文件路径"
msgid "AdGuardHome Bin path if no bin will auto download"
msgstr "AdGuardHome 执行文件路径 如果没有执行文件将自动下载"
msgid "use upx to compress bin after download"
msgstr "下载后使用upx压缩执行文件"
#inlist
msgid "compress faster"
msgstr "快速压缩"
msgid "compress better"
msgstr "更好的压缩"
msgid "compress best(can be slow for big files)"
msgstr "最好的压缩(大文件可能慢)"
msgid "try all available compression methods & filters [slow]"
msgstr "尝试所有可能的压缩方法和过滤器[慢]"
msgid "try even more compression variants [very slow]"
msgstr "尝试更多变体压缩手段[很慢]"
msgid "bin use less space,but may have compatibility issues"
msgstr "减小执行文件空间占用,但是可能压缩后有兼容性问题"
#
msgid "Config Path"
msgstr "配置文件路径"
msgid "AdGuardHome config path"
msgstr "AdGuardHome 配置文件路径"
msgid "Work dir"
msgstr "工作目录"
msgid "AdGuardHome work dir include rules,audit log and database"
msgstr "AdGuardHome 工作目录包含规则,审计日志和数据库"
msgid "Runtime log file"
msgstr "运行日志"
msgid "AdGuardHome runtime Log file if 'syslog': write to system log;if empty no log"
msgstr "AdGuardHome 运行日志 如果填syslog将写入系统日志如果空则不记录日志"
msgid "Verbose log"
msgstr "详细日志"
#hide div
msgid "Add gfwlist"
msgstr "加入gfw列表"
msgid "Add"
msgstr "添加"
msgid "Added"
msgstr "已添加"
msgid "Not added"
msgstr "未添加"
#hide div
msgid "Del gfwlist"
msgstr "删除gfw列表"
msgid "Del"
msgstr "删除"
#hide div
msgid "Gfwlist upstream dns server"
msgstr "gfw列表上游服务器"
msgid "Gfwlist domain upstream dns service"
msgstr "gfw列表域名上游服务器"
#hide div
msgid "Change browser management password"
msgstr "改变网页登录密码"
msgid "Culculate"
msgstr "计算"
##button change
msgid "Load culculate model"
msgstr "载入计算模块"
msgid "loading..."
msgstr "载入中"
msgid "Please save/apply"
msgstr "请提交"
msgid "is empty"
msgstr "为空"
msgid "Press load culculate model and culculate finally save/apply"
msgstr "按载入计算模块 然后计算 最后保存/提交"
#
msgid "Keep files when system upgrade"
msgstr "系统升级时保留文件"
#checkbox
msgid "core bin"
msgstr "核心执行文件"
msgid "config file"
msgstr "配置文件"
msgid "log file"
msgstr "日志文件"
msgid "querylog.json"
msgstr "审计日志.json"
#
msgid "On boot when network ok restart"
msgstr "开机后网络准备好时重启"
msgid "Backup workdir files when shutdown"
msgstr "在关机时备份工作目录文件"
msgid "Will be restore when workdir/data is empty"
msgstr "在工作目录/data为空的时候恢复"
msgid "Backup workdir path"
msgstr "工作目录备份路径"
msgid "Crontab task"
msgstr "计划任务"
msgid "Auto update core"
msgstr "自动升级核心"
msgid "Auto tail querylog"
msgstr "自动截短查询日志"
msgid "Auto tail runtime log"
msgstr "自动截短运行日志"
msgid "Auto update ipv6 hosts and restart adh"
msgstr "自动更新ipv6主机并重启adh"
msgid "Auto update gfwlist and restart adh"
msgstr "自动更新gfw列表并重启adh"
msgid "Please change time and args in crontab"
msgstr "请在计划任务中修改时间和参数"
msgid "Download links for update"
msgstr "升级用的下载链接"
#/cgi-bin/luci/admin/services/AdGuardHome/log/
msgid "reverse"
msgstr "逆序"
msgid "localtime"
msgstr "本地时间"
msgid "Please add log path in config to enable log"
msgstr "请在设置里填写日志路径以启用日志"
msgid "dellog"
msgstr "删除日志"
msgid "download log"
msgstr "下载日志"
#/cgi-bin/luci//admin/services/AdGuardHome/manual/
msgid "Use template"
msgstr "使用模板"
#hide button
msgid "Reload Config"
msgstr "重新载入配置"
msgid "WARNING!!! no bin found apply config will not be test"
msgstr "警告!!!未找到执行文件,提交配置将不会进行校验"
#unused
msgid "Change browser management username"
msgstr "改变网页登录用户名"
msgid "Username"
msgstr "用户名"
msgid "Check Config"
msgstr "检查配置"
msgid "unknown"
msgstr "未知"
msgid "Keep database when system upgrade"
msgstr "系统升级时保留数据"
msgid "Boot delay until network ok"
msgstr "开机时直到网络准备好再启动"

View File

@ -0,0 +1,11 @@
config AdGuardHome 'AdGuardHome'
option enabled '0'
option httpport '3000'
option redirect 'none'
option configpath '/etc/AdGuardHome.yaml'
option workdir '/usr/bin/AdGuardHome'
option logfile '/tmp/AdGuardHome.log'
option verbose '0'
option binpath '/usr/bin/AdGuardHome/AdGuardHome'
option upxflag ''

View File

@ -0,0 +1,618 @@
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=95
STOP=01
CONFIGURATION=AdGuardHome
CRON_FILE=/etc/crontabs/root
EXTRA_COMMANDS="do_redirect testbackup test_crontab force_reload isrunning"
EXTRA_HELP=" do_redirect 0 or 1\
testbackup backup or restore\
test_crontab
force_reload
isrunning"
set_forward_dnsmasq()
{
local PORT="$1"
addr="127.0.0.1#$PORT"
OLD_SERVER="`uci get dhcp.@dnsmasq[0].server 2>/dev/null`"
echo $OLD_SERVER | grep "^$addr" >/dev/null 2>&1
if [ $? -eq 0 ]; then
return
fi
uci delete dhcp.@dnsmasq[0].server 2>/dev/null
uci add_list dhcp.@dnsmasq[0].server=$addr
for server in $OLD_SERVER; do
if [ "$server" = "$addr" ]; then
continue
fi
uci add_list dhcp.@dnsmasq[0].server=$server
done
uci delete dhcp.@dnsmasq[0].resolvfile 2>/dev/null
uci set dhcp.@dnsmasq[0].noresolv=1
uci commit dhcp
/etc/init.d/dnsmasq restart
}
stop_forward_dnsmasq()
{
local OLD_PORT="$1"
addr="127.0.0.1#$OLD_PORT"
OLD_SERVER="`uci get dhcp.@dnsmasq[0].server 2>/dev/null`"
echo $OLD_SERVER | grep "^$addr" >/dev/null 2>&1
if [ $? -ne 0 ]; then
return
fi
uci del_list dhcp.@dnsmasq[0].server=$addr 2>/dev/null
addrlist="`uci get dhcp.@dnsmasq[0].server 2>/dev/null`"
if [ -z "$addrlist" ] ; then
uci set dhcp.@dnsmasq[0].resolvfile=/tmp/resolv.conf.auto 2>/dev/null
uci delete dhcp.@dnsmasq[0].noresolv 2>/dev/null
fi
uci commit dhcp
/etc/init.d/dnsmasq restart
}
set_iptable()
{
local ipv6_server=$1
local tcp_server=$2
uci -q batch <<-EOF >/dev/null 2>&1
delete firewall.AdGuardHome
set firewall.AdGuardHome=include
set firewall.AdGuardHome.type=script
set firewall.AdGuardHome.path=/usr/share/AdGuardHome/firewall.start
set firewall.AdGuardHome.reload=1
commit firewall
EOF
IPS="`ifconfig | grep "inet addr" | grep -v ":127" | grep "Bcast" | awk '{print $2}' | awk -F : '{print $2}'`"
for IP in $IPS
do
if [ "$tcp_server" == "1" ]; then
iptables -t nat -A PREROUTING -p tcp -d $IP --dport 53 -j REDIRECT --to-ports $AdGuardHome_PORT >/dev/null 2>&1
fi
iptables -t nat -A PREROUTING -p udp -d $IP --dport 53 -j REDIRECT --to-ports $AdGuardHome_PORT >/dev/null 2>&1
done
if [ "$ipv6_server" == 0 ]; then
return
fi
IPS="`ifconfig | grep "inet6 addr" | grep -v " fe80::" | grep -v " ::1" | grep "Global" | awk '{print $3}'`"
for IP in $IPS
do
if [ "$tcp_server" == "1" ]; then
ip6tables -t nat -A PREROUTING -p tcp -d $IP --dport 53 -j REDIRECT --to-ports $AdGuardHome_PORT >/dev/null 2>&1
fi
ip6tables -t nat -A PREROUTING -p udp -d $IP --dport 53 -j REDIRECT --to-ports $AdGuardHome_PORT >/dev/null 2>&1
done
}
clear_iptable()
{
uci -q batch <<-EOF >/dev/null 2>&1
delete firewall.AdGuardHome
commit firewall
EOF
local OLD_PORT="$1"
local ipv6_server=$2
IPS="`ifconfig | grep "inet addr" | grep -v ":127" | grep "Bcast" | awk '{print $2}' | awk -F : '{print $2}'`"
for IP in $IPS
do
iptables -t nat -D PREROUTING -p udp -d $IP --dport 53 -j REDIRECT --to-ports $OLD_PORT >/dev/null 2>&1
iptables -t nat -D PREROUTING -p tcp -d $IP --dport 53 -j REDIRECT --to-ports $OLD_PORT >/dev/null 2>&1
done
if [ "$ipv6_server" == 0 ]; then
return
fi
echo "warn ip6tables nat mod is needed"
IPS="`ifconfig | grep "inet6 addr" | grep -v " fe80::" | grep -v " ::1" | grep "Global" | awk '{print $3}'`"
for IP in $IPS
do
ip6tables -t nat -D PREROUTING -p udp -d $IP --dport 53 -j REDIRECT --to-ports $OLD_PORT >/dev/null 2>&1
ip6tables -t nat -D PREROUTING -p tcp -d $IP --dport 53 -j REDIRECT --to-ports $OLD_PORT >/dev/null 2>&1
done
}
service_triggers() {
procd_add_reload_trigger "$CONFIGURATION"
[ "$(uci get AdGuardHome.AdGuardHome.redirect)" == "redirect" ] && procd_add_reload_trigger firewall
}
isrunning(){
config_load "${CONFIGURATION}"
_isrunning
local r=$?
([ "$r" == "0" ] && echo "running") || ([ "$r" == "1" ] && echo "not run" ) || echo "no bin"
return $r
}
_isrunning(){
config_get binpath $CONFIGURATION binpath "/usr/bin/AdGuardHome/AdGuardHome"
[ ! -f "$binpath" ] && return 2
pgrep $binpath 2>&1 >/dev/null && return 0
return 1
}
force_reload(){
config_load "${CONFIGURATION}"
_isrunning && procd_send_signal "$CONFIGURATION" || start
}
get_tz()
{
SET_TZ=""
if [ -e "/etc/localtime" ]; then
return
fi
for tzfile in /etc/TZ /var/etc/TZ
do
if [ ! -e "$tzfile" ]; then
continue
fi
tz="`cat $tzfile 2>/dev/null`"
done
if [ -z "$tz" ]; then
return
fi
SET_TZ=$tz
}
rm_port53()
{
local AdGuardHome_PORT=$(config_editor "dns.port" "" "$configpath" "1")
dnsmasq_port=$(uci get dhcp.@dnsmasq[0].port 2>/dev/null)
if [ -z "$dnsmasq_port" ]; then
dnsmasq_port="53"
fi
if [ "$dnsmasq_port" == "$AdGuardHome_PORT" ]; then
if [ "$dnsmasq_port" == "53" ]; then
dnsmasq_port="1745"
fi
elif [ "$dnsmasq_port" == "53" ]; then
return
fi
config_editor "dns.port" "$dnsmasq_port" "$configpath"
uci set dhcp.@dnsmasq[0].port="53"
uci commit dhcp
/etc/init.d/dnsmasq reload
}
use_port53()
{
local AdGuardHome_PORT=$(config_editor "dns.port" "" "$configpath" "1")
dnsmasq_port=$(uci get dhcp.@dnsmasq[0].port 2>/dev/null)
if [ -z "$dnsmasq_port" ]; then
dnsmasq_port="53"
fi
if [ "$dnsmasq_port" == "$AdGuardHome_PORT" ]; then
if [ "$dnsmasq_port" == "53" ]; then
AdGuardHome_PORT="1745"
fi
elif [ "$AdGuardHome_PORT" == "53" ]; then
return
fi
config_editor "dns.port" "53" "$configpath"
uci set dhcp.@dnsmasq[0].port="$AdGuardHome_PORT"
uci commit dhcp
/etc/init.d/dnsmasq reload
}
do_redirect()
{
config_load "${CONFIGURATION}"
_do_redirect $1
}
_do_redirect()
{
local section="$CONFIGURATION"
args=""
ipv6_server=1
tcp_server=0
enabled=$1
if [ "$enabled" == "1" ]; then
echo -n "1">/var/run/AdGredir
else
echo -n "0">/var/run/AdGredir
fi
config_get configpath $CONFIGURATION configpath "/etc/AdGuardHome.yaml"
AdGuardHome_PORT=$(config_editor "dns.port" "" "$configpath" "1")
if [ -z "$AdGuardHome_PORT" ]; then
AdGuardHome_PORT="0"
fi
config_get "redirect" "$section" "redirect" "none"
config_get "old_redirect" "$section" "old_redirect" "none"
config_get "old_port" "$section" "old_port" "0"
config_get "old_enabled" "$section" "old_enabled" "0"
uci get dhcp.@dnsmasq[0].port >/dev/null 2>&1 || uci set dhcp.@dnsmasq[0].port="53" >/dev/null 2>&1
if [ "$old_enabled" = "1" -a "$old_redirect" == "exchange" ]; then
AdGuardHome_PORT=$(uci get dhcp.@dnsmasq[0].port 2>/dev/null)
fi
if [ "$old_redirect" != "$redirect" ] || [ "$old_port" != "$AdGuardHome_PORT" ] || [ "$old_enabled" = "1" -a "$enabled" = "0" ]; then
if [ "$old_redirect" != "none" ]; then
if [ "$old_redirect" == "redirect" -a "$old_port" != "0" ]; then
clear_iptable "$old_port" "$ipv6_server"
elif [ "$old_redirect" == "dnsmasq-upstream" ]; then
stop_forward_dnsmasq "$old_port"
elif [ "$old_redirect" == "exchange" ]; then
rm_port53
fi
fi
elif [ "$old_enabled" = "1" -a "$enabled" = "1" ]; then
if [ "$old_redirect" == "redirect" -a "$old_port" != "0" ]; then
clear_iptable "$old_port" "$ipv6_server"
fi
fi
uci delete AdGuardHome.@AdGuardHome[0].old_redirect 2>/dev/null
uci delete AdGuardHome.@AdGuardHome[0].old_port 2>/dev/null
uci delete AdGuardHome.@AdGuardHome[0].old_enabled 2>/dev/null
uci add_list AdGuardHome.@AdGuardHome[0].old_redirect="$redirect" 2>/dev/null
uci add_list AdGuardHome.@AdGuardHome[0].old_port="$AdGuardHome_PORT" 2>/dev/null
uci add_list AdGuardHome.@AdGuardHome[0].old_enabled="$enabled" 2>/dev/null
uci commit AdGuardHome
[ "$enabled" == "0" ] && return 1
if [ "$AdGuardHome_PORT" == "0" ]; then
return 1
fi
if [ "$redirect" = "redirect" ]; then
set_iptable $ipv6_server $tcp_server
elif [ "$redirect" = "dnsmasq-upstream" ]; then
set_forward_dnsmasq "$AdGuardHome_PORT"
elif [ "$redirect" == "exchange" -a "$(uci get dhcp.@dnsmasq[0].port 2>/dev/null)" == "53" ]; then
use_port53
fi
}
get_filesystem()
{
# print out path filesystem
echo $1 | awk '
BEGIN{
while (("mount"| getline ret) > 0)
{
split(ret,d);
fs[d[3]]=d[5];
m=index(d[1],":")
if (m==0)
{
pt[d[3]]=d[1]
}else{
pt[d[3]]=substr(d[1],m+1)
}}}{
split($0,d,"/");
if ("/" in fs)
{
result1=fs["/"];
}
if ("/" in pt)
{
result2=pt["/"];
}
for (i=2;i<=length(d);i++)
{
p[i]=p[i-1]"/"d[i];
if (p[i] in fs)
{
result1=fs[p[i]];
result2=pt[p[i]];
}
}
if (result2 in fs){
result=fs[result2]}
else{
result=result1}
print(result);}'
}
config_editor()
{
awk -v yaml="$1" -v value="$2" -v file="$3" -v ro="$4" '
BEGIN{split(yaml,part,"\.");s="";i=1;l=length(part);}
{
if (match($0,s""part[i]":"))
{
if (i==l)
{
split($0,t,": ");
if (ro==""){
system("sed -i '\''"FNR"c \\"t[1]": "value"'\'' "file);
}else{
print(t[2]);
}
exit;
}
s=s"[- ]{2}";
i++;
}
}' $3
}
boot_service() {
rm /var/run/AdGserverdis >/dev/null 2>&1
config_load "${CONFIGURATION}"
config_get waitonboot $CONFIGURATION waitonboot "0"
config_get_bool enabled $CONFIGURATION enabled 0
config_get binpath $CONFIGURATION binpath "/usr/bin/AdGuardHome/AdGuardHome"
[ -f "$binpath" ] && start_service
if [ "$enabled" == "1" ] && [ "$waitonboot" == "1" ]; then
procd_open_instance "waitnet"
procd_set_param command "/usr/share/AdGuardHome/waitnet.sh"
procd_close_instance
echo "no net start pinging"
fi
}
testbackup(){
config_load "${CONFIGURATION}"
if [ "$1" == "backup" ]; then
backup
elif [ "$1" == "restore" ]; then
restore
fi
}
restore()
{
config_get workdir $CONFIGURATION workdir "/usr/bin/AdGuardHome"
config_get backupwdpath $CONFIGURATION backupwdpath "/usr/bin/AdGuardHome"
cp -u -r -f $backupwdpath/data $workdir
}
backup() {
config_get backupwdpath $CONFIGURATION backupwdpath "/usr/bin/AdGuardHome"
mkdir -p $backupwdpath/data
config_get workdir $CONFIGURATION workdir "/usr/bin/AdGuardHome"
config_get backupfile $CONFIGURATION backupfile ""
for one in $backupfile;
do
while :
do
if [ -d "$backupwdpath/data/$one" ]; then
cpret=$(cp -u -r -f $workdir/data/$one $backupwdpath/data 2>&1)
else
cpret=$(cp -u -r -f $workdir/data/$one $backupwdpath/data/$one 2>&1)
fi
echo "$cpret"
echo "$cpret" | grep "no space left on device"
if [ "$?" == "0" ]; then
echo "磁盘已满,删除log重试中"
del_querylog && continue
rm -f -r $backupwdpath/data/filters
rm -f -r $workdir/data/filters && continue
echo "backup failed"
fi
break
done
done
}
start_service() {
# Reading config
rm /var/run/AdGserverdis >/dev/null 2>&1
config_load "${CONFIGURATION}"
# update password
config_get hashpass $CONFIGURATION hashpass ""
config_get configpath $CONFIGURATION configpath "/etc/AdGuardHome.yaml"
if [ -n "$hashpass" ]; then
config_editor "users.password" "$hashpass" "$configpath"
uci set $CONFIGURATION.$CONFIGURATION.hashpass=""
fi
local enabled
config_get_bool enabled $CONFIGURATION enabled 0
# update crontab
do_crontab
if [ "$enabled" == "0" ]; then
_do_redirect 0
return
fi
#what need to do before reload
config_get workdir $CONFIGURATION workdir "/usr/bin/AdGuardHome"
config_get backupfile $CONFIGURATION backupfile ""
mkdir -p $workdir/data
if [ -n "$backupfile" ] && [ ! -d "$workdir/data" ]; then
restore
fi
# for overlay data-stk-oo not suppport
local cwdfs=$(get_filesystem $workdir)
echo "workdir is a $cwdfs filesystem"
if [ "$cwdfs" == "jffs2" ]; then
echo "fs error ln db to tmp $workdir $cwdfs"
logger "AdGuardHome" "warning db redirect to tmp"
touch $workdir/data/stats.db
if [ ! -L $workdir/data/stats.db ]; then
mv -f $workdir/data/stats.db /tmp/stats.db 2>/dev/null
ln -s /tmp/stats.db $workdir/data/stats.db 2>/dev/null
fi
touch $workdir/data/sessions.db
if [ ! -L $workdir/data/sessions.db ]; then
mv -f $workdir/data/sessions.db /tmp/sessions.db 2>/dev/null
ln -s /tmp/sessions.db $workdir/data/sessions.db 2>/dev/null
fi
fi
local ADDITIONAL_ARGS=""
config_get binpath $CONFIGURATION binpath "/usr/bin/AdGuardHome/AdGuardHome"
mkdir -p ${binpath%/*}
ADDITIONAL_ARGS="$ADDITIONAL_ARGS -c $configpath"
ADDITIONAL_ARGS="$ADDITIONAL_ARGS -w $workdir"
config_get httpport $CONFIGURATION httpport 3000
ADDITIONAL_ARGS="$ADDITIONAL_ARGS -p $httpport"
# hack to save config file when upgrade system
config_get upprotect $CONFIGURATION upprotect ""
eval upprotect=${upprotect// /\\\\n}
echo -e "$upprotect">/lib/upgrade/keep.d/luci-app-adguardhome
config_get logfile $CONFIGURATION logfile ""
if [ -n "$logfile" ]; then
ADDITIONAL_ARGS="$ADDITIONAL_ARGS -l $logfile"
fi
if [ ! -f "$binpath" ]; then
_do_redirect 0
/usr/share/AdGuardHome/update_core.sh 2>&1 >/tmp/AdGuardHome_update.log &
exit 0
fi
config_get_bool verbose $CONFIGURATION verbose 0
if [ "$verbose" -eq 1 ]; then
ADDITIONAL_ARGS="$ADDITIONAL_ARGS -v"
fi
procd_open_instance
get_tz
if [ -n "$SET_TZ" ]; then
procd_set_param env TZ="$SET_TZ"
fi
procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
procd_set_param limits core="unlimited" nofile="65535 65535"
procd_set_param stderr 1
procd_set_param command $binpath $ADDITIONAL_ARGS
procd_set_param file "$configpath" "/etc/hosts" "/etc/config/AdGuardHome"
procd_close_instance
if [ -f "$configpath" ]; then
_do_redirect 1
else
_do_redirect 0
config_get "redirect" "AdGuardHome" "redirect" "none"
if [ "$redirect" != "none" ]; then
procd_open_instance "waitconfig"
procd_set_param command "/usr/share/AdGuardHome/watchconfig.sh"
procd_close_instance
echo "no config start watching"
fi
fi
echo "AdGuardHome service enabled"
echo "luci enable switch=$enabled"
(sleep 10 && [ -z "$(pgrep $binpath)" ] && logger "AdGuardHome" "no process in 10s cancel redirect" && _do_redirect 0 )&
}
reload_service()
{
rm /var/run/AdGlucitest >/dev/null 2>&1
echo "AdGuardHome reloading"
start
}
del_querylog(){
local btarget=$(ls $backupwdpath/data | grep -F "querylog.json" | sort -r | head -n 1)
local wtarget=$(ls $workdir/data | grep -F "querylog.json" | sort -r | head -n 1)
if [ "$btarget"x == "$wtarget"x ]; then
[ -z "$btarget" ] && return 1
rm -f $workdir/data/$wtarget
rm -f $backupwdpath/data/$btarget
return 0
fi
if [ "$btarget" \> "$wtarget" ]; then
rm -f $backupwdpath/data/$btarget
return 0
else
rm -f $workdir/data/$wtarget
return 0
fi
}
stop_service()
{
config_load "${CONFIGURATION}"
_do_redirect 0
do_crontab
if [ "$1" != "nobackup" ]; then
config_get backupfile $CONFIGURATION backupfile "0"
if [ -n "$backupfile" ]; then
backup
fi
fi
echo "AdGuardHome service disabled"
touch /var/run/AdGserverdis
}
boot() {
rc_procd boot_service "$@"
if eval "type service_started" 2>/dev/null >/dev/null; then
service_started
fi
}
test_crontab(){
config_load "${CONFIGURATION}"
do_crontab
}
do_crontab(){
config_get_bool enabled $CONFIGURATION enabled 0
config_get crontab $CONFIGURATION crontab ""
local findstr default cronenable replace commit
local cronreload=0
local commit=0
findstr="/usr/share/AdGuardHome/update_core.sh"
default="30 3 * * * /usr/share/AdGuardHome/update_core.sh 2>&1"
[ "$enabled" == "0" ] || [ "${crontab//autoupdate/}" == "$crontab" ] && cronenable=0 || cronenable=1
crontab_editor
config_get workdir $CONFIGURATION workdir "/usr/bin/AdGuardHome"
config_get lastworkdir $CONFIGURATION lastworkdir "/usr/bin/AdGuardHome"
findstr="/usr/share/AdGuardHome/tailto.sh [0-9]* \$(uci get AdGuardHome.AdGuardHome.workdir)/data/querylog.json"
#[ -n "$lastworkdir" ] && findstr="/usr/share/AdGuardHome/tailto.sh [0-9]* $lastworkdir/data/querylog.json" && [ "$lastworkdir" != "$workdir" ] && replace="${lastworkdir//\//\\/}/${workdir//\//\\/}"
default="0 * * * * /usr/share/AdGuardHome/tailto.sh 2000 \$(uci get AdGuardHome.AdGuardHome.workdir)/data/querylog.json"
[ "$enabled" == "0" ] || [ "${crontab//cutquerylog/}" == "$crontab" ] && cronenable=0 || cronenable=1
crontab_editor
#[ "$lastworkdir" != "$workdir" ] && uci set AdGuardHome.AdGuardHome.lastworkdir="$workdir" && commit=1
config_get logfile $CONFIGURATION logfile ""
config_get lastlogfile $CONFIGURATION lastlogfile ""
findstr="/usr/share/AdGuardHome/tailto.sh [0-9]* \$(uci get AdGuardHome.AdGuardHome.logfile)"
default="30 3 * * * /usr/share/AdGuardHome/tailto.sh 2000 \$(uci get AdGuardHome.AdGuardHome.logfile)"
#[ -n "$lastlogfile" ] && findstr="/usr/share/AdGuardHome/tailto.sh [0-9]* $lastlogfile" && [ -n "$logfile" ] && [ "$lastlogfile" != "$logfile" ] && replace="${lastlogfile//\//\\/}/${logfile//\//\\/}"
[ "$logfile" == "syslog" ] || [ "$logfile" == "" ] || [ "$enabled" == "0" ] || [ "${crontab//cutruntimelog/}" == "$crontab" ] && cronenable=0 || cronenable=1
crontab_editor
#[ -n "$logfile" ] && [ "$lastlogfile" != "$logfile" ] && uci set AdGuardHome.AdGuardHome.lastlogfile="$logfile" && commit=1
findstr="/usr/share/AdGuardHome/addhost.sh"
default="0 * * * * /usr/share/AdGuardHome/addhost.sh"
[ "$enabled" == "0" ] || [ "${crontab//autohost/}" == "$crontab" ] && cronenable=0 || cronenable=1
crontab_editor
[ "$cronenable" == "0" ] && /usr/share/AdGuardHome/addhost.sh "del" "noreload" || /usr/share/AdGuardHome/addhost.sh "" "noreload"
findstr="/usr/share/AdGuardHome/gfw2adg.sh"
default="30 3 * * * /usr/share/AdGuardHome/gfw2adg.sh"
[ "$enabled" == "0" ] || [ "${crontab//autogfw/}" == "$crontab" ] && cronenable=0 || cronenable=1
crontab_editor
[ "$cronreload" -gt 0 ] && /etc/init.d/cron restart
#[ "$commit" -gt 0 ] && uci commit AdGuardHome
}
crontab_editor(){
#usage input:
#findstr=
#default=
#cronenable=
#replace="${last//\//\\/}/${now//\//\\/}"
#output:cronreload:if >1 please /etc/init.d/cron restart manual
local testline reload
local line="$(grep "$findstr" $CRON_FILE)"
[ -n "$replace" ] && [ -n "$line" ] && eval testline="\${line//$replace}" && [ "$testline" != "$line" ] && line="$testline" && reload="1" && replace=""
if [ "${line:0:1}" != "#" ]; then
if [ $cronenable -eq 1 ]; then
[ -z "$line" ] && line="$default" && reload="1"
if [ -n "$reload" ]; then
sed -i "\,$findstr,d" $CRON_FILE
echo "$line" >> $CRON_FILE
cronreload=$((cronreload+1))
fi
elif [ -n "$line" ]; then
sed -i "\,$findstr,d" $CRON_FILE
echo "#$line" >> $CRON_FILE
cronreload=$((cronreload+1))
fi
else
if [ $cronenable -eq 1 ]; then
sed -i "\,$findstr,d" $CRON_FILE
echo "${line:1}" >> $CRON_FILE
cronreload=$((cronreload+1))
elif [ -z "$reload" ]; then
sed -i "\,$findstr,d" $CRON_FILE
echo "$line" >> $CRON_FILE
fi
fi
}

View File

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

View File

@ -0,0 +1,109 @@
#提交就可以直接用的配置模板文件
#a template config can be use with a apply
bind_host: 0.0.0.0
bind_port: 3000
users:
- name: root
password: $2y$10$FfeQavihMUiXCuJhHuQwy.6EOXDvkXb/S50qI5fXizqarNT/ShhQm
language: ""
rlimit_nofile: 0
dns:
bind_host: 0.0.0.0
port: 5553
statistics_interval: 1
protection_enabled: true
filtering_enabled: true
filters_update_interval: 24
blocking_mode: nxdomain
blocked_response_ttl: 10
querylog_enabled: false
querylog_interval: 1
ratelimit: 0
ratelimit_whitelist: []
refuse_any: false
bootstrap_dns:
#bootstrap_dns
all_servers: false
allowed_clients: []
disallowed_clients: []
blocked_hosts: []
parental_block_host: ""
safebrowsing_block_host: ""
blocked_services: []
cache_size: 4194304
parental_sensitivity: 13
parental_enabled: false
safesearch_enabled: false
safebrowsing_enabled: false
safebrowsing_cache_size: 1048576
safesearch_cache_size: 1048576
parental_cache_size: 1048576
cache_time: 30
rewrites: []
upstream_dns:
#upstream_dns
tls:
enabled: false
server_name: ""
force_https: false
port_https: 443
port_dns_over_tls: 853
certificate_chain: ""
private_key: ""
certificate_path: ""
private_key_path: ""
filters:
- enabled: true
url: https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt
name: AdGuard Simplified Domain Names filter
id: 1
- enabled: true
url: https://adaway.org/hosts.txt
name: AdAway
id: 2
- enabled: false
url: https://hosts-file.net/ad_servers.txt
name: hpHosts - Ad and Tracking servers only
id: 3
- enabled: true
url: https://www.malwaredomainlist.com/hostslist/hosts.txt
name: MalwareDomainList.com Hosts List
id: 4
- enabled: false
url: https://raw.githubusercontent.com/vokins/yhosts/master/data/tvbox.txt
name: tvbox
id: 1575018007
- enabled: true
url: https://hosts.nfz.moe/full/hosts
name: neoHosts full
id: 1575618240
- enabled: false
url: https://hosts.nfz.moe/basic/hosts
name: neoHosts basic
id: 1575618241
- enabled: false
url: http://sbc.io/hosts/hosts
name: StevenBlack host basic
id: 1575618242
- enabled: false
url: http://sbc.io/hosts/alternates/fakenews-gambling-porn-social/hosts
name: StevenBlack host+fakenews + gambling + porn + social
id: 1575618243
- enabled: false
url: https://cdn.jsdelivr.net/gh/privacy-protection-tools/anti-AD/anti-ad-easylist.txt
name: anti-AD(Adblock+neohosts+yhosts+cjxlist+adhlist)
id: 1577113202
user_rules: []
dhcp:
enabled: false
interface_name: ""
gateway_ip: ""
subnet_mask: ""
range_start: ""
range_end: ""
lease_duration: 86400
icmp_timeout_msec: 1000
clients: []
log_file: ""
verbose: false
schema_version: 5

View File

@ -0,0 +1,33 @@
#!/bin/sh
checkmd5(){
local nowmd5=$(md5sum /etc/hosts)
nowmd5=${nowmd5%% *}
local lastmd5=$(uci get AdGuardHome.AdGuardHome.hostsmd5 2>/dev/null)
if [ "$nowmd5" != "$lastmd5" ]; then
uci set AdGuardHome.AdGuardHome.hostsmd5="$nowmd5"
uci commit AdGuardHome
[ "$1" == "noreload" ] || /etc/init.d/AdGuardHome reload
fi
}
[ "$1" == "del" ] && sed -i '/programaddstart/,/programaddend/d' /etc/hosts && checkmd5 "$2" && exit 0
/usr/bin/awk 'BEGIN{
while ((getline < "/tmp/dhcp.leases") > 0)
{
a[$2]=$4;
}
while (("ip -6 neighbor show | grep -v fe80" | getline) > 0)
{
if (a[$5]) {print $1" "a[$5] >"/tmp/tmphost"; }
}
print "#programaddend" >"/tmp/tmphost";
}'
grep programaddstart /etc/hosts >/dev/null 2>&1
if [ "$?" == "0" ]; then
sed -i '/programaddstart/,/programaddend/c\#programaddstart' /etc/hosts
sed -i '/programaddstart/'r/tmp/tmphost /etc/hosts
else
echo "#programaddstart" >>/etc/hosts
cat /tmp/tmphost >> /etc/hosts
fi
rm /tmp/tmphost
checkmd5 "$2"

View File

@ -0,0 +1,8 @@
#!/bin/sh
AdGuardHome_enable=$(uci get AdGuardHome.AdGuardHome.enabled)
redirect=$(uci get AdGuardHome.AdGuardHome.redirect)
if [ $AdGuardHome_enable -eq 1 -a "$redirect" == "redirect" ]; then
/etc/init.d/AdGuardHome do_redirect 1
fi

View File

@ -0,0 +1,19 @@
#!/bin/sh
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
logread -e AdGuardHome > /tmp/AdGuardHometmp.log
logread -e AdGuardHome -f >> /tmp/AdGuardHometmp.log &
pid=$!
echo "1">/var/run/AdGuardHomesyslog
while true
do
sleep 12
watchdog=$(cat /var/run/AdGuardHomesyslog)
if [ "$watchdog"x == "0"x ]; then
kill $pid
rm /tmp/AdGuardHometmp.log
rm /var/run/AdGuardHomesyslog
exit 0
else
echo "0">/var/run/AdGuardHomesyslog
fi
done

View File

@ -0,0 +1,86 @@
#!/bin/sh
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
checkmd5(){
local nowmd5=$(md5sum /tmp/adguard.list 2>/dev/null)
nowmd5=${nowmd5%% *}
local lastmd5=$(uci get AdGuardHome.AdGuardHome.gfwlistmd5 2>/dev/null)
if [ "$nowmd5" != "$lastmd5" ]; then
uci set AdGuardHome.AdGuardHome.gfwlistmd5="$nowmd5"
uci commit AdGuardHome
[ "$1" == "noreload" ] || /etc/init.d/AdGuardHome reload
fi
}
configpath=$(uci get AdGuardHome.AdGuardHome.configpath 2>/dev/null)
[ "$1" == "del" ] && sed -i '/programaddstart/,/programaddend/d' $configpath && checkmd5 "$2" && exit 0
gfwupstream=$(uci get AdGuardHome.AdGuardHome.gfwupstream 2>/dev/null)
if [ -z $gfwupstream ]; then
gfwupstream="tcp://208.67.220.220:5353"
fi
if [ ! -f "$configpath" ]; then
echo "please make a config first"
exit 1
fi
wget-ssl --no-check-certificate https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt -O- | base64 -d > /tmp/gfwlist.txt
cat /tmp/gfwlist.txt | awk -v upst="$gfwupstream" 'BEGIN{getline;}{
s1=substr($0,1,1);
if (s1=="!")
{next;}
if (s1=="@"){
$0=substr($0,3);
s1=substr($0,1,1);
white=1;}
else{
white=0;
}
if (s1=="|")
{s2=substr($0,2,1);
if (s2=="|")
{
$0=substr($0,3);
split($0,d,"/");
$0=d[1];
}else{
split($0,d,"/");
$0=d[3];
}}
else{
split($0,d,"/");
$0=d[1];
}
star=index($0,"*");
if (star!=0)
{
$0=substr($0,star+1);
dot=index($0,".");
if (dot!=0)
$0=substr($0,dot+1);
else
next;
s1=substr($0,1,1);
}
if (s1==".")
{fin=substr($0,2);}
else{fin=$0;}
if (index(fin,".")==0) next;
if (index(fin,"%")!=0) next;
if (index(fin,":")!=0) next;
match(fin,"^[0-9\.]+")
if (RSTART==1 && RLENGTH==length(fin)) {print "ipset add gfwlist "fin>"/tmp/doipset.sh";next;}
if (fin=="" || finl==fin) next;
finl=fin;
if (white==0)
{print(" - '\''[/"fin"/]"upst"'\''");}
else{
print(" - '\''[/"fin"/]#'\''");}
}END{print(" - '\''[/programaddend/]#'\''")}' > /tmp/adguard.list
grep programaddstart $configpath
if [ "$?" == "0" ]; then
sed -i '/programaddstart/,/programaddend/c\ - '\''\[\/programaddstart\/\]#'\''' $configpath
sed -i '/programaddstart/'r/tmp/adguard.list $configpath
else
sed -i '1i\ - '\''[/programaddstart/]#'\''' /tmp/adguard.list
sed -i '/upstream_dns:/'r/tmp/adguard.list $configpath
fi
checkmd5 "$2"
rm -f /tmp/gfwlist.txt /tmp/adguard.list

View File

@ -0,0 +1,3 @@
https://github.com/AdguardTeam/AdGuardHome/releases/download/${latest_ver}/AdGuardHome_linux_${Arch}.tar.gz
https://static.adguard.com/adguardhome/release/AdGuardHome_linux_${Arch}.tar.gz
#https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_${Arch}.tar.gz

View File

@ -0,0 +1,4 @@
#!/bin/sh
tail -n $1 "$2" > /var/run/tailtmp
cat /var/run/tailtmp > "$2"
rm /var/run/tailtmp

View File

@ -0,0 +1,231 @@
#!/bin/bash
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
binpath=$(uci get AdGuardHome.AdGuardHome.binpath)
if [ -z "$binpath" ]; then
uci set AdGuardHome.AdGuardHome.binpath="/tmp/AdGuardHome/AdGuardHome"
binpath="/tmp/AdGuardHome/AdGuardHome"
fi
mkdir -p ${binpath%/*}
upxflag=$(uci get AdGuardHome.AdGuardHome.upxflag 2>/dev/null)
check_if_already_running(){
running_tasks="$(ps |grep "AdGuardHome" |grep "update_core" |grep -v "grep" |awk '{print $1}' |wc -l)"
[ "${running_tasks}" -gt "2" ] && echo -e "\nA task is already running." && EXIT 2
}
check_wgetcurl(){
which curl && downloader="curl -L -k --retry 2 --connect-timeout 20 -o" && return
which wget-ssl && downloader="wget-ssl --no-check-certificate -t 2 -T 20 -O" && return
[ -z "$1" ] && opkg update || (echo error opkg && EXIT 1)
[ -z "$1" ] && (opkg remove wget wget-nossl --force-depends ; opkg install wget ; check_wgetcurl 1 ;return)
[ "$1" == "1" ] && (opkg install curl ; check_wgetcurl 2 ; return)
echo error curl and wget && EXIT 1
}
check_latest_version(){
check_wgetcurl
latest_ver="$($downloader - https://api.github.com/repos/AdguardTeam/AdGuardHome/releases/latest 2>/dev/null|grep -E 'tag_name' |grep -E 'v[0-9.]+' -o 2>/dev/null)"
if [ -z "${latest_ver}" ]; then
echo -e "\nFailed to check latest version, please try again later." && EXIT 1
fi
now_ver="$($binpath -c /dev/null --check-config 2>&1| grep -m 1 -E 'v[0-9.]+' -o)"
if [ "${latest_ver}"x != "${now_ver}"x ] || [ "$1" == "force" ]; then
echo -e "Local version: ${now_ver}., cloud version: ${latest_ver}."
doupdate_core
else
echo -e "\nLocal version: ${now_ver}, cloud version: ${latest_ver}."
echo -e "You're already using the latest version."
if [ ! -z "$upxflag" ]; then
filesize=$(ls -l $binpath | awk '{ print $5 }')
if [ $filesize -gt 8000000 ]; then
echo -e "start upx may take a long time"
doupx
mkdir -p "/tmp/AdGuardHomeupdate/AdGuardHome" >/dev/null 2>&1
rm -fr /tmp/AdGuardHomeupdate/AdGuardHome/${binpath##*/}
/tmp/upx-${upx_latest_ver}-${Arch}_linux/upx $upxflag $binpath -o /tmp/AdGuardHomeupdate/AdGuardHome/${binpath##*/}
rm -rf /tmp/upx-${upx_latest_ver}-${Arch}_linux
/etc/init.d/AdGuardHome stop nobackup
rm $binpath
mv -f /tmp/AdGuardHomeupdate/AdGuardHome/${binpath##*/} $binpath
/etc/init.d/AdGuardHome start
echo -e "finished"
fi
fi
EXIT 0
fi
}
doupx(){
Archt="$(opkg info kernel | grep Architecture | awk -F "[ _]" '{print($2)}')"
case $Archt in
"i386")
Arch="i386"
;;
"i686")
Arch="i386"
echo -e "i686 use $Arch may have bug"
;;
"x86")
Arch="amd64"
;;
"mipsel")
Arch="mipsel"
;;
"mips64el")
Arch="mips64el"
Arch="mipsel"
echo -e "mips64el use $Arch may have bug"
;;
"mips")
Arch="mips"
;;
"mips64")
Arch="mips64"
Arch="mips"
echo -e "mips64 use $Arch may have bug"
;;
"arm")
Arch="arm"
;;
"armeb")
Arch="armeb"
;;
"aarch64")
Arch="arm64"
;;
"powerpc")
Arch="powerpc"
;;
"powerpc64")
Arch="powerpc64"
;;
*)
echo -e "error not support $Archt if you can use offical release please issue a bug"
EXIT 1
;;
esac
upx_latest_ver="$($downloader - https://api.github.com/repos/upx/upx/releases/latest 2>/dev/null|grep -E 'tag_name' |grep -E '[0-9.]+' -o 2>/dev/null)"
$downloader /tmp/upx-${upx_latest_ver}-${Arch}_linux.tar.xz "https://github.com/upx/upx/releases/download/v${upx_latest_ver}/upx-${upx_latest_ver}-${Arch}_linux.tar.xz" 2>&1
#tar xvJf
which xz || (opkg list | grep ^xz || opkg update && opkg install xz) || (echo "xz download fail" && EXIT 1)
mkdir -p /tmp/upx-${upx_latest_ver}-${Arch}_linux
xz -d -c /tmp/upx-${upx_latest_ver}-${Arch}_linux.tar.xz| tar -x -C "/tmp" >/dev/null 2>&1
if [ ! -e "/tmp/upx-${upx_latest_ver}-${Arch}_linux/upx" ]; then
echo -e "Failed to download upx."
EXIT 1
fi
rm /tmp/upx-${upx_latest_ver}-${Arch}_linux.tar.xz
}
doupdate_core(){
echo -e "Updating core..."
mkdir -p "/tmp/AdGuardHomeupdate"
rm -rf /tmp/AdGuardHomeupdate/* >/dev/null 2>&1
Archt="$(opkg info kernel | grep Architecture | awk -F "[ _]" '{print($2)}')"
case $Archt in
"i386")
Arch="386"
;;
"i686")
Arch="386"
;;
"x86")
Arch="amd64"
;;
"mipsel")
Arch="mipsle"
;;
"mips64el")
Arch="mips64le"
Arch="mipsle"
echo -e "mips64el use $Arch may have bug"
;;
"mips")
Arch="mips"
;;
"mips64")
Arch="mips64"
Arch="mips"
echo -e "mips64 use $Arch may have bug"
;;
"arm")
Arch="arm"
;;
"aarch64")
Arch="arm64"
;;
"powerpc")
Arch="ppc"
echo -e "error not support $Archt"
EXIT 1
;;
"powerpc64")
Arch="ppc64"
echo -e "error not support $Archt"
EXIT 1
;;
*)
echo -e "error not support $Archt if you can use offical release please issue a bug"
EXIT 1
;;
esac
echo -e "start download"
grep -v "^#" /usr/share/AdGuardHome/links.txt >/tmp/run/AdHlinks.txt
while read link
do
eval link="$link"
$downloader /tmp/AdGuardHomeupdate/${link##*/} "$link" 2>&1
if [ "$?" != "0" ]; then
echo "download failed try another download"
rm -f /tmp/AdGuardHomeupdate/${link##*/}
else
local success="1"
break
fi
done < "/tmp/run/AdHlinks.txt"
rm /tmp/run/AdHlinks.txt
[ -z "$success" ] && echo "no download success" && EXIT 1
if [ "${link##*.}" == "gz" ]; then
tar -zxf "/tmp/AdGuardHomeupdate/${link##*/}" -C "/tmp/AdGuardHomeupdate/"
if [ ! -e "/tmp/AdGuardHomeupdate/AdGuardHome" ]; then
echo -e "Failed to download core."
rm -rf "/tmp/AdGuardHomeupdate" >/dev/null 2>&1
EXIT 1
fi
downloadbin="/tmp/AdGuardHomeupdate/AdGuardHome/AdGuardHome"
else
downloadbin="/tmp/AdGuardHomeupdate/${link##*/}"
fi
chmod 755 $downloadbin
echo -e "download success start copy"
if [ -n "$upxflag" ]; then
echo -e "start upx may take a long time"
doupx
/tmp/upx-${upx_latest_ver}-${Arch}_linux/upx $upxflag $downloadbin
rm -rf /tmp/upx-${upx_latest_ver}-${Arch}_linux
fi
echo -e "start copy"
/etc/init.d/AdGuardHome stop nobackup
rm "$binpath"
mv -f "$downloadbin" "$binpath"
if [ "$?" == "1" ]; then
echo "mv failed maybe not enough space please use upx or change bin to /tmp/AdGuardHome"
EXIT 1
fi
/etc/init.d/AdGuardHome start
rm -rf "/tmp/AdGuardHomeupdate" >/dev/null 2>&1
echo -e "Succeeded in updating core."
echo -e "Local version: ${latest_ver}, cloud version: ${latest_ver}.\n"
EXIT 0
}
EXIT(){
rm /var/run/update_core 2>/dev/null
[ "$1" != "0" ] && touch /var/run/update_core_error
exit $1
}
main(){
check_if_already_running
check_latest_version $1
}
trap "EXIT 1" SIGTERM SIGINT
touch /var/run/update_core
rm /var/run/update_core_error 2>/dev/null
main $1

View File

@ -0,0 +1,34 @@
#!/bin/sh
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
count=0
while :
do
ping -c 1 -W 1 -q www.baidu.com 1>/dev/null 2>&1
if [ "$?" == "0" ]; then
/etc/init.d/AdGuardHome force_reload
break
fi
ping -c 1 -W 1 -q 202.108.22.5 1>/dev/null 2>&1
if [ "$?" == "0" ]; then
/etc/init.d/AdGuardHome force_reload
break
fi
sleep 5
ping -c 1 -W 1 -q www.google.com 1>/dev/null 2>&1
if [ "$?" == "0" ]; then
/etc/init.d/AdGuardHome force_reload
break
fi
ping -c 1 -W 1 -q 8.8.8.8 1>/dev/null 2>&1
if [ "$?" == "0" ]; then
/etc/init.d/AdGuardHome force_reload
break
fi
sleep 5
count=$((count+1))
if [ $count -gt 18 ]; then
/etc/init.d/AdGuardHome force_reload
break
fi
done
return 0

View File

@ -0,0 +1,12 @@
#!/bin/sh
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
configpath=$(uci get AdGuardHome.AdGuardHome.configpath)
while :
do
sleep 10
if [ -f "$configpath" ]; then
/etc/init.d/AdGuardHome do_redirect 1
break
fi
done
return 0

View File

@ -0,0 +1 @@
!function(n){"object"==typeof exports&&"object"==typeof module?n(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],n):n(CodeMirror)}(function(n){"use strict";function e(e,o,i,t){if(i&&i.call){var l=i;i=null}else l=r(e,i,"rangeFinder");"number"==typeof o&&(o=n.Pos(o,0));var f=r(e,i,"minFoldSize");function d(n){var r=l(e,o);if(!r||r.to.line-r.from.line<f)return null;for(var i=e.findMarksAt(r.from),d=0;d<i.length;++d)if(i[d].__isFold&&"fold"!==t){if(!n)return null;r.cleared=!0,i[d].clear()}return r}var u=d(!0);if(r(e,i,"scanUp"))for(;!u&&o.line>e.firstLine();)o=n.Pos(o.line-1,0),u=d(!1);if(u&&!u.cleared&&"unfold"!==t){var a=function(n,e){var o=r(n,e,"widget");if("string"==typeof o){var i=document.createTextNode(o);(o=document.createElement("span")).appendChild(i),o.className="CodeMirror-foldmarker"}else o&&(o=o.cloneNode(!0));return o}(e,i);n.on(a,"mousedown",function(e){c.clear(),n.e_preventDefault(e)});var c=e.markText(u.from,u.to,{replacedWith:a,clearOnEnter:r(e,i,"clearOnEnter"),__isFold:!0});c.on("clear",function(o,r){n.signal(e,"unfold",e,o,r)}),n.signal(e,"fold",e,u.from,u.to)}}n.newFoldFunction=function(n,o){return function(r,i){e(r,i,{rangeFinder:n,widget:o})}},n.defineExtension("foldCode",function(n,o,r){e(this,n,o,r)}),n.defineExtension("isFolded",function(n){for(var e=this.findMarksAt(n),o=0;o<e.length;++o)if(e[o].__isFold)return!0}),n.commands.toggleFold=function(n){n.foldCode(n.getCursor())},n.commands.fold=function(n){n.foldCode(n.getCursor(),null,"fold")},n.commands.unfold=function(n){n.foldCode(n.getCursor(),null,"unfold")},n.commands.foldAll=function(e){e.operation(function(){for(var o=e.firstLine(),r=e.lastLine();o<=r;o++)e.foldCode(n.Pos(o,0),null,"fold")})},n.commands.unfoldAll=function(e){e.operation(function(){for(var o=e.firstLine(),r=e.lastLine();o<=r;o++)e.foldCode(n.Pos(o,0),null,"unfold")})},n.registerHelper("fold","combine",function(){var n=Array.prototype.slice.call(arguments,0);return function(e,o){for(var r=0;r<n.length;++r){var i=n[r](e,o);if(i)return i}}}),n.registerHelper("fold","auto",function(n,e){for(var o=n.getHelpers(e,"fold"),r=0;r<o.length;r++){var i=o[r](n,e);if(i)return i}});var o={rangeFinder:n.fold.auto,widget:"↔",minFoldSize:0,scanUp:!1,clearOnEnter:!0};function r(n,e,r){if(e&&void 0!==e[r])return e[r];var i=n.options.foldOptions;return i&&void 0!==i[r]?i[r]:o[r]}n.defineOption("foldOptions",null),n.defineExtension("foldOption",function(n,e){return r(this,n,e)})});

View File

@ -0,0 +1 @@
.CodeMirror-foldmarker{color:blue;text-shadow:#b9f 1px 1px 2px,#b9f -1px -1px 2px,#b9f 1px -1px 2px,#b9f -1px 1px 2px;font-family:arial;line-height:.3;cursor:pointer}.CodeMirror-foldgutter{width:.7em}.CodeMirror-foldgutter-open,.CodeMirror-foldgutter-folded{cursor:pointer}.CodeMirror-foldgutter-open:after{content:"\25BE"}.CodeMirror-foldgutter-folded:after{content:"\25B8"}

View File

@ -0,0 +1 @@
!function(t){"object"==typeof exports&&"object"==typeof module?t(require("../../lib/codemirror"),require("./foldcode")):"function"==typeof define&&define.amd?define(["../../lib/codemirror","./foldcode"],t):t(CodeMirror)}(function(t){"use strict";t.defineOption("foldGutter",!1,function(o,e,r){r&&r!=t.Init&&(o.clearGutter(o.state.foldGutter.options.gutter),o.state.foldGutter=null,o.off("gutterClick",a),o.off("changes",d),o.off("viewportChange",u),o.off("fold",l),o.off("unfold",l),o.off("swapDoc",d)),e&&(o.state.foldGutter=new function(t){this.options=t,this.from=this.to=0}(function(t){!0===t&&(t={});null==t.gutter&&(t.gutter="CodeMirror-foldgutter");null==t.indicatorOpen&&(t.indicatorOpen="CodeMirror-foldgutter-open");null==t.indicatorFolded&&(t.indicatorFolded="CodeMirror-foldgutter-folded");return t}(e)),f(o),o.on("gutterClick",a),o.on("changes",d),o.on("viewportChange",u),o.on("fold",l),o.on("unfold",l),o.on("swapDoc",d))});var o=t.Pos;function e(t,e){for(var r=t.findMarks(o(e,0),o(e+1,0)),n=0;n<r.length;++n)if(r[n].__isFold){var i=r[n].find(-1);if(i&&i.line===e)return r[n]}}function r(t){if("string"==typeof t){var o=document.createElement("div");return o.className=t+" CodeMirror-guttermarker-subtle",o}return t.cloneNode(!0)}function n(t,n,f){var a=t.state.foldGutter.options,d=n-1,u=t.foldOption(a,"minFoldSize"),l=t.foldOption(a,"rangeFinder"),c="string"==typeof a.indicatorFolded&&i(a.indicatorFolded),s="string"==typeof a.indicatorOpen&&i(a.indicatorOpen);t.eachLine(n,f,function(n){++d;var i=null,f=n.gutterMarkers;if(f&&(f=f[a.gutter]),e(t,d)){if(c&&f&&c.test(f.className))return;i=r(a.indicatorFolded)}else{var p=o(d,0),m=l&&l(t,p);if(m&&m.to.line-m.from.line>=u){if(s&&f&&s.test(f.className))return;i=r(a.indicatorOpen)}}(i||f)&&t.setGutterMarker(n,a.gutter,i)})}function i(t){return new RegExp("(^|\\s)"+t+"(?:$|\\s)\\s*")}function f(t){var o=t.getViewport(),e=t.state.foldGutter;e&&(t.operation(function(){n(t,o.from,o.to)}),e.from=o.from,e.to=o.to)}function a(t,r,n){var i=t.state.foldGutter;if(i){var f=i.options;if(n==f.gutter){var a=e(t,r);a?a.clear():t.foldCode(o(r,0),f)}}}function d(t){var o=t.state.foldGutter;if(o){var e=o.options;o.from=o.to=0,clearTimeout(o.changeUpdate),o.changeUpdate=setTimeout(function(){f(t)},e.foldOnChangeTimeSpan||600)}}function u(t){var o=t.state.foldGutter;if(o){var e=o.options;clearTimeout(o.changeUpdate),o.changeUpdate=setTimeout(function(){var e=t.getViewport();o.from==o.to||e.from-o.to>20||o.from-e.to>20?f(t):t.operation(function(){e.from<o.from&&(n(t,e.from,o.from),o.from=e.from),e.to>o.to&&(n(t,o.to,e.to),o.to=e.to)})},e.updateViewportTimeSpan||400)}}function l(t,o){var e=t.state.foldGutter;if(e){var r=o.line;r>=e.from&&r<e.to&&n(t,r,r+1)}}});

View File

@ -0,0 +1 @@
!function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(e){"use strict";function n(n,t){var i=n.getLine(t),o=i.search(/\S/);return-1==o||/\bcomment\b/.test(n.getTokenTypeAt(e.Pos(t,o+1)))?-1:e.countColumn(i,null,n.getOption("tabSize"))}e.registerHelper("fold","indent",function(t,i){var o=n(t,i.line);if(!(o<0)){for(var r=null,l=i.line+1,f=t.lastLine();l<=f;++l){var u=n(t,l);if(-1==u);else{if(!(u>o))break;r=l}}return r?{from:e.Pos(i.line,t.getLine(i.line).length),to:e.Pos(r,t.getLine(r).length)}:void 0}})});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
!function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(e){"use strict";e.defineMode("yaml",function(){var e=new RegExp("\\b(("+["true","false","on","off","yes","no"].join(")|(")+"))$","i");return{token:function(i,t){var r=i.peek(),n=t.escaped;if(t.escaped=!1,"#"==r&&(0==i.pos||/\s/.test(i.string.charAt(i.pos-1))))return i.skipToEnd(),"comment";if(i.match(/^('([^']|\\.)*'?|"([^"]|\\.)*"?)/))return"string";if(t.literal&&i.indentation()>t.keyCol)return i.skipToEnd(),"string";if(t.literal&&(t.literal=!1),i.sol()){if(t.keyCol=0,t.pair=!1,t.pairStart=!1,i.match(/---/))return"def";if(i.match(/\.\.\./))return"def";if(i.match(/\s*-\s+/))return"meta"}if(i.match(/^(\{|\}|\[|\])/))return"{"==r?t.inlinePairs++:"}"==r?t.inlinePairs--:"["==r?t.inlineList++:t.inlineList--,"meta";if(t.inlineList>0&&!n&&","==r)return i.next(),"meta";if(t.inlinePairs>0&&!n&&","==r)return t.keyCol=0,t.pair=!1,t.pairStart=!1,i.next(),"meta";if(t.pairStart){if(i.match(/^\s*(\||\>)\s*/))return t.literal=!0,"meta";if(i.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i))return"variable-2";if(0==t.inlinePairs&&i.match(/^\s*-?[0-9\.\,]+\s?$/))return"number";if(t.inlinePairs>0&&i.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/))return"number";if(i.match(e))return"keyword"}return!t.pair&&i.match(/^\s*(?:[,\[\]{}&*!|>'"%@`][^\s'":]|[^,\[\]{}#&*!|>'"%@`])[^#]*?(?=\s*:($|\s))/)?(t.pair=!0,t.keyCol=i.indentation(),"atom"):t.pair&&i.match(/^:\s*/)?(t.pairStart=!0,"meta"):(t.pairStart=!1,t.escaped="\\"==r,i.next(),null)},startState:function(){return{pair:!1,pairStart:!1,keyCol:0,inlinePairs:0,inlineList:0,literal:!1,escaped:!1}},lineComment:"#",fold:"indent"}}),e.defineMIME("text/x-yaml","yaml"),e.defineMIME("text/yaml","yaml")});

View File

@ -0,0 +1 @@
.cm-s-dracula.CodeMirror,.cm-s-dracula .CodeMirror-gutters{background-color:#282a36 !important;color:#f8f8f2 !important;border:0}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:solid thin #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:rgba(255,255,255,0.10)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:rgba(255,255,255,0.10)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:rgba(255,255,255,0.10)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:white}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-keyword{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute{color:#50fa7b}.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-variable-3,.cm-s-dracula span.cm-type{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:rgba(255,255,255,0.1)}.cm-s-dracula .CodeMirror-matchingbracket{text-decoration:underline;color:white !important}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,51 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-airwhu
PKG_VERSION=1.0
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/luci-app-airwhu
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
DEPENDS:=+mentohust +kmod-ipt-nat6
TITLE:=luci-app-airwhu
PKGARCH:=all
endef
define Package/luci-app-airwhu/description
LuCI web-interface for Ruijie 802.1X Client with IPv6 NAT.
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./* $(PKG_BUILD_DIR)/
po2lmo $(PKG_BUILD_DIR)/po/airwhu.zh-cn.po $(PKG_BUILD_DIR)/po/airwhu.zh-cn.lmo
endef
define Build/Compile
endef
define Package/luci-app-airwhu/install
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_DIR) $(1)/etc/hotplug.d/iface
$(INSTALL_DIR) $(1)/bin
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
$(INSTALL_CONF) ./files/root/etc/config/airwhu $(1)/etc/config/airwhu
$(INSTALL_BIN) ./files/root/etc/init.d/mentohust $(1)/etc/init.d/mentohust
$(INSTALL_BIN) ./files/root/etc/hotplug.d/iface/99-ipv6nat $(1)/etc/hotplug.d/iface/99-ipv6nat
$(INSTALL_BIN) ./files/root/bin/ipv6masq.sh $(1)/bin/ipv6masq.sh
$(INSTALL_DATA) ./files/root/usr/lib/lua/luci/model/cbi/airwhu.lua $(1)/usr/lib/lua/luci/model/cbi/airwhu.lua
$(INSTALL_DATA) ./files/root/usr/lib/lua/luci/controller/airwhu.lua $(1)/usr/lib/lua/luci/controller/airwhu.lua
$(INSTALL_DATA) $(PKG_BUILD_DIR)/po/airwhu.zh-cn.lmo $(1)/usr/lib/lua/luci/i18n/airwhu.zh-cn.lmo
endef
$(eval $(call BuildPackage,luci-app-airwhu))

View File

@ -0,0 +1,41 @@
#!/bin/sh /etc/rc.common
#Author:KyleRicardo
#Email:shaoyz714@126.com
action=$1
if [ "$action"x = "install"x ]; then
uci delete network.globals.ula_prefix
uci set network.wan6.peerdns='0'
uci set network.wan6.dns='2001:da8:202:10::36'
uci delete network.lan.ip6assign
uci set network.lan.ip6addr='BABE:BABE:BABE:BABE::1/64'
uci set network.lan.ip6prefix='BABE:BABE:BABE:BABE::/64'
uci set dhcp.lan.ra_management='1'
uci set dhcp.lan.ra_default='1'
uci set $(uci show firewall|grep Allow-ICMPv6-Forward|sed -e 's/.name[^ ]*'//).enabled='0'
uci commit
elif [ "$action"x = "uninstall"x ]; then
uci set network.globals.ula_prefix='fdad:91b7:54bb::/48'
uci delete network.wan6.peerdns='0'
uci delete network.wan6.dns='2001:da8:202:10::36'
uci set network.lan.ip6assign='60'
uci delete network.lan.ip6addr='BABE:BABE:BABE:BABE::1/64'
uci delete network.lan.ip6prefix='BABE:BABE:BABE:BABE::/64'
uci delete dhcp.lan.ra_management='1'
uci delete dhcp.lan.ra_default='1'
uci delete $(uci show firewall|grep Allow-ICMPv6-Forward|sed -e 's/.name[^ ]*'//).enabled
uci commit
fi

View File

@ -0,0 +1,25 @@
config switch
option enableauth
option startwithboot
option enableipv6
config auth
option Username
option Password
option ifname
option IP
option Mask
option Gateway
option DNS
option PingHost
option Timeout
option EchoInterval
option RestartWait
option MaxFail
option StartMode
option DHCPMode
option DaemonMode
option ShowNotify
option Version
option DataFile
option DHCPScript

View File

@ -0,0 +1,56 @@
#!/bin/sh /etc/rc.common
#Author:KyleRicardo
#Email:shaoyz714@126.com
[ "$ACTION" = ifup ] || exit 0
iface=wan6
[ -z "$iface" -o "$INTERFACE" = "$iface" ] || exit 0
ip -6 route add `ip -6 route show default|sed -e 's/from [^ ]* //'`
logger -t IPv6 "Add IPv6 default route."
MAX_TRIES=99
WAN6_NAME="wan6"
WAN6_INTERFACE=$(uci get "network.$WAN6_NAME.ifname")
#eth0.2 by default
LAN_IP6PREFIX=$(uci get network.lan.ip6prefix)
#e.g. aaaa:bbbb:cccc:dddd::/64
#LAN_ULA_PREFIX=$(uci get network.globals.ula_prefix)
#e.g. ddc2:d512:65f5::/48
PROBE=0
COUNT=1
while [ $PROBE -eq 0 ]
do
if [ $COUNT -gt $MAX_TRIES ]
then
logger -t NAT6 "No IPv6 route found (reached retry limit $MAX_TRIES times)" && exit 1
fi
sleep 5
logger -t NAT6 "Probing IPv6 route ($COUNT time)"
COUNT=$((COUNT+1))
PROBE=$(route -A inet6 | grep -c '::/0')
done
#ip6tables -t nat -I POSTROUTING -s "$LAN_ULA_PREFIX" -o "$WAN6_INTERFACE" -j MASQUERADE
ip6tables -t nat -I POSTROUTING -s "$LAN_IP6PREFIX" -o "$WAN6_INTERFACE" -j MASQUERADE
#WAN6_GATEWAY=$(route -A inet6 -e | grep "$WAN6_INTERFACE" | awk '/::\/0/{print $2; exit}')
#get gateway from routing table. !!!Caution!!! May not work !
WAN6_GATEWAY=$(ifconfig eth0.2 | grep 'Global' | awk '{print $3}'| awk -F':' '{print $1":"$2":"$3":"$4"::1"}')
#caculate gateway from wan ipv6
#WAN6_GATEWAY=$(tracepath6 -n tv.byr.cn | grep ' 1: ' | awk 'NR==1 {print $2}')
#opkg install iputils-tracepath6 . change tv.byr.cn for faster site. e.g. ipv6.bjtu.edu.cn
#route -A inet6 add 2000::/3 gw "$WAN6_GATEWAY" dev "$WAN6_INTERFACE"
route -A inet6 add default gw "$WAN6_GATEWAY" dev "$WAN6_INTERFACE"
logger -t NAT6 "Done with IPv6 settings"

View File

@ -0,0 +1,49 @@
#!/bin/sh /etc/rc.common
#Author:KyleRicardo
#Email:shaoyz714@126.com
START=65
STOP=10
start()
{
if [ $(uci get airwhu.@switch[0].enableauth) ] ;
then
local Username=$(uci get airwhu.@auth[0].Username)
local Password=$(uci get airwhu.@auth[0].Password)
local ifname=$(uci get airwhu.@auth[0].ifname)
local IP=$(uci get airwhu.@auth[0].IP)
local Mask=$(uci get airwhu.@auth[0].Mask)
local Gateway=$(uci get airwhu.@auth[0].Gateway)
local DNS=$(uci get airwhu.@auth[0].DNS)
local PingHost=$(uci get airwhu.@auth[0].PingHost)
local Timeout=$(uci get airwhu.@auth[0].Timeout)
local EchoInterval=$(uci get airwhu.@auth[0].EchoInterval)
local RestartWait=$(uci get airwhu.@auth[0].RestartWait)
local MaxFail=$(uci get airwhu.@auth[0].MaxFail)
local StartMode=$(uci get airwhu.@auth[0].StartMode)
local DHCPMode=$(uci get airwhu.@auth[0].DHCPMode)
local DaemonMode=$(uci get airwhu.@auth[0].DaemonMode)
local ShowNotify=$(uci get airwhu.@auth[0].ShowNotify)
local Version=$(uci get airwhu.@auth[0].Version)
local DataFile=$(uci get airwhu.@auth[0].DataFile)
local DHCPScript=$(uci get airwhu.@auth[0].DHCPScript)
mentohust -u$Username -p$Password -n$ifname -b$DaemonMode -a$StartMode -d$DaemonMode -c"$DHCPScript" -w>/dev/null 2>&1
#Temporarily designed only for whu. To be continued...
else
stop
fi
}
stop()
{
sync
mentohust -k >/dev/null 2>&1
}
restart()
{
stop
start
}

View File

@ -0,0 +1,5 @@
module("luci.controller.airwhu", package.seeall)
function index()
entry({"admin", "services", "AirWHU"}, cbi("airwhu"), _("AirWHU"), 100)
end

View File

@ -0,0 +1,115 @@
--[[
LuCI - Lua Configuration Interface
LICENSE under GPLv3.
Copyright 2017 KyleRicardo[W.B.L.E. TeAm] <shaoyz714@126.com>
]]
--
require("luci.sys")
require("luci.tools.webadmin")
local IsOnAir = (luci.sys.call("pidof mentohust > /dev/null") == 0)
if IsOnAir then
state_msg = "<b><font color=\"green\">" .. translate("Running") .. "</font></b>"
else
state_msg = "<b><font color=\"red\">" .. translate("Not Running") .. "</font></b>"
end
m = Map("airwhu", translate("AirWHU"), translate("Configure Ruijie 802.1X client with IPv6 NAT based on Masquerade.") .. "<br /><br />" .. translate("Status") .. " : " .. state_msg)
s = m:section(TypedSection, "switch", translate("Global Switch"), translate("Configure global 802.1X Authentication and IPv6-NAT on-off."))
s.addremove = false
s.anonymous = true
s:option(Flag, "enableauth", translate("Enable 802.1X Auth"), translate("Enable or disable Ruijie 802.1X authentication."))
tmp = s:option(Flag, "startwithboot", translate("Start with boot"), translate("Start Ruijie 802.1X Authentication based on MentoHUST when the router is booting."))
tmp:depends("enableauth","1")
s:option(Flag, "enableipv6", translate("Enable IPv6 NAT"), translate("Enable IPv6 NAT pass-through based on ip6tables MASQUERADE."))
s = m:section(TypedSection, "auth", translate("Config Authentication"), translate("The options below are all of MentoHUST's arguments."))
s.anonymous = true
s.addremove = false
ur = s:option(Value, "Username", translate("Username"))
ur.rmempty = false
pw = s:option(Value, "Password", translate("Password"))
pw.password = true
pw.rmempty = false
wan_dev = luci.sys.exec("uci get network.wan.ifname")
wan_dev = string.sub(wan_dev,1,string.len(wan_dev)-1)
ifname = s:option(ListValue, "ifname", translate("Interfaces"))
ifname:value(wan_dev)
s:option(Value, "IP", translate("IP"), translate("default to localhost's IP")).default = "0.0.0.0"
s:option(Value, "Mask", translate("Netmask"), translate("default to localhost's netmask")).default = "255.255.255.0"
s:option(Value, "Gateway", translate("Gateway"), translate("default to 0.0.0.0")).default = "0.0.0.0"
s:option(Value, "DNS", translate("DNS"), translate("default to 0.0.0.0")).default = "0.0.0.0"
s:option(Value, "PingHost", translate("Ping host"), translate("default to 0.0.0.0,i.e. disable this function")).default = "0.0.0.0"
s:option(Value, "Timeout", translate("Authenticate timeout(s)"), translate("default to 8s")).default = "8"
s:option(Value, "EchoInterval", translate("Heartbeat timeout(s)"), translate("default to 30s")).default = "30"
s:option(Value, "RestartWait", translate("Timeout on failure(s)"), translate("default to 15s")).default = "15"
s:option(Value, "MaxFail", translate("Max failure times"), translate("0 means no limit,default to 8")).default = "8"
t = s:option(ListValue, "StartMode", translate("mulcast address"), translate("default to 1"))
t:value("0", translate("0(standard)"))
t:value("1", translate("1(ruijjie)"))
t:value("2", translate("2(saier)"))
t.default = "1"
t = s:option(ListValue, "DHCPMode", translate("DHCP type"), translate("default to 1"))
t:value("0", translate("0(not in used)"))
t:value("1", translate("1(secondary authenticate)"))
t:value("2", translate("2(post authenticate)"))
t:value("3", translate("3(pre authenticate)"))
t.default = "1"
t = s:option(ListValue, "DaemonMode", translate("run in daemon mode"), translate("default to 3"))
t:value("0", translate("0(no)"))
t:value("1", translate("1(yes,turn off output)"))
t:value("2", translate("2(yes,persevere output)"))
t:value("3", translate("3(yes,output to file)"))
t.default = "3"
s:option(Value, "ShowNotify", translate("display notification"), translate("0(no),1-20(yes),default to 0")).default = "0"
s:option(Value, "Version", translate("client version"), translate("default to 0.00,compatible with xrgsu")).default = "0.00"
tmp = s:option(Value, "DataFile", translate("customized data file"), translate("not in used by default"))
tmp.rmempty = true
script = s:option(Value, "DHCPScript", translate("DHCP script"), translate("use dhclient by default"))
script.default = "udhcpc -i "..wan_dev
local apply = luci.http.formvalue("cbi.apply")
if apply then
if luci.sys.exec("uci get airwhu.@switch[0].enableauth") then
luci.sys.exec("/etc/init.d/mentohust restart")
else
luci.sys.exec("/etc/init.d/mentohust stop")
end
if luci.sys.exec("uci get airwhu.@switch[0].startwithboot") then
luci.sys.exec("/etc/init.d/mentohust enable")
else
luci.sys.exec("/etc/init.d/mentohust disable")
end
if luci.sys.exec("uci get airwhu.@switch[0].enableipv6") then
luci.sys.exec("sh /bin/ipv6masq.sh install")
else
luci.sys.exec("sh /bin/ipv6masq.sh uninstall")
end
end
return m

View File

@ -0,0 +1,258 @@
msgid ""
msgstr ""
"Project-Id-Version: airwhu\n"
"POT-Creation-Date: 2017-05-11 19:39+0800\n"
"PO-Revision-Date: 2017-05-11 19:40+0800\n"
"Last-Translator: KyleRicardo <shaoyz714@126.com>\n"
"Language-Team: [W.B.L.E. TeAm] <shaoyz714@126.com>\n"
"Language: en_US\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.8\n"
"X-Poedit-Basepath: ../files\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: translate\n"
"X-Poedit-SearchPath-0: .\n"
#: root/usr/lib/lua/luci/controller/airwhu.lua:4
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:19
msgid "AirWHU"
msgstr "AirWHU"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:14
msgid "Running"
msgstr "运行中"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:16
msgid "Not Running"
msgstr "未运行"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:19
msgid "Configure Ruijie 802.1X client with IPv6 NAT based on Masquerade."
msgstr "配置基于IPv6 Masquerade技术的锐捷802.1X认证网络。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:19
msgid "Status"
msgstr "状态"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:21
msgid "Global Switch"
msgstr "全局开关"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:21
msgid "Configure global 802.1X Authentication and IPv6-NAT on-off."
msgstr "配置全局802.1X认证与IPv6 NAT开关。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:25
msgid "Enable 802.1X Auth"
msgstr "开启锐捷认证"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:25
msgid "Enable or disable Ruijie 802.1X authentication."
msgstr "打开或关闭锐捷802.1X认证。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:26
msgid "Start with boot"
msgstr "开机启动"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:26
msgid ""
"Start Ruijie 802.1X Authentication based on MentoHUST when the router is "
"booting."
msgstr "开机自动启动基于MentoHUST的锐捷802.1X认证客户端。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:28
msgid "Enable IPv6 NAT"
msgstr "开启IPv6 NAT"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:28
msgid "Enable IPv6 NAT pass-through based on ip6tables MASQUERADE."
msgstr "开启基于ip6tables Masquerade技术的IPv6 NAT穿透。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:30
msgid "Config Authentication"
msgstr "配置认证参数"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:30
msgid "The options below are all of MentoHUST's arguments."
msgstr "以下是全部的MentoHUST认证参数。"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:34
msgid "Username"
msgstr "用户名"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:36
msgid "Password"
msgstr "密码"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:41
msgid "Interfaces"
msgstr "网卡"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:51
msgid "IP"
msgstr "IP地址"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:51
msgid "default to localhost's IP"
msgstr "默认为localhost的IP地址"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:53
msgid "Netmask"
msgstr "掩码"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:53
msgid "default to localhost's netmask"
msgstr "默认为localhost的掩码"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:55
msgid "Gateway"
msgstr "网关"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:55
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:57
msgid "default to 0.0.0.0"
msgstr "默认为0.0.0.0"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:57
msgid "DNS"
msgstr "DNS服务器"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:59
msgid "Ping host"
msgstr "Ping主机"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:59
msgid "default to 0.0.0.0,i.e. disable this function"
msgstr "默认为0.0.0.0,即关闭该功能"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:61
msgid "Authenticate timeout(s)"
msgstr "认证超时(秒)"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:61
msgid "default to 8s"
msgstr "默认为8秒"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:63
msgid "Heartbeat timeout(s)"
msgstr "心跳包超时(秒)"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:63
msgid "default to 30s"
msgstr "默认为30秒"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:65
msgid "Timeout on failure(s)"
msgstr "失败等待(秒)"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:65
msgid "default to 15s"
msgstr "默认为15秒"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:67
msgid "Max failure times"
msgstr "允许失败次数"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:67
msgid "0 means no limit,default to 8"
msgstr "0表示无限制默认为8"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:69
msgid "mulcast address"
msgstr "多播地址"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:69
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:75
msgid "default to 1"
msgstr "默认为1"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:70
msgid "0(standard)"
msgstr "0标准"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:71
msgid "1(ruijjie)"
msgstr "1锐捷"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:72
msgid "2(saier)"
msgstr "2赛尔"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:75
msgid "DHCP type"
msgstr "DHCP方式"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:76
msgid "0(not in used)"
msgstr "0不使用"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:77
msgid "1(secondary authenticate)"
msgstr "1二次认证"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:78
msgid "2(post authenticate)"
msgstr "3认证后"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:79
msgid "3(pre authenticate)"
msgstr "3认证前"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:82
msgid "run in daemon mode"
msgstr "后台运行模式"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:82
msgid "default to 3"
msgstr "默认为3"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:83
msgid "0(no)"
msgstr "0不使用后台模式"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:84
msgid "1(yes,turn off output)"
msgstr "1后台模式关闭输出"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:85
msgid "2(yes,persevere output)"
msgstr "2后台模式保留输出"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:86
msgid "3(yes,output to file)"
msgstr "3后台模式输出到日志文件"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:89
msgid "display notification"
msgstr "显示通知"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:89
msgid "0(no),1-20(yes),default to 0"
msgstr "0不使用1-20使用默认为0"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:91
msgid "client version"
msgstr "客户端版本号"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:91
msgid "default to 0.00,compatible with xrgsu"
msgstr "默认为0.00表示兼容xrgsu"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:93
msgid "customized data file"
msgstr "自定义数据文件"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:93
msgid "not in used by default"
msgstr "默认不使用"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:96
msgid "DHCP script"
msgstr "DHCP脚本"
#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:96
msgid "use dhclient by default"
msgstr "默认使用dhclient"

View File

@ -0,0 +1,12 @@
INSTALL = install
PREFIX = /usr/bin
po2lmo: src/po2lmo.o src/template_lmo.o
$(CC) $(LDFLAGS) -o src/po2lmo src/po2lmo.o src/template_lmo.o
install:
$(INSTALL) -m 755 src/po2lmo $(PREFIX)
clean:
$(RM) src/po2lmo src/*.o

Binary file not shown.

View File

@ -0,0 +1,247 @@
/*
* lmo - Lua Machine Objects - PO to LMO conversion tool
*
* Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "template_lmo.h"
static void die(const char *msg)
{
fprintf(stderr, "Error: %s\n", msg);
exit(1);
}
static void usage(const char *name)
{
fprintf(stderr, "Usage: %s input.po output.lmo\n", name);
exit(1);
}
static void print(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
if( fwrite(ptr, size, nmemb, stream) == 0 )
die("Failed to write stdout");
}
static int extract_string(const char *src, char *dest, int len)
{
int pos = 0;
int esc = 0;
int off = -1;
for( pos = 0; (pos < strlen(src)) && (pos < len); pos++ )
{
if( (off == -1) && (src[pos] == '"') )
{
off = pos + 1;
}
else if( off >= 0 )
{
if( esc == 1 )
{
switch (src[pos])
{
case '"':
case '\\':
off++;
break;
}
dest[pos-off] = src[pos];
esc = 0;
}
else if( src[pos] == '\\' )
{
dest[pos-off] = src[pos];
esc = 1;
}
else if( src[pos] != '"' )
{
dest[pos-off] = src[pos];
}
else
{
dest[pos-off] = '\0';
break;
}
}
}
return (off > -1) ? strlen(dest) : -1;
}
static int cmp_index(const void *a, const void *b)
{
uint32_t x = ((const lmo_entry_t *)a)->key_id;
uint32_t y = ((const lmo_entry_t *)b)->key_id;
if (x < y)
return -1;
else if (x > y)
return 1;
return 0;
}
static void print_uint32(uint32_t x, FILE *out)
{
uint32_t y = htonl(x);
print(&y, sizeof(uint32_t), 1, out);
}
static void print_index(void *array, int n, FILE *out)
{
lmo_entry_t *e;
qsort(array, n, sizeof(*e), cmp_index);
for (e = array; n > 0; n--, e++)
{
print_uint32(e->key_id, out);
print_uint32(e->val_id, out);
print_uint32(e->offset, out);
print_uint32(e->length, out);
}
}
int main(int argc, char *argv[])
{
char line[4096];
char key[4096];
char val[4096];
char tmp[4096];
int state = 0;
int offset = 0;
int length = 0;
int n_entries = 0;
void *array = NULL;
lmo_entry_t *entry = NULL;
uint32_t key_id, val_id;
FILE *in;
FILE *out;
if( (argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL) )
usage(argv[0]);
memset(line, 0, sizeof(key));
memset(key, 0, sizeof(val));
memset(val, 0, sizeof(val));
while( (NULL != fgets(line, sizeof(line), in)) || (state >= 2 && feof(in)) )
{
if( state == 0 && strstr(line, "msgid \"") == line )
{
switch(extract_string(line, key, sizeof(key)))
{
case -1:
die("Syntax error in msgid");
case 0:
state = 1;
break;
default:
state = 2;
}
}
else if( state == 1 || state == 2 )
{
if( strstr(line, "msgstr \"") == line || state == 2 )
{
switch(extract_string(line, val, sizeof(val)))
{
case -1:
state = 4;
break;
default:
state = 3;
}
}
else
{
switch(extract_string(line, tmp, sizeof(tmp)))
{
case -1:
state = 2;
break;
default:
strcat(key, tmp);
}
}
}
else if( state == 3 )
{
switch(extract_string(line, tmp, sizeof(tmp)))
{
case -1:
state = 4;
break;
default:
strcat(val, tmp);
}
}
if( state == 4 )
{
if( strlen(key) > 0 && strlen(val) > 0 )
{
key_id = sfh_hash(key, strlen(key));
val_id = sfh_hash(val, strlen(val));
if( key_id != val_id )
{
n_entries++;
array = realloc(array, n_entries * sizeof(lmo_entry_t));
entry = (lmo_entry_t *)array + n_entries - 1;
if (!array)
die("Out of memory");
entry->key_id = key_id;
entry->val_id = val_id;
entry->offset = offset;
entry->length = strlen(val);
length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
print(val, length, 1, out);
offset += length;
}
}
state = 0;
memset(key, 0, sizeof(key));
memset(val, 0, sizeof(val));
}
memset(line, 0, sizeof(line));
}
print_index(array, n_entries, out);
if( offset > 0 )
{
print_uint32(offset, out);
fsync(fileno(out));
fclose(out);
}
else
{
fclose(out);
unlink(argv[2]);
}
fclose(in);
return(0);
}

View File

@ -0,0 +1,328 @@
/*
* lmo - Lua Machine Objects - Base functions
*
* Copyright (C) 2009-2010 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "template_lmo.h"
/*
* Hash function from http://www.azillionmonkeys.com/qed/hash.html
* Copyright (C) 2004-2008 by Paul Hsieh
*/
uint32_t sfh_hash(const char *data, int len)
{
uint32_t hash = len, tmp;
int rem;
if (len <= 0 || data == NULL) return 0;
rem = len & 3;
len >>= 2;
/* Main loop */
for (;len > 0; len--) {
hash += sfh_get16(data);
tmp = (sfh_get16(data+2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
data += 2*sizeof(uint16_t);
hash += hash >> 11;
}
/* Handle end cases */
switch (rem) {
case 3: hash += sfh_get16(data);
hash ^= hash << 16;
hash ^= data[sizeof(uint16_t)] << 18;
hash += hash >> 11;
break;
case 2: hash += sfh_get16(data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1: hash += *data;
hash ^= hash << 10;
hash += hash >> 1;
}
/* Force "avalanching" of final 127 bits */
hash ^= hash << 3;
hash += hash >> 5;
hash ^= hash << 4;
hash += hash >> 17;
hash ^= hash << 25;
hash += hash >> 6;
return hash;
}
uint32_t lmo_canon_hash(const char *str, int len)
{
char res[4096];
char *ptr, prev;
int off;
if (!str || len >= sizeof(res))
return 0;
for (prev = ' ', ptr = res, off = 0; off < len; prev = *str, off++, str++)
{
if (isspace(*str))
{
if (!isspace(prev))
*ptr++ = ' ';
}
else
{
*ptr++ = *str;
}
}
if ((ptr > res) && isspace(*(ptr-1)))
ptr--;
return sfh_hash(res, ptr - res);
}
lmo_archive_t * lmo_open(const char *file)
{
int in = -1;
uint32_t idx_offset = 0;
struct stat s;
lmo_archive_t *ar = NULL;
if (stat(file, &s) == -1)
goto err;
if ((in = open(file, O_RDONLY)) == -1)
goto err;
if ((ar = (lmo_archive_t *)malloc(sizeof(*ar))) != NULL)
{
memset(ar, 0, sizeof(*ar));
ar->fd = in;
ar->size = s.st_size;
fcntl(ar->fd, F_SETFD, fcntl(ar->fd, F_GETFD) | FD_CLOEXEC);
if ((ar->mmap = mmap(NULL, ar->size, PROT_READ, MAP_SHARED, ar->fd, 0)) == MAP_FAILED)
goto err;
idx_offset = ntohl(*((const uint32_t *)
(ar->mmap + ar->size - sizeof(uint32_t))));
if (idx_offset >= ar->size)
goto err;
ar->index = (lmo_entry_t *)(ar->mmap + idx_offset);
ar->length = (ar->size - idx_offset - sizeof(uint32_t)) / sizeof(lmo_entry_t);
ar->end = ar->mmap + ar->size;
return ar;
}
err:
if (in > -1)
close(in);
if (ar != NULL)
{
if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
munmap(ar->mmap, ar->size);
free(ar);
}
return NULL;
}
void lmo_close(lmo_archive_t *ar)
{
if (ar != NULL)
{
if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
munmap(ar->mmap, ar->size);
close(ar->fd);
free(ar);
ar = NULL;
}
}
lmo_catalog_t *_lmo_catalogs = NULL;
lmo_catalog_t *_lmo_active_catalog = NULL;
int lmo_load_catalog(const char *lang, const char *dir)
{
DIR *dh = NULL;
char pattern[16];
char path[PATH_MAX];
struct dirent *de = NULL;
lmo_archive_t *ar = NULL;
lmo_catalog_t *cat = NULL;
if (!lmo_change_catalog(lang))
return 0;
if (!dir || !(dh = opendir(dir)))
goto err;
if (!(cat = malloc(sizeof(*cat))))
goto err;
memset(cat, 0, sizeof(*cat));
snprintf(cat->lang, sizeof(cat->lang), "%s", lang);
snprintf(pattern, sizeof(pattern), "*.%s.lmo", lang);
while ((de = readdir(dh)) != NULL)
{
if (!fnmatch(pattern, de->d_name, 0))
{
snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
ar = lmo_open(path);
if (ar)
{
ar->next = cat->archives;
cat->archives = ar;
}
}
}
closedir(dh);
cat->next = _lmo_catalogs;
_lmo_catalogs = cat;
if (!_lmo_active_catalog)
_lmo_active_catalog = cat;
return 0;
err:
if (dh) closedir(dh);
if (cat) free(cat);
return -1;
}
int lmo_change_catalog(const char *lang)
{
lmo_catalog_t *cat;
for (cat = _lmo_catalogs; cat; cat = cat->next)
{
if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
{
_lmo_active_catalog = cat;
return 0;
}
}
return -1;
}
static lmo_entry_t * lmo_find_entry(lmo_archive_t *ar, uint32_t hash)
{
unsigned int m, l, r;
uint32_t k;
l = 0;
r = ar->length - 1;
while (1)
{
m = l + ((r - l) / 2);
if (r < l)
break;
k = ntohl(ar->index[m].key_id);
if (k == hash)
return &ar->index[m];
if (k > hash)
{
if (!m)
break;
r = m - 1;
}
else
{
l = m + 1;
}
}
return NULL;
}
int lmo_translate(const char *key, int keylen, char **out, int *outlen)
{
uint32_t hash;
lmo_entry_t *e;
lmo_archive_t *ar;
if (!key || !_lmo_active_catalog)
return -2;
hash = lmo_canon_hash(key, keylen);
for (ar = _lmo_active_catalog->archives; ar; ar = ar->next)
{
if ((e = lmo_find_entry(ar, hash)) != NULL)
{
*out = ar->mmap + ntohl(e->offset);
*outlen = ntohl(e->length);
return 0;
}
}
return -1;
}
void lmo_close_catalog(const char *lang)
{
lmo_archive_t *ar, *next;
lmo_catalog_t *cat, *prev;
for (prev = NULL, cat = _lmo_catalogs; cat; prev = cat, cat = cat->next)
{
if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
{
if (prev)
prev->next = cat->next;
else
_lmo_catalogs = cat->next;
for (ar = cat->archives; ar; ar = next)
{
next = ar->next;
lmo_close(ar);
}
free(cat);
break;
}
}
}

View File

@ -0,0 +1,92 @@
/*
* lmo - Lua Machine Objects - General header
*
* Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _TEMPLATE_LMO_H_
#define _TEMPLATE_LMO_H_
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <fnmatch.h>
#include <dirent.h>
#include <ctype.h>
#include <limits.h>
#if (defined(__GNUC__) && defined(__i386__))
#define sfh_get16(d) (*((const uint16_t *) (d)))
#else
#define sfh_get16(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+(uint32_t)(((const uint8_t *)(d))[0]) )
#endif
struct lmo_entry {
uint32_t key_id;
uint32_t val_id;
uint32_t offset;
uint32_t length;
} __attribute__((packed));
typedef struct lmo_entry lmo_entry_t;
struct lmo_archive {
int fd;
int length;
uint32_t size;
lmo_entry_t *index;
char *mmap;
char *end;
struct lmo_archive *next;
};
typedef struct lmo_archive lmo_archive_t;
struct lmo_catalog {
char lang[6];
struct lmo_archive *archives;
struct lmo_catalog *next;
};
typedef struct lmo_catalog lmo_catalog_t;
uint32_t sfh_hash(const char *data, int len);
uint32_t lmo_canon_hash(const char *data, int len);
lmo_archive_t * lmo_open(const char *file);
void lmo_close(lmo_archive_t *ar);
extern lmo_catalog_t *_lmo_catalogs;
extern lmo_catalog_t *_lmo_active_catalog;
int lmo_load_catalog(const char *lang, const char *dir);
int lmo_change_catalog(const char *lang);
int lmo_translate(const char *key, int keylen, char **out, int *outlen);
void lmo_close_catalog(const char *lang);
#endif

View File

@ -0,0 +1,15 @@
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=Open App Filter Module
LUCI_PKGARCH:=all
LUCI_DEPENDS:=+kmod-oaf +appfilter
PKG_NAME:=luci-app-appfilter
PKG_VERSION:=3.0
PKG_RELEASE:=1
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,96 @@
module("luci.controller.appfilter", package.seeall)
function index()
if not nixio.fs.access("/etc/config/appfilter") then
return
end
local page
page = entry({"admin", "network", "appfilter"}, cbi("appfilter/appfilter"), _("appfilter"))
page.dependent = true
page = entry({"admin", "network", "user_status"}, call("user_status"), nil)
page.leaf = true
end
function get_hostname_by_mac(dst_mac)
leasefile="/tmp/dhcp.leases"
local fd = io.open(leasefile, "r")
if not fd then return end
while true do
local ln = fd:read("*l")
if not ln then
break
end
local ts, mac, ip, name, duid = ln:match("^(%d+) (%S+) (%S+) (%S+) (%S+)")
print(ln)
if dst_mac == mac then
print("match mac", mac, "hostname=", name);
fd:close()
return name
end
end
fd:close()
return ""
end
function get_app_name_by_id(appid)
local class_fd = io.popen("find /etc/appfilter/ -type f -name *.class |xargs cat |grep "..appid.."|awk '{print $2}'")
if class_fd then
local name = class_fd:read("*l")
class_fd:close()
return name
end
return ""
end
function cmp_func(a,b)
return a.latest_time > b.latest_time
end
function user_status()
local s = require "luci.tools.status"
local json = require "luci.jsonc"
luci.http.prepare_content("application/json")
tb={}
obj={};
obj.hostname="derry"
obj.ip="192.168.10.199"
obj.mac="192.168.10.199"
tb[#tb+1]=obj
--local fs=require "nixio.fs"
--local ok, status_data = pcall(json.parse, fs.readfile("/proc/net/af_client"))
--luci.http.write_json(tb);
local fd = io.open("/proc/net/af_client","r")
status_buf=fd:read('*a')
fd:close()
user_array=json.parse(status_buf)
local history={}
for i, v in pairs(user_array) do
visit_array=user_array[i].visit_info
for j,s in pairs(visit_array) do
print(user_array[i].mac, user_array[i].ip,visit_array[j].appid, visit_array[j].latest_time)
history[#history+1]={
mac=user_array[i].mac,
ip=user_array[i].ip,
hostname=get_hostname_by_mac(user_array[i].mac),
appid=visit_array[j].appid,
appname=get_app_name_by_id(visit_array[j].appid),
total_num=visit_array[j].total_num,
drop_num=visit_array[j].drop_num,
latest_action=visit_array[j].latest_action,
latest_time=os.date("%Y/%m/%d %H:%M:%S", visit_array[j].latest_time)
}
end
end
table.sort(history, cmp_func)
--luci.http.write(history);
luci.http.write_json(history);
end

View File

@ -0,0 +1,87 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local ds = require "luci.dispatcher"
local m, s
m = Map("appfilter",
translate("appfilter"),
translate(""))
s = m:section(TypedSection, "global", translate("Basic Settings"))
s:option(Flag, "enable", translate("Enable App Filter"),translate(""))
s.anonymous = true
s = m:section(TypedSection, "appfilter", translate("App Filter Rules"))
s.anonymous = true
s.addremove = false
local class_fd = io.popen("find /etc/appfilter/ -type f -name '*.class'")
if class_fd then
while true do
local apps
local class
local path = class_fd:read("*l")
if not path then
break
end
class = path:match("([^/]+)%.class$")
-- add a tab
s:tab(class, translate(class))
-- multi value option
apps = s:taboption(class, MultiValue, class.."apps", translate(""))
apps.rmempty=true
--apps.delimiter=";"
-- select
apps.widget="checkbox"
apps.size=12
local fd = io.open(path)
if fd then
local line
while true do
local cmd
local cmd_fd
line = fd:read("*l")
if not line then break end
if string.len(line) < 5 then break end
if not string.find(line,"#") then
cmd = "echo "..line.."|awk '{print $1}'"
cmd_fd = io.popen(cmd)
id = cmd_fd:read("*l");
cmd_fd:close()
cmd = "echo "..line.."|awk '{print $2}'"
cmd_fd = io.popen(cmd)
name = cmd_fd:read("*l")
cmd_fd:close()
if not id then break end
if not name then break end
apps:value(id, name)
end
end
fd:close()
end
end
class_fd:close()
end
m:section(SimpleSection).template = "admin_network/user_status"
return m

View File

@ -0,0 +1,85 @@
<style type="text/css">
table.imagetable {
font-family: verdana,arial,sans-serif;
font-size:11px;
color:#333333;
border-width: 1px;
border-color: #999999;
border-collapse: collapse;
}
table.imagetable th {
background:#b5cfd2 url('cell-blue.jpg');
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #999999;
}
table.imagetable td {
background:#dcddc0 url('cell-grey.jpg');
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #999999;
}
</style>
<script type="text/javascript">//<![CDATA[
XHR.poll(10, '<%=url('admin/network/user_status')%>', null,
function(x, st)
{
var tb = document.getElementById('user_status_table');
var str=JSON.stringify(st);
if (st && tb)
{
/* clear all rows */
while(tb.rows.length > 1)
tb.deleteRow(1);
for(var i = 0; i < st.length; i++ )
{
var action_status=""
if(st[i].latest_action == 1)
action_status="已过滤"
else
action_status="未过滤"
var hostname=""
if(st[i].hostname == "" || st[i].hostname == "*"){
hostname="?";
}
else{
hostname=st[i].hostname;
}
var tr = tb.insertRow(-1);
//tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
tr.insertCell(-1).innerHTML = hostname;
tr.insertCell(-1).innerHTML = st[i].mac;
tr.insertCell(-1).innerHTML = st[i].ip;
tr.insertCell(-1).innerHTML = st[i].appname;
tr.insertCell(-1).innerHTML = st[i].drop_num;
tr.insertCell(-1).innerHTML = st[i].total_num;
tr.insertCell(-1).innerHTML = st[i].latest_time;
tr.insertCell(-1).innerHTML = action_status;
}
}
}
);
//]]></script>
<fieldset class="cbi-section">
<legend><%:访问记录%></legend>
<table class="imagetable" id="user_status_table">
<tr>
<th ><%:主机名%></th>
<th ><%:mac地址%></th>
<th ><%:ip地址%></th>
<th ><%:App名称%></th>
<th><%:丢包次数%></th>
<th ><%:访问次数%></th>
<th><%:最后访问时间%></th>
<th><%:过滤状态%></th>
</tr>
<tr>
<td colspan="8"><em><br /><%:Collecting data...%></em></td>
</tr>
</table>
</fieldset>

View File

@ -0,0 +1,48 @@
msgid "website"
msgstr "常用网站"
msgid "appfilter"
msgstr "应用过滤"
msgid "game"
msgstr "游戏"
msgid "web"
msgstr "网页"
msgid "video"
msgstr "视频"
msgid "chat"
msgstr "聊天"
msgid "download"
msgstr "下载"
msgid "p2p"
msgstr "p2p"
msgid "music"
msgstr "音乐"
msgid "shopping"
msgstr "购物"
msgid "working"
msgstr "办公"
msgid "employee"
msgstr "招聘"
msgid "Basic Settings"
msgstr "基本设置"
msgid "App Filter Rules"
msgstr "应用过滤规则"
msgid "Enable App Filter"
msgstr "开启应用过滤"

View File

@ -0,0 +1,14 @@
#!/bin/sh
# replace existing mwan ucitrack entry
uci -q batch <<-EOF >/dev/null
del ucitrack.@appfilter[-1]
add ucitrack appfilter
set ucitrack.@appfilter[-1].exec="/etc/init.d/appfilter reload"
commit ucitrack
EOF
# remove LuCI cache
rm -rf /tmp/luci-indexcache /tmp/luci-modulecache
exit 0

View File

@ -0,0 +1,64 @@
# Copyright (C) 2018-2019 Lienol
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-autoipsetadder
PKG_VERSION:=1.0
PKG_RELEASE:=7
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/luci-app-autoipsetadder
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=LuCI Support for autoipsetadder
PKGARCH:=all
DEPENDS:= +httping +curl
endef
define Package/luci-app-autoipsetadder/description
LuCI support for autoipsetadder
endef
define Build/Prepare
endef
define Build/Compile
endef
define Package/luci-app-autoipsetadder/conffiles
/etc/config/autoipsetadder
endef
define Package/luci-app-autoipsetadder/install
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
cp -pR ./luasrc/* $(1)/usr/lib/lua/luci
$(INSTALL_DIR) $(1)/
cp -pR ./root/* $(1)/
endef
define Package/luci-app-autoipsetadder/postinst
#!/bin/sh
/etc/init.d/autoipsetadder enable >/dev/null 2>&1
/etc/init.d/autoipsetadder start
rm -f /tmp/luci-indexcache
rm -f /tmp/luci-modulecache/*
exit 0
endef
define Package/luci-app-autoipsetadder/prerm
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
/etc/init.d/autoipsetadder disable
/etc/init.d/autoipsetadder stop
fi
exit 0
endef
$(eval $(call BuildPackage,luci-app-autoipsetadder))

View File

@ -0,0 +1,56 @@
module("luci.controller.autoipsetadder",package.seeall)
local io = require "io"
local fs=require"nixio.fs"
local sys=require"luci.sys"
local uci=require"luci.model.uci".cursor()
function index()
entry({"admin","services","autoipsetadder"},firstchild(),_("autoipsetadder"),30).dependent=true
entry({"admin","services","autoipsetadder","autoipsetadder"},cbi("autoipsetadder"),_("Base Setting"),1)
entry({"admin","services","autoipsetadder","status"},call("act_status")).leaf=true
entry({"admin", "services", "autoipsetadder", "getlog"}, call("get_log"))
entry({"admin", "services", "autoipsetadder", "dodellog"}, call("do_del_log"))
entry({"admin", "services", "autoipsetadder", "debugip"}, call("do_debug_ip"))
end
function act_status()
local e={}
e.running=luci.sys.call("pgrep -f /usr/bin/autoipsetadder/autoaddlist.sh >/dev/null")==0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
function do_del_log()
local logfile=uci:get("autoipsetadder","autoipsetadder","logfile") or "/tmp/addlist.log"
nixio.fs.writefile(logfile,"")
luci.http.prepare_content("application/json")
luci.http.write('')
end
function do_debug_ip()
luci.http.prepare_content("text/plain; charset=utf-8")
local a=sys.exec("/usr/bin/autoipsetadder/debugip.sh")
if (a=="") then
a="noproblem"
end
luci.http.write(a)
end
function get_log()
local logfile,fdp
logfile=uci:get("autoipsetadder","autoipsetadder","logfile") or "/tmp/addlist.log"
luci.http.prepare_content("text/plain; charset=utf-8")
if not fs.access(logfile) then
luci.http.write("")
return
end
if fs.access("/var/run/lucilogreload") then
fdp=0
fs.remove("/var/run/lucilogreload")
else
fdp=tonumber(fs.readfile("/var/run/lucilogpos")) or 0
end
local f=io.open(logfile, "r+")
f:seek("set",fdp)
local a=f:read(2048000) or ""
fdp=f:seek()
fs.writefile("/var/run/lucilogpos",tostring(fdp))
f:close()
luci.http.write(a)
end

View File

@ -0,0 +1,97 @@
require("luci.sys")
require("luci.util")
local fs=require"nixio.fs"
local uci=require"luci.model.uci".cursor()
local m,s,o
m = Map("autoipsetadder", translate("ipsetautoadder"))
m.description = translate("自动将联不通的域名加入ipset")
m:section(SimpleSection).template = "autoipsetadder/status"
s = m:section(TypedSection, "autoipsetadder")
s.anonymous=true
s.addremove=false
---- enable
o = s:option(Flag, "enabled", translate("启用"))
o.default = 0
o.rmempty = false
---- logview
o=s:option(TextValue, "show", "日志")
o.template = "autoipsetadder/check"
---- logpath
o = s:option(Value, "logfile", translate("Runtime log file"))
o.datatype = "string"
o.default="/tmp/addlist.log"
o.optional = false
o.validate=function(self, value)
if fs.stat(value,"type")=="dir" then
fs.rmdir(value)
end
if fs.stat(value,"type")=="dir" then
if m.message then
m.message =m.message.."\nerror!log file is a dir"
else
m.message ="error!log file is a dir"
end
return nil
end
return value
end
---- dnsmasq log
o = s:option(Value, "dnslogfile", translate("dnsmasq log file"))
o.datatype = "string"
o.optional = false
o.default="/tmp/dnsmasq.log"
o.validate=function(self, value)
if fs.stat(value,"type")=="dir" then
fs.rmdir(value)
end
if fs.stat(value,"type")=="dir" then
if m.message then
m.message =m.message.."\nerror!log file is a dir"
else
m.message ="error!log file is a dir"
end
return nil
end
return value
end
---- crontab
o = s:option(MultiValue, "crontab", translate("Crontab task"),translate("Please change time and args in crontab"))
o:value("autodeldnslog",translate("Auto del dnsmasq log"))
o:value("autotaillog",translate("Auto tail runtime log"))
o.widget = "checkbox"
o.default = "autodeldnslog autotaillog"
o.rmempty= true
o = s:option(MultiValue, "config", translate("the way add to gfwlist"))
o:value("nochina",translate("no china ip"))
o:value("pingadd",translate("5ping loss1-4"))
o:value("packetpass",translate("packet >12 pass"))
o.widget = "checkbox"
o.default = "nochina pingadd packetpass"
o.rmempty=true
---- apply
nixio.fs.writefile("/var/run/lucilogreload","")
function m.on_commit(map)
local ucitracktest=uci:get("autoipsetadder","autoipsetadder","ucitracktest")
if ucitracktest=="1" then
return
elseif ucitracktest=="0" then
io.popen("/etc/init.d/autoipsetadder reload &")
else
if (fs.access("/var/run/AdGucitest")) then
uci:set("autoipsetadder","autoipsetadder","ucitracktest","0")
io.popen("/etc/init.d/autoipsetadder reload &")
else
fs.writefile("/var/run/AdGucitest","")
if (ucitracktest=="2") then
uci:set("autoipsetadder","autoipsetadder","ucitracktest","1")
else
uci:set("autoipsetadder","autoipsetadder","ucitracktest","2")
end
end
uci:save("autoipsetadder")
uci:commit("autoipsetadder")
end
end
return m

View File

@ -0,0 +1,62 @@
<%+cbi/valueheader%>
<%uci=require"luci.model.uci".cursor()%>
<%nixio=require"nixio"%>
<%if uci:get("autoipsetadder","autoipsetadder","enabled")=="1" then%>
<textarea id="cbid.logview.1.conf" class="cbi-input-textarea" style="width: 100%;display:inline" data-update="change" rows="10" cols="60" readonly="readonly" > </textarea>
<input type="checkbox" value="reverse" onclick=" return reverselog()" style="vertical-align:middle;height: auto;" checked><%:reverse%></input>
<input type="button" class="cbi-button cbi-button-apply" id="apply_update_button" value="dellog" onclick=" return apply_del_log() "/>
<input type="button" class="cbi-button cbi-button-apply" id="apply_update_button" value="debug" onclick=" return debug_ip() "/>
<textarea id="cbid.logview.2.conf" class="cbi-input-textarea" style="width: 100%;display:none" data-update="change" rows="10" cols="60" readonly="readonly" > </textarea>
<script type="text/javascript">//<![CDATA[
var islogreverse = true;
function apply_del_log(){
XHR.get('<%=url([[admin]], [[services]], [[autoipsetadder]], [[dodellog]])%>',null,function(x, data){
var lv = document.getElementById('cbid.logview.1.conf');
lv.innerHTML="";
}
);
return
}
function reverselog(){
var lv = document.getElementById('cbid.logview.1.conf');
lv.innerHTML=lv.innerHTML.split('\n').reverse().join('\n')
if (islogreverse){
islogreverse=false;
}else{
islogreverse=true;
}
return
}
function debug_ip(){
var lv2 = document.getElementById('cbid.logview.2.conf');
lv2.style.display="inline"
XHR.get('<%=url([[admin]], [[services]], [[autoipsetadder]], [[debugip]])%>',null,function(x, data){
var lv2 = document.getElementById('cbid.logview.2.conf');
lv2.innerHTML = x.responseText;
}
);
return
}
function poll_check(){
XHR.poll(3, '<%=url([[admin]], [[services]], [[autoipsetadder]], [[getlog]])%>', null,
function(x, data) {
var lv = document.getElementById('cbid.logview.1.conf');
if (x.responseText && lv) {
if (islogreverse){
lv.innerHTML = x.responseText.split('\n').reverse().join('\n')+lv.innerHTML;
}else{
lv.innerHTML += x.responseText;
}
}
}
);}
poll_check();
//]]>
</script>
<%end%>
<%+cbi/valuefooter%>

View File

@ -0,0 +1,22 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(3, '<%=url([[admin]], [[services]], [[autoipsetadder]], [[status]])%>', null,
function(x, data) {
var tb = document.getElementById('autoipsetadder_status');
if (data && tb) {
if (data.running) {
var links = '<em><b><font color=green>autoipsetadder <%:RUNNING%></font></b></em>';
tb.innerHTML = links;
} else {
tb.innerHTML = '<em><b><font color=red>autoipsetadder <%:NOT RUNNING%></font></b></em>';
}
}
}
);
//]]>
</script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="autoipsetadder_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>

View File

@ -0,0 +1,2 @@
config autoipsetadder 'autoipsetadder'
option enabled '0'

View File

@ -0,0 +1,126 @@
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=99
STOP=01
CRON_FILE=/etc/crontabs/root
CONFIGURATION=autoipsetadder
EXTRA_COMMANDS="test_crontab"
EXTRA_HELP="
test_crontab"
set_dnsmasq_log()
{
sed -i '/log-facility/d' /etc/dnsmasq.conf
sed -i '/log-queries/d' /etc/dnsmasq.conf
uci set dhcp.@dnsmasq[0].logfacility='/tmp/dnsmasq.log'
uci delete dhcp.@dnsmasq[0].logqueries
echo log-queries >> /etc/dnsmasq.conf
uci commit dhcp
/etc/init.d/dnsmasq reload
}
stop_dnsmasq_log()
{
sed -i '/log-queries/d' /etc/dnsmasq.conf
uci delete dhcp.@dnsmasq[0].logfacility
uci commit dhcp
/etc/init.d/dnsmasq reload
}
reload_service()
{
rm -f /var/run/AdGucitest 2>/dev/null
kill $(cat /var/run/autoipsetadder.pid)
start
}
service_triggers() {
procd_add_reload_trigger "$CONFIGURATION"
}
start_service() {
# Reading config
config_load "${CONFIGURATION}"
mkdir -p /tmp/run/autoipsetadder
local enabled
config_get_bool enabled $CONFIGURATION enabled 0
do_crontab
if [ "$enabled" == "1" ]; then
set_dnsmasq_log
procd_open_instance
procd_set_param respawn
# pass config to script on start
procd_set_param command sh /usr/bin/autoipsetadder/autoaddlist.sh
procd_close_instance
echo "autoipsetadder turn on"
echo "enabled=$enabled"
else
stop_dnsmasq_log
echo "autoipsetadder turn off"
echo "enabled=$enabled"
fi
}
stop_service()
{
config_load "${CONFIGURATION}"
stop_dnsmasq_log
do_crontab
kill $(cat /var/run/autoipsetadder.pid)
echo "autoipsetadder turn off"
echo "enabled="$enabled""
}
do_crontab(){
config_get_bool enabled $CONFIGURATION enabled 0
#config_get logfile $CONFIGURATION logfile "/tmp/addlist.log"
#config_get dnslogfile $CONFIGURATION dnslogfile "/tmp/dnsmasq.log"
config_get crontab $CONFIGURATION crontab ""
cronreload=0
findstr="echo qingkong > \$(uci get autoipsetadder.autoipsetadder.dnslogfile)"
default="0 * * * * echo qingkong > \$(uci get autoipsetadder.autoipsetadder.dnslogfile)"
#[ -n "$lastdnslogfile" ] && findstr="echo qingkong > $lastdnslogfile" && [ "$lastdnslogfile" != "$dnslogfile" ] && replace="${lastdnslogfile//\//\\/}/${dnslogfile//\//\\/}"
[ "$enabled" == "0" ] || [ "${crontab//autodeldnslog/}" == "$crontab" ] && cronenable=0 || cronenable=1
crontab_editor
#[ "$lastdnslogfile" != "$dnslogfile" ] && uci set autoipsetadder.autoipsetadder.lastdnslogfile="$dnslogfile" && commit=1
findstr="/usr/bin/autoipsetadder/tailto.sh [0-9]* \$(uci get autoipsetadder.autoipsetadder.logfile)"
default="0 0 * * * /usr/bin/autoipsetadder/tailto.sh 2000 \$(uci get autoipsetadder.autoipsetadder.logfile)"
#[ -n "$lastlogfile" ] && findstr="/usr/bin/autoipsetadder/tailto.sh [0-9]* $lastlogfile" && [ "$lastlogfile" != "$logfile" ] && replace="${lastlogfile//\//\\/}/${logfile//\//\\/}"
[ "$enabled" == "0" ] || [ "${crontab//autotaillog/}" == "$crontab" ] && cronenable=0 || cronenable=1
crontab_editor
#[ -n "$logfile" ] && [ "$lastlogfile" != "$logfile" ] && uci set autoipsetadder.autoipsetadder.lastlogfile="$logfile" && commit=1
[ "$cronreload" -gt 0 ] && /etc/init.d/cron restart
#[ "$commit" -gt 0 ] && uci commit autoipsetadder
}
crontab_editor(){
local testline reload
local line="$(grep "$findstr" $CRON_FILE)"
[ -n "$replace" ] && [ -n "$line" ] && eval testline="\${line//$replace}" && [ "$testline" != "$line" ] && line="$testline" && reload="1" && replace=""
if [ "${line:0:1}" != "#" ]; then
if [ $cronenable -eq 1 ]; then
[ -z "$line" ] && line="$default" && reload="1"
if [ -n "$reload" ]; then
sed -i "\,$findstr,d" $CRON_FILE
echo "$line" >> $CRON_FILE
cronreload=$((cronreload+1))
fi
elif [ -n "$line" ]; then
sed -i "\,$findstr,d" $CRON_FILE
echo "#$line" >> $CRON_FILE
cronreload=$((cronreload+1))
fi
else
if [ $cronenable -eq 1 ]; then
sed -i "\,$findstr,d" $CRON_FILE
echo "${line:1}" >> $CRON_FILE
cronreload=$((cronreload+1))
elif [ -z "$reload" ]; then
sed -i "\,$findstr,d" $CRON_FILE
echo "$line" >> $CRON_FILE
fi
fi
}
test_crontab(){
config_load "${CONFIGURATION}"
do_crontab
}

View File

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

View File

@ -0,0 +1,164 @@
#!/bin/sh
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
logfile=$(uci get autoipsetadder.autoipsetadder.logfile)
[ -z "$logfile" ] && logfile="/tmp/addlist.log"
dnslogfile=$(uci get autoipsetadder.autoipsetadder.dnslogfile)
[ -z "$logfile" ] && dnslogfile="/tmp/dnsmasq.log"
config=$(uci get autoipsetadder.autoipsetadder.config 2>/dev/null)
[ "${config//nochina/}" == "$config" ] && nochina="0" || nochina="1"
[ "${config//packetpass/}" == "$config" ] && packetpass="0" || packetpass="1"
(tail -F $dnslogfile & echo $! >/var/run/autoipsetadder.pid ) | awk -v nochina="$nochina" -v packetpass="$packetpass" -F "[, ]+" '/reply/{
ip=$8;
if (ip=="" || ip=="127.0.0.1"|| ip=="0.0.0.0")
{
next;
}
if (index(ip,"<CNAME>")!=0)
{
if (cname==1)
{
next;
}
cname=1;
domain=$6;
#第一次cname时锁定域名防止解析cname对其改动
next;
}
#以上获得上行是否为cname本行不是cname执行以下内容
#lastdomain记录上次非cname的域名与本次域名对比
if(lastdomain!=$6){
for (ipindex in ipcache)
{
delete ipcache[ipindex];
}
ipcount=0;
createpid=1;
#上行非cname并且不是同cname解析域名的多个ip更新域名清理同域名免试flag
if (cname!=1)
{
domain=$6;
testall=0;
}}
ipcount++;
cname=0;
lastdomain=$6
#去除非ipv4
if (index(ip,".")==0)
{
next;
}
#不重复探测ip
if (!(ip in a))
{
#包数>12的同域名放过
if (passdomain==domain)
{
print(ip" "domain" pass by same domain ok");
a[ip]=domain;
next;
}
if (nochina==1){
"ipset test china "ip" 2>&1"| getline ipset;
close("ipset test china "ip" 2>&1");
if (index(ipset,"Warning")!=0){
print("china "ip" pass");
a[ip]=domain;
next;
}}
"ipset test gfwlist "ip" 2>&1"| getline ipset;
close("ipset test gfwlist "ip" 2>&1");
if (index(ipset,"Warning")!=0){
print("gfwlist "ip" pass");
a[ip]=domain;
next;
}
#ip压入缓存用于未检测到443/80的缓存
ipcache[ipcount]=ip;
if (testall==0){
tryhttps=0;
tryhttp=0;
#探测 nf_conntrack 的443/80
while ("grep "ip" /proc/net/nf_conntrack"| getline ret > 0)
{
split(ret, b," +");
split(b[11], pagnum,"=");
#包数>12的放过
if (packetpass==1 && pagnum[2]>12)
{
print("pass by packets="pagnum[2]" "ip" "domain);
for (ipindex in ipcache)
{
a[ipcache[ipindex]]=domain;
delete ipcache[ipindex];
}
passdomain=domain;
close("grep "ip" /proc/net/nf_conntrack");
ipcount--;
next;
}
if (b[8]=="dst="ip)
{
if (b[10]=="dport=443"){
tryhttps=1;
break;
}
else if (b[10]=="dport=80"){
tryhttp=1;
}
}
}
close("grep "ip" /proc/net/nf_conntrack");
}else{
if (testall==443)
{
tryhttps=1
}else{
tryhttp=1
}
}
if (tryhttps==1)
{ if (createpid==1)
{
print "">"/tmp/run/autoipsetadder/"domain
close("/tmp/run/autoipsetadder/"domain);
print("create"domain);
print(ip" "domain" 443"ipcount-1);
a[ip]=domain;
#正在使用的ip用最大延迟最后探测减少打断tcp的可能
system("/usr/bin/autoipsetadder/testip.sh "ip" "domain" 443 "ipcount-1" &");
delete ipcache[ipcount];
createpid=0;
}
#未检测到443/80同域名缓存的ip进行测试ipindex-1为测试延迟时间
for (ipindex in ipcache){
print(ipcache[ipindex]" "domain" 443 "ipindex-1);
a[ipcache[ipindex]]=domain;
system("/usr/bin/autoipsetadder/testip.sh "ipcache[ipindex]" "domain" 443 "ipindex-1" &");
delete ipcache[ipindex];
}
#后续同域名ip免nf_conntrack测试
testall=443;
}
else if (tryhttp==1)
{
if (createpid==1)
{
print "">"/tmp/run/autoipsetadder/"domain
close("/tmp/run/autoipsetadder/"domain);
print("create"domain);
print(ip" "domain" 80 "ipcount-1);
a[ip]=domain;
system("/usr/bin/autoipsetadder/testip.sh "ip" "domain" 80 "ipcount-1" &");
delete ipcache[ipcount];
createpid=0;
}
for (ipindex in ipcache){
print(ipcache[ipindex]" "domain" 80 "ipindex-1);
a[ipcache[ipindex]]=domain;
system("/usr/bin/autoipsetadder/testip.sh "ipcache[ipindex]" "domain" 80 "ipindex-1" &");
delete ipcache[ipindex];
}
testall=80;
}}
}' >> $logfile

View File

@ -0,0 +1,56 @@
#!/bin/sh
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
dlchina=$1;
logfile=$(uci get autoipsetadder.autoipsetadder.logfile)
[ -z "$logfile" ] && logfile="/tmp/addlist.log"
dnslogfile=$(uci get autoipsetadder.autoipsetadder.dnslogfile)
[ -z "$logfile" ] && dnslogfile="/tmp/dnsmasq.log"
ipset list gfwlist | awk -v dlchina="$dlchina" -v dnslogfile="$dnslogfile" -v logfile="$logfile" '{
if (index($0,".")==0)
{
next;
}
if ($0=="127.0.0.1") {system("ipset d gfwlist 127.0.0.1");next;}
"ipset test whitelist "$0" 2>&1"| getline ipset;
close("ipset test whitelist "$0" 2>&1");
if (index(ipset,"Warning")==0){
white=0;
}else{
white=1;
}
"ipset test china "$0" 2>&1"| getline ipset;
close("ipset test china "$0" 2>&1");
if (index(ipset,"Warning")!=0){
china=1;
}
else{
china=0;
}
if (white==1)
{
if (china==0)
{
print("warning white ip not china"$0);
ret=system("grep "$0" "logfile);
if (ret!=0)
{
ret=system("grep "$0" "dnslogfile);
}
}
}else if (china==1)
{
print("warning china ip not white"$0);
ret=system("grep "$0" "logfile)
if (ret!=0)
{
ret=system("grep "$0" "dnslogfile);
}
if (dlchina)
{
system("ipset del gfwlist "$0);
}
}
}'

View File

@ -0,0 +1,4 @@
#!/bin/sh
tail -n $1 "$2" > /var/run/tailtmp
cat /var/run/tailtmp > "$2"
rm /var/run/tailtmp

View File

@ -0,0 +1,182 @@
#!/bin/sh
config=$(uci get autoipsetadder.autoipsetadder.config 2>/dev/null)
[ "${config//pingadd/}" == "$config" ] && pingadd="0" || pingadd="1"
echo $* | awk -v pingadd="$pingadd" '{
if ($4=="")
{
wait=0;
}else
{wait=$4;}
system("sleep "wait);
ERRNO="";
pidfile="/tmp/run/autoipsetadder/"$2
getline drop< pidfile;
close(pidfile);
if (ERRNO) {
addlist=0;
print("bypass"$1" "$2);
next;}
if ($3=="443")
{
cmd=("httping -c 1 -t 4 -l "$2" --divert-connect "$1);
}
else if ($3=="80")
{
cmd=("httping -c 1 -t 4 "$2" --divert-connect "$1);
}
addlist=0;
slow=0;
while ((cmd | getline ret) > 0)
{
if (addlist!=0)
{
continue;
}
else if (index(ret,"short read")!=0)
{
if (system("httping -q -c 1 -t 4 -l "$2" --divert-connect "$1)==0)
{
addlist=-1;
break;
}else{
print("doname rst autoaddip "$1" "$2);
addlist=1;
}
}
else if (index(ret,"timeout")!=0)
{
while ((cmd | getline ret) > 0)
{
if (index(ret,"timeout")!=0)
{
print("direct so slow autoaddip "$1" "$2);
addlist=1;
slow=1;
}
}
}else if (index(ret,"SSL handshake error: (null)")!=0)
{
if(system("curl -m 10 --resolve "$2":443:"$1" https://"$2" -o /dev/null 2>/dev/null")==0){
addlist=-1;
break;
}
}else if (index(ret,"Connection refused")!=0){
print("direct Connection refused autoaddip"$1" "$2);
addlist=1;
}
}
close(cmd);
if (addlist!=1)
{
if (addlist==0){
split(ret, c,"[ /]+");
print(c[6]);
if (c[6]=="failed,"){
print("can not connect autoaddip "$1" "$2);
addlist=1;
}
else if (c[6]+0>10000)
{
print("direct ssl so slow autoaddip "$1" "$2);
addlist=1;
}else{
addlist=-1;}
}
if (addlist==-1 && pingadd==1)
{
while (("ping -c 5 -q -A "$1 | getline ret) > 0)
{
if (index(ret,"packet loss")!=0)
{
split(ret, p,"[ ]+");
if (p[4]>0 && p[4]<5)
{
print("ping packet loss autoaddip "$1" "$2);
pingloss=1;
addlist=1;
}else{pingloss=0;}
break;
}
}
close("ping -c 5 -q "$1);
}
}
ERRNO="";
if (pingloss!=1){
getline drop< pidfile;
close(pidfile);
}
if (ERRNO) {addlist=0;next;}
if (addlist==1){
system("ipset add gfwlist "$1);
while ((cmd | getline ret) > 0)
{
if (addlist==1)
{
if (index(ret,"short read")!=0)
{
system("ipset del gfwlist "$1);
print("doname proxy rst autodelip "$1" "$2);
addlist=-2;
}
else if (index(ret,"SSL handshake error: (null)")!=0)
{
if(system("curl -m 10 --resolve "$2":443:"$1" https://"$2" -o /dev/null 2>/dev/null")==0)
{
addlist=2;
}
}
}
}
close(cmd);
if (addlist==1){
split(ret, c,"[ /]+");
print(c[6]);
if (c[6]=="failed,")
{
system("ipset del gfwlist "$1);
print("proxy can not connect autodelip "$1" "$2);
addlist=-2;
}else{
addlist=2;
}
}
}
}END{
if (addlist==2)
{ if (pingloss==0){
ERRNO="";
getline drop< pidfile;
if (ERRNO) {
system("ipset del gfwlist "$1);
print("cancel add myself "$1" "$2" due to one ip success direct");
}else{
print $1"\n">>pidfile;
}
close(pidfile);}
}else if (addlist==-1)
{
print($1" "$2" direct success");
while ((getline ret< pidfile) > 0)
{
if (ret!=""){
system("ipset del gfwlist "ret);
print("cancel add someone "ret" "$2" due to me"$1" success direct");
}
}
close(pidfile);
system("rm "pidfile" 2>/dev/null");
print($1" del "$2);
}else if (addlist==-2)
{
system("sleep 10");
while ((getline ret< pidfile) > 0)
{
if (ret!=""){
system("ipset add gfwlist "$1);
print("add "ret" "$2" due to one ip success proxy");
break;}
}
close(pidfile);
}}'

Some files were not shown because too many files have changed in this diff Show More