mirror of
https://github.com/bol-van/zapret.git
synced 2025-04-19 13:32:58 +03:00
history purge
This commit is contained in:
commit
ae2b6d1e86
21
Makefile
Normal file
21
Makefile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
DIRS := nfq tpws ip2net mdig
|
||||||
|
TGT := binaries/my
|
||||||
|
|
||||||
|
all: clean
|
||||||
|
mkdir -p "$(TGT)"; \
|
||||||
|
for dir in $(DIRS); do \
|
||||||
|
chmod -x "$$dir/"*; \
|
||||||
|
$(MAKE) -C "$$dir" || exit; \
|
||||||
|
for exe in "$$dir/"*; do \
|
||||||
|
if [ -f "$$exe" ] && [ -x "$$exe" ]; then \
|
||||||
|
mv -f "$$exe" "${TGT}" ; \
|
||||||
|
ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \
|
||||||
|
fi \
|
||||||
|
done \
|
||||||
|
done
|
||||||
|
|
||||||
|
clean:
|
||||||
|
[ -d "$(TGT)" ] && rm -r "$(TGT)" ; \
|
||||||
|
for dir in $(DIRS); do \
|
||||||
|
$(MAKE) -C "$$dir" clean; \
|
||||||
|
done
|
BIN
binaries/aarch64/ip2net
Executable file
BIN
binaries/aarch64/ip2net
Executable file
Binary file not shown.
BIN
binaries/aarch64/mdig
Executable file
BIN
binaries/aarch64/mdig
Executable file
Binary file not shown.
BIN
binaries/aarch64/nfqws
Executable file
BIN
binaries/aarch64/nfqws
Executable file
Binary file not shown.
BIN
binaries/aarch64/tpws
Executable file
BIN
binaries/aarch64/tpws
Executable file
Binary file not shown.
BIN
binaries/armhf/ip2net
Executable file
BIN
binaries/armhf/ip2net
Executable file
Binary file not shown.
BIN
binaries/armhf/mdig
Executable file
BIN
binaries/armhf/mdig
Executable file
Binary file not shown.
BIN
binaries/armhf/nfqws
Executable file
BIN
binaries/armhf/nfqws
Executable file
Binary file not shown.
BIN
binaries/armhf/tpws
Executable file
BIN
binaries/armhf/tpws
Executable file
Binary file not shown.
BIN
binaries/mips32r1-lsb/ip2net
Executable file
BIN
binaries/mips32r1-lsb/ip2net
Executable file
Binary file not shown.
BIN
binaries/mips32r1-lsb/mdig
Executable file
BIN
binaries/mips32r1-lsb/mdig
Executable file
Binary file not shown.
BIN
binaries/mips32r1-lsb/nfqws
Executable file
BIN
binaries/mips32r1-lsb/nfqws
Executable file
Binary file not shown.
BIN
binaries/mips32r1-lsb/tpws
Executable file
BIN
binaries/mips32r1-lsb/tpws
Executable file
Binary file not shown.
BIN
binaries/mips32r1-msb/ip2net
Executable file
BIN
binaries/mips32r1-msb/ip2net
Executable file
Binary file not shown.
BIN
binaries/mips32r1-msb/mdig
Executable file
BIN
binaries/mips32r1-msb/mdig
Executable file
Binary file not shown.
BIN
binaries/mips32r1-msb/nfqws
Executable file
BIN
binaries/mips32r1-msb/nfqws
Executable file
Binary file not shown.
BIN
binaries/mips32r1-msb/tpws
Executable file
BIN
binaries/mips32r1-msb/tpws
Executable file
Binary file not shown.
BIN
binaries/mips64r2-msb/ip2net
Executable file
BIN
binaries/mips64r2-msb/ip2net
Executable file
Binary file not shown.
BIN
binaries/mips64r2-msb/mdig
Executable file
BIN
binaries/mips64r2-msb/mdig
Executable file
Binary file not shown.
BIN
binaries/mips64r2-msb/nfqws
Executable file
BIN
binaries/mips64r2-msb/nfqws
Executable file
Binary file not shown.
BIN
binaries/mips64r2-msb/tpws
Executable file
BIN
binaries/mips64r2-msb/tpws
Executable file
Binary file not shown.
BIN
binaries/ppc/ip2net
Executable file
BIN
binaries/ppc/ip2net
Executable file
Binary file not shown.
BIN
binaries/ppc/mdig
Executable file
BIN
binaries/ppc/mdig
Executable file
Binary file not shown.
BIN
binaries/ppc/nfqws
Executable file
BIN
binaries/ppc/nfqws
Executable file
Binary file not shown.
BIN
binaries/ppc/tpws
Executable file
BIN
binaries/ppc/tpws
Executable file
Binary file not shown.
BIN
binaries/x86/ip2net
Executable file
BIN
binaries/x86/ip2net
Executable file
Binary file not shown.
BIN
binaries/x86/mdig
Executable file
BIN
binaries/x86/mdig
Executable file
Binary file not shown.
BIN
binaries/x86/nfqws
Executable file
BIN
binaries/x86/nfqws
Executable file
Binary file not shown.
BIN
binaries/x86/tpws
Executable file
BIN
binaries/x86/tpws
Executable file
Binary file not shown.
BIN
binaries/x86_64/ip2net
Executable file
BIN
binaries/x86_64/ip2net
Executable file
Binary file not shown.
BIN
binaries/x86_64/mdig
Executable file
BIN
binaries/x86_64/mdig
Executable file
Binary file not shown.
BIN
binaries/x86_64/nfqws
Executable file
BIN
binaries/x86_64/nfqws
Executable file
Binary file not shown.
BIN
binaries/x86_64/tpws
Executable file
BIN
binaries/x86_64/tpws
Executable file
Binary file not shown.
53
config
Normal file
53
config
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# this file is included from init scripts
|
||||||
|
# change values here
|
||||||
|
|
||||||
|
# can help in case /tmp has not enough space
|
||||||
|
#TMPDIR=/opt/zapret/tmp
|
||||||
|
|
||||||
|
# options for ipsets
|
||||||
|
# too low hashsize can cause memory allocation errors on low RAM systems , even if RAM is enough
|
||||||
|
# too large hashsize will waste lots of RAM
|
||||||
|
IPSET_OPT="hashsize 262144 maxelem 2097152"
|
||||||
|
|
||||||
|
# options for ip2net. "-4" or "-6" auto added by ipset create script
|
||||||
|
IP2NET_OPT4="--prefix-length=22-30 --v4-threshold=3/4"
|
||||||
|
IP2NET_OPT6="--prefix-length=56-64 --v6-threshold=5"
|
||||||
|
|
||||||
|
# CHOOSE OPERATION MODE
|
||||||
|
# use nfqws : nfqws_ipset nfqws_ipset_https nfqws_all nfqws_all_https
|
||||||
|
# use tpws : tpws_ipset tpws_ipset_https tpws_all tpws_all_https tpws_hostlist
|
||||||
|
# no daemon, just ipset : ipset
|
||||||
|
# custom mode : custom . should modify init script and add your own code
|
||||||
|
MODE=tpws_ipset_https
|
||||||
|
|
||||||
|
# CHOOSE NFQWS DAEMON OPTIONS. run "nfq/nfqws --help" for option list
|
||||||
|
NFQWS_OPT="--wsize=3 --hostspell=HOST"
|
||||||
|
|
||||||
|
# CHOOSE NFQWS DAEMON OPTIONS for DPI desync mode. run "nfq/nfqws --help" for option list
|
||||||
|
DESYNC_MARK=0x40000000
|
||||||
|
NFQWS_OPT_DESYNC="--dpi-desync=fake --dpi-desync-ttl=0 --dpi-desync-fooling=badsum --dpi-desync-fwmark=$DESYNC_MARK"
|
||||||
|
|
||||||
|
# CHOOSE TPWS DAEMON OPTIONS. run "tpws/tpws --help" for option list
|
||||||
|
TPWS_OPT_HTTP="--hostspell=HOST --split-http-req=method"
|
||||||
|
TPWS_OPT_HTTPS="--split-pos=3"
|
||||||
|
|
||||||
|
# for routers based on desktop linux only. has not effect in openwrt.
|
||||||
|
# CHOOSE LAN and WAN NETWORK INTERFACES
|
||||||
|
# or leave them commented if its not router
|
||||||
|
#IFACE_LAN=eth0
|
||||||
|
#IFACE_WAN=eth1
|
||||||
|
|
||||||
|
# should init scripts apply firewall rules ?
|
||||||
|
# set to 0 if firewall control system is present
|
||||||
|
# openwrt uses fw3 firewall , init never touch fw
|
||||||
|
INIT_APPLY_FW=1
|
||||||
|
|
||||||
|
# do not work with ipv4
|
||||||
|
#DISABLE_IPV4=1
|
||||||
|
# do not work with ipv6
|
||||||
|
DISABLE_IPV6=1
|
||||||
|
|
||||||
|
# select which init script will be used to get ip or host list
|
||||||
|
# possible values : get_user.sh get_antizapret.sh get_combined.sh get_reestr.sh get_hostlist.sh
|
||||||
|
# comment if not required
|
||||||
|
GETLIST=get_antifilter_ipsmart.sh
|
154
docs/changes.txt
Normal file
154
docs/changes.txt
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
v1
|
||||||
|
|
||||||
|
Initial release
|
||||||
|
|
||||||
|
v2
|
||||||
|
|
||||||
|
nfqws : command line options change. now using standard getopt.
|
||||||
|
nfqws : added options for window size changing and "Host:" case change
|
||||||
|
ISP support : tested on mns.ru and beeline (corbina)
|
||||||
|
init scripts : rewritten init scripts for simple choise of ISP
|
||||||
|
create_ipset : now using 'ipset restore', it works much faster
|
||||||
|
readme : updated. now using UTF-8 charset.
|
||||||
|
|
||||||
|
v3
|
||||||
|
|
||||||
|
tpws : added transparent proxy (supports TPROXY and DNAT).
|
||||||
|
can help when ISP tracks whole HTTP session, not only the beginning
|
||||||
|
ipset : added zapret-hosts-user.txt which contain user defined host names to be resolved
|
||||||
|
and added to zapret ip list
|
||||||
|
ISP support : dom.ru support via TPROXY/DNAT
|
||||||
|
ISP support : successfully tested sknt.ru on 'domru' configuration
|
||||||
|
other configs will probably also work, but cannot test
|
||||||
|
compile : openwrt compile howto
|
||||||
|
|
||||||
|
v4
|
||||||
|
|
||||||
|
tpws : added ability to insert extra space after http method : "GET /" => "GET /"
|
||||||
|
ISP support : TKT support
|
||||||
|
|
||||||
|
v5
|
||||||
|
|
||||||
|
nfqws : ipv6 support in nfqws
|
||||||
|
|
||||||
|
v6
|
||||||
|
|
||||||
|
ipset : added "get_antizapret.sh"
|
||||||
|
|
||||||
|
v7
|
||||||
|
|
||||||
|
tpws : added ability to insert "." after Host: name
|
||||||
|
|
||||||
|
v8
|
||||||
|
|
||||||
|
openwrt init : removed hotplug.d/firewall because of race conditions. now only use /etc/firewall.user
|
||||||
|
|
||||||
|
v9
|
||||||
|
|
||||||
|
ipban : added ipban ipset. place domains banned by ip to zapret-hosts-user-ipban.txt
|
||||||
|
these IPs must be soxified for both http and https
|
||||||
|
ISP support : tiera support
|
||||||
|
ISP support : added DNS filtering to ubuntu and debian scripts
|
||||||
|
|
||||||
|
v10
|
||||||
|
|
||||||
|
tpws : added split-pos option. split every message at specified position
|
||||||
|
|
||||||
|
v11
|
||||||
|
|
||||||
|
ipset : scripts optimizations
|
||||||
|
|
||||||
|
v12
|
||||||
|
|
||||||
|
nfqws : fix wrong tcp checksum calculation if packet length is odd and platform is big-endian
|
||||||
|
|
||||||
|
v13
|
||||||
|
|
||||||
|
added binaries
|
||||||
|
|
||||||
|
v14
|
||||||
|
|
||||||
|
change get_antizapret script to work with https://github.com/zapret-info/z-i/raw/master/dump.csv
|
||||||
|
filter out 192.168.*, 127.*, 10.* from blocked ips
|
||||||
|
|
||||||
|
v15
|
||||||
|
|
||||||
|
added --hostspell option to nfqws and tpws
|
||||||
|
ISP support : beeline now catches "host" but other spellings still work
|
||||||
|
openwrt/LEDE : changed init script to work with procd
|
||||||
|
tpws, nfqws : minor cosmetic fixes
|
||||||
|
|
||||||
|
v16
|
||||||
|
|
||||||
|
tpws: split-http-req=method : split inside method name, not after
|
||||||
|
ISP support : mns.ru changed split pos to 3 (got redirect page with HEAD req : curl -I ej.ru)
|
||||||
|
|
||||||
|
v17
|
||||||
|
|
||||||
|
ISP support : athome moved from nfqws to tpws because of instability and http request hangs
|
||||||
|
tpws : added options unixeol,methodeol,hosttab
|
||||||
|
|
||||||
|
v18
|
||||||
|
|
||||||
|
tpws,nfqws : added hostnospace option
|
||||||
|
|
||||||
|
v19
|
||||||
|
|
||||||
|
tpws : added hostlist option
|
||||||
|
|
||||||
|
v20
|
||||||
|
|
||||||
|
added ip2net. ip2net groups ips from iplist into subnets and reduces ipset size twice
|
||||||
|
|
||||||
|
v21
|
||||||
|
|
||||||
|
added mdig. get_reestr.sh is *real* again
|
||||||
|
|
||||||
|
v22
|
||||||
|
|
||||||
|
total review of init script logic
|
||||||
|
dropped support of older debian 7 and ubuntu 12/14 systems
|
||||||
|
install_bin.sh : auto binaries preparation
|
||||||
|
docs: readme review. some new topics added, others deleted
|
||||||
|
docs: VPN setup with policy based routing using wireguard
|
||||||
|
docs: wireguard modding guide
|
||||||
|
|
||||||
|
v23
|
||||||
|
|
||||||
|
major init system rewrite
|
||||||
|
openwrt : separate firewall include /etc/firewall.zapret
|
||||||
|
install_easy.sh : easy setup on openwrt, debian, ubuntu, centos, fedora, opensuse
|
||||||
|
|
||||||
|
v24
|
||||||
|
|
||||||
|
separate config from init scripts
|
||||||
|
gzip support in ipset/*.sh and tpws
|
||||||
|
|
||||||
|
v25
|
||||||
|
|
||||||
|
init : move to native systemd units
|
||||||
|
use links to units, init scripts and firewall includes, no more copying
|
||||||
|
|
||||||
|
v26
|
||||||
|
|
||||||
|
ipv6 support
|
||||||
|
tpws : advanced bind options
|
||||||
|
|
||||||
|
v27
|
||||||
|
|
||||||
|
tpws : major connection code rewrite. originally it was derived from not top quality example , with many bugs and potential problems.
|
||||||
|
next generation connection code uses nonblocking sockets. now its in EXPERIMENTAL state.
|
||||||
|
|
||||||
|
v28
|
||||||
|
|
||||||
|
tpws : added socks5 support
|
||||||
|
ipset : major RKN getlist rewrite. added antifilter.network support
|
||||||
|
|
||||||
|
v29
|
||||||
|
|
||||||
|
nfqws : DPI desync attack
|
||||||
|
ip exclude system
|
||||||
|
|
||||||
|
v30
|
||||||
|
|
||||||
|
nfqws : DPI desync attack modes : fake,rst
|
42
docs/compile/build_howto_openwrt.txt
Normal file
42
docs/compile/build_howto_openwrt.txt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
How to compile native programs for use in openwrt
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
1) <fetch correct version of openwrt>
|
||||||
|
|
||||||
|
cd ~
|
||||||
|
|
||||||
|
<chaos calmer>
|
||||||
|
git clone git://git.openwrt.org/15.05/openwrt.git
|
||||||
|
<barrier breaker>
|
||||||
|
git clone git://git.openwrt.org/14.07/openwrt.git
|
||||||
|
<trunk>
|
||||||
|
git clone git://git.openwrt.org/openwrt.git
|
||||||
|
|
||||||
|
cd openwrt
|
||||||
|
|
||||||
|
2) ./scripts/feeds update -a
|
||||||
|
./scripts/feeds install -a
|
||||||
|
|
||||||
|
3) #add zapret packages to build root
|
||||||
|
#copy package descriptions
|
||||||
|
copy compile/openwrt/* to ~/openwrt
|
||||||
|
#copy source code of tpws
|
||||||
|
copy tpws to ~/openwrt/package/zapret/tpws
|
||||||
|
#copy source code of nfq
|
||||||
|
copy nfq to ~/openwrt/package/zapret/nfq
|
||||||
|
#copy source code of ip2net
|
||||||
|
copy ip2net to ~/openwrt/package/zapret/ip2net
|
||||||
|
|
||||||
|
4) make menuconfig
|
||||||
|
#select your target architecture
|
||||||
|
#select packages Network/Zapret/* as "M"
|
||||||
|
|
||||||
|
5) make toolchain/compile
|
||||||
|
|
||||||
|
6) make package/tpws/compile
|
||||||
|
make package/nfqws/compile
|
||||||
|
make package/ip2net/compile
|
||||||
|
make package/mdig/compile
|
||||||
|
|
||||||
|
7) find bin -name tpws*.ipk
|
||||||
|
#take your tpws*.ipk , nfqws*.ipk , ip2net*.ipk, mdig*.ipk from there
|
32
docs/compile/openwrt/package/zapret/ip2net/Makefile
Normal file
32
docs/compile/openwrt/package/zapret/ip2net/Makefile
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=ip2net
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/ip2net
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
TITLE:=ip2net
|
||||||
|
SUBMENU:=Zapret
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Prepare
|
||||||
|
mkdir -p $(PKG_BUILD_DIR)
|
||||||
|
$(CP) ./ip2net/* $(PKG_BUILD_DIR)/
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/ip2net/install
|
||||||
|
$(INSTALL_DIR) $(1)/opt/zapret/ip2net
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/ip2net $(1)/opt/zapret/ip2net
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,ip2net))
|
||||||
|
|
1
docs/compile/openwrt/package/zapret/ip2net/readme.txt
Normal file
1
docs/compile/openwrt/package/zapret/ip2net/readme.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Copy "ip2net" folder here !
|
32
docs/compile/openwrt/package/zapret/mdig/Makefile
Normal file
32
docs/compile/openwrt/package/zapret/mdig/Makefile
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=mdig
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/mdig
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
TITLE:=mdig
|
||||||
|
SUBMENU:=Zapret
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Prepare
|
||||||
|
mkdir -p $(PKG_BUILD_DIR)
|
||||||
|
$(CP) ./mdig/* $(PKG_BUILD_DIR)/
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/mdig/install
|
||||||
|
$(INSTALL_DIR) $(1)/opt/zapret/mdig
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/mdig $(1)/opt/zapret/mdig
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,mdig))
|
||||||
|
|
1
docs/compile/openwrt/package/zapret/mdig/readme.txt
Normal file
1
docs/compile/openwrt/package/zapret/mdig/readme.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Copy "mdig" folder here !
|
34
docs/compile/openwrt/package/zapret/nfqws/Makefile
Normal file
34
docs/compile/openwrt/package/zapret/nfqws/Makefile
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=nfqws
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/nfqws
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
TITLE:=nfqws
|
||||||
|
SUBMENU:=Zapret
|
||||||
|
DEPENDS:=+libnetfilter-queue +libcap +zlib
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Prepare
|
||||||
|
mkdir -p $(PKG_BUILD_DIR)
|
||||||
|
$(CP) ./nfq/* $(PKG_BUILD_DIR)/
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/nfqws/install
|
||||||
|
$(INSTALL_DIR) $(1)/opt/zapret/nfq
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/nfqws $(1)/opt/zapret/nfq
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,nfqws))
|
||||||
|
|
||||||
|
|
1
docs/compile/openwrt/package/zapret/nfqws/readme.txt
Normal file
1
docs/compile/openwrt/package/zapret/nfqws/readme.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Copy "nfq" folder here !
|
33
docs/compile/openwrt/package/zapret/tpws/Makefile
Normal file
33
docs/compile/openwrt/package/zapret/tpws/Makefile
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=tpws
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/tpws
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
TITLE:=tpws
|
||||||
|
SUBMENU:=Zapret
|
||||||
|
DEPENDS:=+zlib +libcap
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Prepare
|
||||||
|
mkdir -p $(PKG_BUILD_DIR)
|
||||||
|
$(CP) ./tpws/* $(PKG_BUILD_DIR)/
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/tpws/install
|
||||||
|
$(INSTALL_DIR) $(1)/opt/zapret/tpws
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/tpws $(1)/opt/zapret/tpws
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,tpws))
|
||||||
|
|
1
docs/compile/openwrt/package/zapret/tpws/readme.txt
Normal file
1
docs/compile/openwrt/package/zapret/tpws/readme.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Copy "tpws" folder here !
|
159
docs/https.txt
Normal file
159
docs/https.txt
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
Расскажу как я решал вопрос с блокировкой https на роутере.
|
||||||
|
На тех провайдерах, что мне доступны, все, кроме одного либо банили https по IP (вообще нет конекта), либо захватывали TLS сессию и она намертво зависала - пакеты больше не приходили. На домру удалось выяснить, что DPI цепляется к SNI (Server Name Indication) в TLS, но сплит TLS запроса не помог. Я пришел к выводу, что https самым разумным будет прозрачно заворачивать в socks.
|
||||||
|
Tor поддерживает "из коробки" режим transparent proxy. Это можно использовать в теории, но практически - только на роутерах с 128 мб памяти и выше. Таких роутеров не так много. В основном объем памяти 32 или 64 мб. И тор еще и тормозной.
|
||||||
|
Другой вариант напрашивается, если у вас есть доступ к какой-нибудь unix системе с SSH, где сайты не блокируются. Например, у вас есть VPS вне России. Именно так и поступил.
|
||||||
|
Понятийно требуются следующие шаги :
|
||||||
|
1) Выделять IP, на которые надо проксировать трафик. У нас уже имеется ipset "zapret", технология создания которого отработана.
|
||||||
|
2) Сделать так, чтобы все время при загрузке системы на некотором порту возникал socks.
|
||||||
|
3) Установить transparent соксификатор. Redsocks прекрасно подошел на эту роль.
|
||||||
|
4) Завернуть через iptables трафик с порта назначения 443 и на ip адреса из ipset 'zapret' на соксификатор
|
||||||
|
Буду рассматривать систему на базе openwrt, где уже установлена система обхода dpi "zapret".
|
||||||
|
По крайней мере нужно иметь заполненный ipset 'zapret', устанавливать tpws или nfqws не обязательно.
|
||||||
|
Более того, если они на вашей системе не срабатывают, то можно соксифицировать не только https, но и http.
|
||||||
|
|
||||||
|
* Сделать так, чтобы все время при загрузке системы на некотором порту возникал socks
|
||||||
|
|
||||||
|
Т.к. дефолтный dropbear клиент не поддерживает создание socks, то для начала придется заменить dropbear ssh client на openssh : пакеты openssh-client и openssh-client-utils.
|
||||||
|
Устанавливать их нужно с опцией opkg --force-overwrite, поскольку они перепишут ssh клиент от dropbear.
|
||||||
|
После установки пакетов расслабим неоправданно жестокие права : chmod 755 /etc/ssh.
|
||||||
|
Следует создать пользователя, под которым будем крутить ssh client. Допустим, это будет 'proxy'.
|
||||||
|
Сначала установить пакет shadow-useradd.
|
||||||
|
------------------
|
||||||
|
useradd -d /home/proxy proxy
|
||||||
|
mkdir -p /home/proxy
|
||||||
|
chown proxy:proxy /home/proxy
|
||||||
|
------------------
|
||||||
|
Openssh ловит разные глюки, если у него нет доступа к /dev/tty.
|
||||||
|
Добавим в /etc/rc.local строчку : "chmod 666 /dev/tty"
|
||||||
|
Сгенерируем для него ключ RSA для доступа к ssh серверу.
|
||||||
|
------------------
|
||||||
|
su proxy
|
||||||
|
cd
|
||||||
|
mkdir -m 700 .ssh
|
||||||
|
cd .ssh
|
||||||
|
ssh-keygen
|
||||||
|
ls
|
||||||
|
exit
|
||||||
|
------------------
|
||||||
|
Должны получиться файлы id_rsa и id_rsa.pub.
|
||||||
|
Строчку из id_rsa.pub следует добавить на ssh сервер в файл $HOME/.ssh/authorized_keys.
|
||||||
|
Более подробно о доступе к ssh через авторизацию по ключам : https://beget.com/ru/articles/ssh_by_key
|
||||||
|
Предположим, ваш ssh сервер - vps.mydomain.com, пользователь называется 'proxy'.
|
||||||
|
Проверить подключение можно так : ssh -N -D 1098 -l proxy vps.mydomain.com.
|
||||||
|
Сделайте это под пользователем "proxy", поскольку при первом подключении ssh спросит о правильности hostkey.
|
||||||
|
Соединение может отвалиться в любой момент, поэтому нужно зациклить запуск ssh.
|
||||||
|
Для этого лучший вариант - использовать procd - упрощенная замена systemd на openwrt версий BB и выше.
|
||||||
|
--- /etc/init.d/socks_vps ---
|
||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
START=50
|
||||||
|
STOP=50
|
||||||
|
USE_PROCD=1
|
||||||
|
USERNAME=proxy
|
||||||
|
COMMAND="ssh -N -D 1098 -l proxy vps.mydomain.com"
|
||||||
|
start_service() {
|
||||||
|
procd_open_instance
|
||||||
|
procd_set_param user $USERNAME
|
||||||
|
procd_set_param respawn 10 10 0
|
||||||
|
procd_set_param command $COMMAND
|
||||||
|
procd_close_instance
|
||||||
|
}
|
||||||
|
-----------------------------
|
||||||
|
Этому файлу нужно дать права : chmod +x /etc/init.d/socks_vps
|
||||||
|
Запуск : /etc/init.d/socks_vps start
|
||||||
|
Останов : /etc/init.d/socks_vps stop
|
||||||
|
Включить автозагрузку : /etc/init.d/socks_vps enable
|
||||||
|
Проверка : curl -4 --socks5 127.0.0.1:1098 https://rutracker.org
|
||||||
|
|
||||||
|
* Организовать прозрачную соксификацию
|
||||||
|
|
||||||
|
Установить пакет redsocks.
|
||||||
|
Конфиг :
|
||||||
|
-- /etc/redsocks.conf : ---
|
||||||
|
base {
|
||||||
|
log_debug = off;
|
||||||
|
log_info = on;
|
||||||
|
log = "syslog:local7";
|
||||||
|
daemon = on;
|
||||||
|
user = nobody;
|
||||||
|
group = nogroup;
|
||||||
|
redirector = iptables;
|
||||||
|
}
|
||||||
|
redsocks {
|
||||||
|
local_ip = 127.0.0.1;
|
||||||
|
local_port = 1099;
|
||||||
|
ip = 127.0.0.1;
|
||||||
|
port = 1098;
|
||||||
|
type = socks5;
|
||||||
|
}
|
||||||
|
---------------------------
|
||||||
|
После чего перезапускаем : /etc/init.d/redsocks restart
|
||||||
|
Смотрим появился ли листенер : netstat -tnlp | grep 1099
|
||||||
|
Автостарт redsocks при таком конфиге не работает, потому что на момент запуска сеть не инициализирована, и у нас даже нет 127.0.0.1.
|
||||||
|
Вместо штатного автостарта будем вешаться на события поднятия интерфейса. Разберем это позже.
|
||||||
|
Пока что отключим автостарт : /etc/init.d/redsocks disable
|
||||||
|
|
||||||
|
* Завертывание соединений через iptables
|
||||||
|
|
||||||
|
Будем завертывать любые tcp соединения на ip из ipset "ipban" и https на ip из ipset "zapret".
|
||||||
|
|
||||||
|
--- /etc/firewall.user -----
|
||||||
|
SOXIFIER_PORT=1099
|
||||||
|
|
||||||
|
. /opt/zapret/init.d/openwrt/functions
|
||||||
|
|
||||||
|
create_ipset no-update
|
||||||
|
|
||||||
|
network_find_wan_all wan_iface
|
||||||
|
for ext_iface in $wan_iface; do
|
||||||
|
network_get_device ext_device $ext_iface
|
||||||
|
ipt OUTPUT -t nat -o $ext_device -p tcp --dport 443 -m set --match-set zapret dst -j REDIRECT --to-port $SOXIFIER_PORT
|
||||||
|
ipt OUTPUT -t nat -o $ext_device -p tcp -m set --match-set ipban dst -j REDIRECT --to-port $SOXIFIER_PORT
|
||||||
|
done
|
||||||
|
|
||||||
|
network_get_device DEVICE lan
|
||||||
|
sysctl -w net.ipv4.conf.$DEVICE.route_localnet=1
|
||||||
|
ipt prerouting_lan_rule -t nat -p tcp --dport 443 -m set --match-set zapret dst -j DNAT --to 127.0.0.1:$SOXIFIER_PORT
|
||||||
|
ipt prerouting_lan_rule -t nat -p tcp -m set --match-set ipban dst -j DNAT --to 127.0.0.1:$SOXIFIER_PORT
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Внести параметр "reload" в указанное место :
|
||||||
|
--- /etc/config/firewall ---
|
||||||
|
config include
|
||||||
|
option path '/etc/firewall.user'
|
||||||
|
option reload '1'
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Перезапуск : /etc/init.d/firewall restart
|
||||||
|
Все, теперь можно проверять :
|
||||||
|
/etc/init.d/redsocks stop
|
||||||
|
curl -4 https://rutracker.org
|
||||||
|
# должно обломаться с надписью "Connection refused". если не обламывается - значит ip адрес rutracker.org не в ipset,
|
||||||
|
# либо не сработали правила фаервола. например, из-за не установленных модулей ipt
|
||||||
|
/etc/init.d/redsocks start
|
||||||
|
curl -4 https://rutracker.org
|
||||||
|
# должно выдать страницу
|
||||||
|
|
||||||
|
* Автозапуск redsocks
|
||||||
|
|
||||||
|
Я сделал для себя небольшой скриптик, вешающийся на события поднятия и опускания интерфейсов.
|
||||||
|
|
||||||
|
--- /etc/hotplug.d/iface/99-exec-on-updown ---
|
||||||
|
#!/bin/sh
|
||||||
|
if [ "$ACTION" = ifup ]; then
|
||||||
|
cmd=$(uci get network.$INTERFACE.exec_on_up)
|
||||||
|
[ -n "$cmd" ] && $cmd
|
||||||
|
fi
|
||||||
|
if [ "$ACTION" = ifdown ]; then
|
||||||
|
cmd=$(uci get network.$INTERFACE.exec_on_down)
|
||||||
|
[ -n "$cmd" ] && $cmd
|
||||||
|
fi
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
Теперь можно в описания интерфейсов внести в соответствующий раздел :
|
||||||
|
--- /etc/config/nework ---
|
||||||
|
config interface 'wan'
|
||||||
|
........
|
||||||
|
option exec_on_up '/etc/init.d/redsocks start'
|
||||||
|
--------------------------
|
||||||
|
reboot. Заходим снова, смотрим, что есть redsocks, есть ssh, опять проверяем curl -4 https://rutracker.org.
|
||||||
|
Пробуем зайти на https://rutracker.org с компа внутри локалки.
|
62
docs/iptables.txt
Normal file
62
docs/iptables.txt
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
For window size changing :
|
||||||
|
|
||||||
|
iptables -t mangle -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
iptables -t mangle -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK -m set --match-set zapret src -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
For outgoing data manipulation ("Host:" case changing) :
|
||||||
|
|
||||||
|
iptables -t mangle -I POSTROUTING -p tcp --dport 80 -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m set --match-set zapret dst -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:5 -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
For dpi desync attack :
|
||||||
|
|
||||||
|
iptables -t mangle -I POSTROUTING -p tcp -m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
|
||||||
|
For TPROXY :
|
||||||
|
|
||||||
|
sysctl -w net.ipv4.ip_forward=1
|
||||||
|
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
||||||
|
|
||||||
|
ip -f inet rule add fwmark 1 lookup 100
|
||||||
|
ip -f inet route add local default dev lo table 100
|
||||||
|
# prevent loop
|
||||||
|
iptables -t filter -I INPUT -p tcp --dport 1188 -j REJECT
|
||||||
|
iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -j MARK --set-mark 1
|
||||||
|
iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 1188
|
||||||
|
|
||||||
|
iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -m set --match-set zapret dst -j MARK --set-mark 1
|
||||||
|
iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -m mark --mark 0x1/0x1 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 1188
|
||||||
|
|
||||||
|
For DNAT :
|
||||||
|
|
||||||
|
# run tpws as user "tpws". its required to avoid loops.
|
||||||
|
sysctl -w net.ipv4.conf.eth1.route_localnet=1
|
||||||
|
iptables -t nat -I PREROUTING -p tcp --dport 80 -j DNAT --to 127.0.0.1:1188
|
||||||
|
iptables -t nat -I OUTPUT -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to 127.0.0.1:1188
|
||||||
|
|
||||||
|
|
||||||
|
Reset all iptable rules :
|
||||||
|
|
||||||
|
iptables -F
|
||||||
|
iptables -X
|
||||||
|
iptables -t nat -F
|
||||||
|
iptables -t nat -X
|
||||||
|
iptables -t mangle -F
|
||||||
|
iptables -t mangle -X
|
||||||
|
iptables -t raw -F
|
||||||
|
iptables -t raw -X
|
||||||
|
|
||||||
|
Reset iptable policies :
|
||||||
|
|
||||||
|
iptables -P INPUT ACCEPT
|
||||||
|
iptables -P FORWARD ACCEPT
|
||||||
|
iptables -P OUTPUT ACCEPT
|
||||||
|
iptables -t mangle -P POSTROUTING ACCEPT
|
||||||
|
iptables -t mangle -P PREROUTING ACCEPT
|
||||||
|
iptables -t mangle -P INPUT ACCEPT
|
||||||
|
iptables -t mangle -P FORWARD ACCEPT
|
||||||
|
iptables -t mangle -P OUTPUT ACCEPT
|
||||||
|
iptables -t raw -P PREROUTING ACCEPT
|
||||||
|
iptables -t raw -P OUTPUT ACCEPT
|
475
docs/readme.eng.txt
Normal file
475
docs/readme.eng.txt
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
What is it for
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Bypass the blocking of web sites http.
|
||||||
|
The project is mainly aimed at the Russian audience to fight russian regulator named "Roskomnadzor".
|
||||||
|
Some features of the project are russian reality specific (such as getting list of sites
|
||||||
|
blocked by Roskomnadzor), but most others are common.
|
||||||
|
|
||||||
|
How it works
|
||||||
|
------------
|
||||||
|
|
||||||
|
DPI providers have gaps. They happen because DPI rules are writtten for
|
||||||
|
ordinary user programs, omitting all possible cases that are permissible by standards.
|
||||||
|
This is done for simplicity and speed. It makes no sense to catch 0.01% hackers,
|
||||||
|
because these blockings are quite simple and easily bypassed even by ordinary users.
|
||||||
|
|
||||||
|
Some DPIs cannot recognize the http request if it is divided into TCP segments.
|
||||||
|
For example, a request of the form "GET / HTTP / 1.1 \ r \ nHost: kinozal.tv ......"
|
||||||
|
we send in 2 parts: first go "GET", then "/ HTTP / 1.1 \ r \ nHost: kinozal.tv .....".
|
||||||
|
Other DPIs stumble when the "Host:" header is written in another case: for example, "host:".
|
||||||
|
Sometimes work adding extra space after the method: "GET /" => "GET /"
|
||||||
|
or adding a dot at the end of the host name: "Host: kinozal.tv."
|
||||||
|
|
||||||
|
|
||||||
|
How to put this into practice in the linux system
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
How to make the system break the request into parts? You can pipe the entire TCP session
|
||||||
|
through transparent proxy, or you can replace the tcp window size field on the first incoming TCP packet with a SYN, ACK.
|
||||||
|
Then the client will think that the server has set a small window size for it and the first data segment
|
||||||
|
will send no more than the specified length. In subsequent packages, we will not change anything.
|
||||||
|
The further behavior of the system depends on the implemented algorithm in the OS.
|
||||||
|
Experience shows that linux always sends first packet no more than the specified
|
||||||
|
in window size length, the rest of the packets until some time sends no more than max (36, specified_size).
|
||||||
|
After a number of packets, the window scaling mechanism is triggered and starts taking
|
||||||
|
the scaling factor into account. The packet size becomes no more than max (36, specified_ramer << scale_factor).
|
||||||
|
The behavior is not very elegant, but since we do not affect the size of the incoming packets,
|
||||||
|
and the amount of data received in http is usually much higher than the amount sent, then visually
|
||||||
|
there will be only small delays.
|
||||||
|
Windows behaves in a similar case much more predictably. First segment
|
||||||
|
the specified length goes away, then the window size changes depending on the value,
|
||||||
|
sent in new tcp packets. That is, the speed is almost immediately restored to the possible maximum.
|
||||||
|
|
||||||
|
Its easy to intercept a packet with SYN, ACK using iptables.
|
||||||
|
However, the options for editing packets in iptables are severely limited.
|
||||||
|
It’s not possible to change window size with standard modules.
|
||||||
|
For this, we will use the NFQUEUE. This tool allows transfer packets to the processes running in user mode.
|
||||||
|
The process, accepting a packet, can change it, which is what we need.
|
||||||
|
|
||||||
|
iptables -t mangle -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
It will queue the packets we need to the process that listens on the queue with the number 200.
|
||||||
|
Process will replace the window size. PREROUTING will catch packets addressed to the host itself and routed packets.
|
||||||
|
That is, the solution works the same way as on the client, so on the router. On a PC-based or OpenWRT router.
|
||||||
|
In principle, this is enough.
|
||||||
|
However, with such an impact on TCP there will be a slight delay.
|
||||||
|
In order not to touch the hosts that are not blocked by the provider, you can make such a move.
|
||||||
|
Create a list of blocked domains, resolve them to IP addresses and save to ipset named "zapret".
|
||||||
|
Add to rule:
|
||||||
|
|
||||||
|
iptables -t mangle -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK -m set --match-set zapret src -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
Thus, the impact will be made only on ip addresses related to blocked sites.
|
||||||
|
The list can be updated in scheduled task every few days.
|
||||||
|
|
||||||
|
If DPI cant be bypassed with splitting a request into segments, then sometimes helps changing case
|
||||||
|
of the "Host:" http header. We may not need a window size replacement, so the do not need PREROUTING chain.
|
||||||
|
Instead, we hang on outgoing packets in the POSTROUTING chain:
|
||||||
|
|
||||||
|
iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
In this case, additional points are also possible. DPI can catch only the first http request, ignoring
|
||||||
|
subsequent requests in the keep-alive session. Then we can reduce the cpu load abandoning the processing of unnecessary packages.
|
||||||
|
|
||||||
|
iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:5 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
It happens that the provider monitors the entire HTTP session with keep-alive requests. In this case
|
||||||
|
it is not enough to restrict the TCP window when establishing a connection. Each http request must be splitted
|
||||||
|
to multiple TCP segments. This task is solved through the full proxying of traffic using
|
||||||
|
transparent proxy (TPROXY or DNAT). TPROXY does not work with connections originating from the local system
|
||||||
|
so this solution is applicable only on the router. DNAT works with local connections,
|
||||||
|
but there is a danger of entering into endless recursion, so the daemon is launched as a separate user,
|
||||||
|
and for this user, DNAT is disabled via "-m owner". Full proxying requires more resources than outbound packet
|
||||||
|
manipulation without reconstructing a TCP connection.
|
||||||
|
|
||||||
|
iptables -t nat -I PREROUTING -p tcp --dport 80 -j DNAT --to 127.0.0.1:1188
|
||||||
|
iptables -t nat -I OUTPUT -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to 127.0.0.1:1188
|
||||||
|
|
||||||
|
NOTE: DNAT on localhost works in the OUTPUT chain, but does not work in the PREROUTING chain without enabling the route_localnet parameter:
|
||||||
|
|
||||||
|
sysctl -w net.ipv4.conf.<incoming_interface_name>.route_localnet=1
|
||||||
|
|
||||||
|
You can use "-j REDIRECT --to-port 1188" instead of DNAT, but in this case the transpareny proxy process
|
||||||
|
should listen on the ip address of the incoming interface or on all addresses. Listen all - not good
|
||||||
|
in terms of security. Listening one (local) is possible, but in the case of automated
|
||||||
|
script will have to recognize it, then dynamically enter it into the command. In any case, additional efforts are required.
|
||||||
|
|
||||||
|
ip6tables
|
||||||
|
---------
|
||||||
|
|
||||||
|
ip6tables work almost exactly the same way as ipv4, but there are a number of important nuances.
|
||||||
|
In DNAT, you should take the address --to in square brackets. For example :
|
||||||
|
|
||||||
|
iptables -t nat -I OUTPUT -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to [::1]:1188
|
||||||
|
|
||||||
|
The route_localnet parameter does not exist for ipv6.
|
||||||
|
DNAT to localhost (:: 1) is possible only in the OUTPUT chain.
|
||||||
|
In the PREROUTING DNAT chain, it is possible to any global address or to the link local address of the same interface
|
||||||
|
the packet came from.
|
||||||
|
NFQUEUE works without changes.
|
||||||
|
|
||||||
|
When it will not work
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
* If DNS server returns false responses. ISP can return false IP addresses or not return anything
|
||||||
|
when blocked domains are queried. If this is the case change DNS to public ones, such as 8.8.8.8 or 1.1.1.1.
|
||||||
|
Sometimes ISP hijacks queries to any DNS server. Dnscrypt or dns-over-tls help.
|
||||||
|
* If blocking is done by IP.
|
||||||
|
* If a connection passes through a filter capable of reconstructing a TCP connection, and which
|
||||||
|
follows all standards. For example, we are routed to squid. Connection goes through the full OS tcpip stack,
|
||||||
|
fragmentation disappears immediately as a means of circumvention. Squid is correct, it will find everything
|
||||||
|
as it should, it is useless to deceive him.
|
||||||
|
BUT. Only small providers can afford using squid, since it is very resource intensive.
|
||||||
|
Large companies usually use DPI, which is designed for much greater bandwidth.
|
||||||
|
|
||||||
|
nfqws
|
||||||
|
-----
|
||||||
|
|
||||||
|
This program is a packet modifier and a NFQUEUE queue handler.
|
||||||
|
It takes the following parameters:
|
||||||
|
|
||||||
|
--debug=0|1 ; 1=print debug info
|
||||||
|
--qnum=<nfqueue_number>
|
||||||
|
--wsize=<window_size> ; set window size. 0 = do not modify
|
||||||
|
--hostcase ; change Host: => host:
|
||||||
|
--hostspell=HoSt ; exact spelling of the "Host" header. must be 4 chars. default is "host"
|
||||||
|
--hostnospace ; remove space after Host: and add it to User-Agent: to preserve packet size
|
||||||
|
--daemon ; daemonize
|
||||||
|
--pidfile=<filename> ; write pid to file
|
||||||
|
--user=<username> ; drop root privs
|
||||||
|
--uid=uid[:gid] ; drop root privs
|
||||||
|
--dpi-desync[=fake|rst|rstack|disorder] ; try to desync dpi state
|
||||||
|
--dpi-desync-fwmark=<int|0xHEX> ; override fwmark for desync packet. default = 0x40000000
|
||||||
|
--dpi-desync-ttl=<int> ; set ttl for desync packet
|
||||||
|
--dpi-desync-fooling=none|md5sig|badsum
|
||||||
|
--dpi-desync-retrans=0|1 ; (fake,rst,rstack only) 0(default)=reinject original data packet after fake 1=drop original data packet to force its retransmission
|
||||||
|
--dpi-desync-skip-nosni=0|1 ; 1(default)=do not apply desync to requests without hostname in the SNI
|
||||||
|
--dpi-desync-split-pos=<1..1500> ; (for disorder only) split TCP packet at specified position
|
||||||
|
--hostlist=<filename> ; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply)
|
||||||
|
|
||||||
|
The manipulation parameters can be combined in any way.
|
||||||
|
|
||||||
|
COMMENT. As described earlier, Linux behaves strangely when the window size is changed, unlike Windows.
|
||||||
|
Following segments do not restore their full length. Connection can go for a long time in batches of small packets.
|
||||||
|
Package modification parameters (--hostcase, ...) may not work, because nfqws does not work with the connection,
|
||||||
|
but only with separate packets in which the search may not be found, because scattered across multiple packets.
|
||||||
|
If the source of the packages is Windows, there is no such problem.
|
||||||
|
|
||||||
|
DPI DESYNC ATTACK
|
||||||
|
After completion of the tcp 3-way handshake, the first data packet from the client goes.
|
||||||
|
It usually has "GET / ..." or TLS ClientHello. We drop this packet, replacing with something else.
|
||||||
|
It can be a fake version with another harmless but valid http or https request (fake), tcp reset packet (rst,rstack),
|
||||||
|
split into 2 segments original packet with fake segment in the middle (disorder).
|
||||||
|
In articles these attack have names "TCB desynchronization" and "TCB teardown".
|
||||||
|
Fake packet must reach DPI, but do not reach the destination server.
|
||||||
|
The following means are available: set a low TTL, send a packet with bad checksum,
|
||||||
|
add tcp option "MD5 signature". All of them have their own disadvantages :
|
||||||
|
|
||||||
|
* md5sig does not work on all servers
|
||||||
|
* badsum doesn't work if your device is behind NAT which does not pass invalid packets.
|
||||||
|
Linux NAT by default does not pass them without special setting "sysctl -w net.netfilter.nf_conntrack_checksum=0"
|
||||||
|
Openwrt sets it from the box, other routers in most cases dont, and its not always possible to change it.
|
||||||
|
If nfqws is on the router, its not neccessary to switch of "net.netfilter.nf_conntrack_checksum".
|
||||||
|
Fake packet doesn't go through FORWARD chain, it goes through OUTPUT. But if your router is behind another NAT, for example ISP NAT,
|
||||||
|
and that NAT does not pass invalid packets, you cant do anything.
|
||||||
|
* TTL looks like the best option, but it requires special tuning for earch ISP. If DPI is further than local ISP websites
|
||||||
|
you can cut access to them. Manual IP exclude list is required. Its possible to use md5sig with ttl.
|
||||||
|
This way you cant hurt anything, but good chances it will help to open local ISP websites.
|
||||||
|
If automatic solution cannot be found then use zapret-hosts-user-exclude.txt.
|
||||||
|
|
||||||
|
For fake,rst,rstack modes original packet can be sent after the fake one or just dropped.
|
||||||
|
If its dropped OS will perform first retransmission after 0.2 sec, then the delay increases exponentially.
|
||||||
|
Delay can help to make sure fake and original packets are properly ordered and processed on DPI.
|
||||||
|
|
||||||
|
Disorder mode splits original packet and sends packets in the following order :
|
||||||
|
1. 2nd segment
|
||||||
|
2. fake 1st segment, data filled with zeroes
|
||||||
|
3. 1st segment
|
||||||
|
4. fake 1st segment, data filled with zeroes (2nd copy)
|
||||||
|
Original packet is always dropped. --dpi-desync-split-pos sets split position (default 3).
|
||||||
|
If position is higher than packet length, pos=1 is used.
|
||||||
|
This sequence is designed to make reconstruction of critical message as difficult as possible.
|
||||||
|
Fake segments may not be required to bypass some DPIs, but can potentially help if more sophisticated reconstruction
|
||||||
|
algorithms are used.
|
||||||
|
|
||||||
|
Hostlist is applicable only to desync attack. It does not work for other options.
|
||||||
|
Hosts are extracted from plain http request Host: header and SNI of ClientHelllo TLS message.
|
||||||
|
Subdomains are applied automatically. gzip lists are supported.
|
||||||
|
|
||||||
|
iptables for performing the attack :
|
||||||
|
|
||||||
|
iptables -t mangle -I POSTROUTING -p tcp -m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
connbytes will only queue the first data packet. mark is needed to keep away generated packets from NFQUEUE.
|
||||||
|
nfqws sets fwmark when it sends generated packets.
|
||||||
|
|
||||||
|
tpws
|
||||||
|
-----
|
||||||
|
|
||||||
|
tpws is transparent proxy.
|
||||||
|
|
||||||
|
--debug=0|1|2 ; 0(default)=silent 1=verbose 2=debug
|
||||||
|
--bind-addr=<ipv4_addr>|<ipv6_addr>
|
||||||
|
--bind-iface4=<interface_name> ; bind to the first ipv4 addr of interface
|
||||||
|
--bind-iface6=<interface_name> ; bind to the first ipv6 addr of interface
|
||||||
|
--bind-linklocal=prefer|force ; prefer or force ipv6 link local
|
||||||
|
--bind-wait-ifup=<sec> ; wait for interface to appear and up
|
||||||
|
--bind-wait-ip=<sec> ; after ifup wait for ip address to appear up to N seconds
|
||||||
|
--bind-wait-ip-linklocal=<sec> ; accept only link locals first N seconds then any
|
||||||
|
--port=<port> ; port number to listen on
|
||||||
|
--socks ; implement socks4/5 proxy instead of transparent proxy
|
||||||
|
--local-rcvbuf=<bytes> ; SO_RCVBUF for local legs
|
||||||
|
--local-sndbuf=<bytes> ; SO_SNDBUF for local legs
|
||||||
|
--remote-rcvbuf=<bytes> ; SO_RCVBUF for remote legs
|
||||||
|
--remote-sndbuf=<bytes> ; SO_SNDBUF for remote legs
|
||||||
|
--skip-nodelay ; do not set TCP_NODELAY for outgoing connections. incompatible with split.
|
||||||
|
--no-resolve ; disable socks5 remote dns
|
||||||
|
--maxconn=<max_connections> ; max number of local legs
|
||||||
|
--maxfiles=<max_open_files> ; max file descriptors (setrlimit). min requirement is (X*connections+16), where X=6 in tcp proxy mode, X=4 in tampering mode.
|
||||||
|
; its worth to make a reserve with 1.5 multiplier. by default maxfiles is (X*connections)*1.5+16
|
||||||
|
--max-orphan-time=<sec> ; if local leg sends something and closes and remote leg is still connecting then cancel connection attempt after N seconds
|
||||||
|
|
||||||
|
--hostlist=<filename> ; only act on host in the list (one host per line, subdomains auto apply)
|
||||||
|
--split-http-req=method|host ; split http request at specified logical position
|
||||||
|
--split-pos=<numeric_offset> ; split at specified pos. invalidates split-http-req.
|
||||||
|
--hostcase ; change Host: => host:
|
||||||
|
--hostspell ; exact spelling of "Host" header. must be 4 chars. default is "host"
|
||||||
|
--hostdot ; add "." after Host: name
|
||||||
|
--hosttab ; add tab after Host: name
|
||||||
|
--hostnospace ; remove space after Host:
|
||||||
|
--hostpad=<bytes> ; add dummy padding headers before Host:
|
||||||
|
--methodspace ; add extra space after method
|
||||||
|
--methodeol ; add end-of-line before method
|
||||||
|
--unixeol ; replace 0D0A to 0A
|
||||||
|
--daemon ; daemonize
|
||||||
|
--pidfile=<filename> ; write pid to file
|
||||||
|
--user=<username> ; drop root privs
|
||||||
|
--uid=uid[:gid] ; drop root privs
|
||||||
|
|
||||||
|
The manipulation parameters can be combined in any way.
|
||||||
|
There are exceptions: split-pos replaces split-http-req. hostdot and hosttab are mutually exclusive.
|
||||||
|
Only split-pos option works for non-HTTP traffic.
|
||||||
|
|
||||||
|
tpws can bind only to one ip or to all at once.
|
||||||
|
To bind to all ipv4, specify "0.0.0.0", to all ipv6 - "::". Without parameters, tpws bind to all ipv4 and ipv6.
|
||||||
|
The --bind-wait * parameters can help in situations where you need to get IP from the interface, but it is not there yet, it is not raised
|
||||||
|
or not configured.
|
||||||
|
In different systems, ifup events are caught in different ways and do not guarantee that the interface has already received an IP address of a certain type.
|
||||||
|
In the general case, there is no single mechanism to hang oneself on an event of the type "link local address appeared on the X interface."
|
||||||
|
|
||||||
|
in socks proxy mode no additional system privileges are required
|
||||||
|
connection to local IPs of the system where tpws runs are prohibited
|
||||||
|
tpws supports remote dns resolving (curl : --socks5-hostname firefox : socks_remote_dns=true) , but does it in blocking mode.
|
||||||
|
tpws uses async sockets for all activity but resolving can break this model.
|
||||||
|
if tpws serves many clients it can cause trouble. also DoS attack is possible against tpws.
|
||||||
|
if remote resolving causes trouble configure clients to use local name resolution and use
|
||||||
|
--no-resolve option on tpws side.
|
||||||
|
|
||||||
|
Ways to get a list of blocked IP
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
1) Enter the blocked domains to ipset/zapret-hosts-user.txt and run ipset/get_user.sh
|
||||||
|
At the output, you get ipset/zapret-ip-user.txt with IP addresses.
|
||||||
|
|
||||||
|
2) ipset/get_reestr_*.sh. Russian specific
|
||||||
|
|
||||||
|
3) ipset/get_antifilter_*.sh. Russian specific
|
||||||
|
|
||||||
|
4) ipset/get_config.sh. This script calls what is written into the GETLIST variable from the config file.
|
||||||
|
If the variable is not defined, then only lists for ipsets nozapret/nozapret6 are resolved.
|
||||||
|
|
||||||
|
So, if you're not russian, the only way for you is to manually add blocked domains.
|
||||||
|
Or write your own ipset/get_iran_blocklist.sh , if you know where to download this one.
|
||||||
|
|
||||||
|
On routers, it is not recommended to call these scripts more than once in 2 days to minimize flash memory writes.
|
||||||
|
|
||||||
|
ipset/create_ipset.sh executes forced ipset update.
|
||||||
|
The regulator list has already reached an impressive size of hundreds of thousands of IP addresses. Therefore, to optimize ipset
|
||||||
|
ip2net utility is used. It takes a list of individual IP addresses and tries to find in it subnets of the maximum size (from / 22 to / 30),
|
||||||
|
in which more than 3/4 addresses are blocked. ip2net is written in C because the operation is resource intensive.
|
||||||
|
If ip2net is compiled or a binary is copied to the ip2net directory, the create_ipset.sh script uses an ipset of the hash:net type,
|
||||||
|
piping the list through ip2net. Otherwise, ipset of hash:ip type is used, the list is loaded as is.
|
||||||
|
Accordingly, if you don’t like ip2net, just remove the binary from the ip2net directory.
|
||||||
|
create_ipset.sh supports loading ip lists from gzip files. First it looks for the filename with the ".gz" extension,
|
||||||
|
such as "zapret-ip.txt.gz", if not found it falls back to the original name "zapret-ip.txt".
|
||||||
|
So your own get_iran_blockslist.sh can use "zz" function to produce gz. Study how other russian get_XXX.sh work.
|
||||||
|
Gzipping helps saving a lot of precious flash space on embedded systems.
|
||||||
|
User lists are not gzipped because they are not expected to be very large.
|
||||||
|
|
||||||
|
You can add a list of domains to ipset/zapret-hosts-user-ipban.txt. Their ip addresses will be placed
|
||||||
|
in a separate ipset "ipban". It can be used to route connections to transparent proxy "redsocks" or VPN.
|
||||||
|
|
||||||
|
IPV6: if ipv6 is enabled, then additional txt's are created with the same name, but with a "6" at the end before the extension.
|
||||||
|
zapret-ip.txt => zapret-ip6.txt
|
||||||
|
The ipsets zapret6 and ipban6 are created.
|
||||||
|
|
||||||
|
IP EXCLUSION SYSTEM. All scripts resolve zapret-hosts-user-exclude.txt file, creating zapret-ip-exclude.txt and zapret-ip-exclude6.txt.
|
||||||
|
They are the source for ipsets nozapret/nozapret6. All rules created by init scripts are created with these ipsets in mind.
|
||||||
|
The IPs placed in them are not involved in the process.
|
||||||
|
zapret-hosts-user-exclude.txt can contain domains, ipv4 and ipv6 addresses or subnets.
|
||||||
|
|
||||||
|
Domain name filtering
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
An alternative to ipset is to use tpws with a list of domains.
|
||||||
|
tpws can only read one hostlist.
|
||||||
|
|
||||||
|
Enter the blocked domains to ipset/zapret-hosts-users.txt. Remove ipset/zapret-hosts.txt.gz.
|
||||||
|
Then the init script will run tpws with the zapret-hosts-users.txt list.
|
||||||
|
|
||||||
|
Other option ( Roskomnadzor list - get_hostlist.sh ) is russian specific.
|
||||||
|
You can write your own replacement for get_hostlist.sh.
|
||||||
|
|
||||||
|
When filtering by domain name, tpws should run without filtering by ipset.
|
||||||
|
All http traffic goes through tpws, and it decides whether to use manipulation depending on the Host: field in the http request.
|
||||||
|
This creates an increased load on the system.
|
||||||
|
The domain search itself works very quickly, the load is connected with pumping the amount of data through the process.
|
||||||
|
When using large regulator lists estimate the amount of RAM on the router!
|
||||||
|
|
||||||
|
Choosing parameters
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
The file /opt/zapret/config is used by various components of the system and contains basic settings.
|
||||||
|
It needs to be viewed and edited if necessary.
|
||||||
|
Select MODE:
|
||||||
|
|
||||||
|
nfqws_ipset - use nfqws for http. targets are filtered by ipset "zapret"
|
||||||
|
nfqws_ipset_https - use nfqws for http and https. targets are filtered by ipset "zapret"
|
||||||
|
nfqws_all - use nfqws for all http
|
||||||
|
nfqws_all_https - use nfqws for all http and https
|
||||||
|
nfqws_all_desync - use nfqws for DPI desync attack on http и https for all http and https
|
||||||
|
nfqws_ipset_desync - use nfqws for DPI desync attack on http и https for all http and https. targets are filtered by ipset "zapret"
|
||||||
|
nfqws_hostlist_desync - use nfqws for DPI desync attack on http и https , only to hosts from hostlist
|
||||||
|
|
||||||
|
tpws_ipset - use tpws for http. targets are filtered by ipset "zapret"
|
||||||
|
tpws_ipset_https - use tpws for http and https. targets are filtered by ipset "zapret"
|
||||||
|
tpws_all - use tpws for all http
|
||||||
|
tpws_all_https - use tpws for all http and https
|
||||||
|
tpws_hostlist - same as tpws_all but touch only domains from the hostlist
|
||||||
|
|
||||||
|
ipset - only fill ipset. futher actions depend on your own code
|
||||||
|
|
||||||
|
Its possible to change manipulation options used by the daemons :
|
||||||
|
|
||||||
|
NFQWS_OPT="--wsize=3 --hostspell=HOST"
|
||||||
|
TPWS_OPT_HTTP="--hostspell=HOST --split-http-req=method"
|
||||||
|
TPWS_OPT_HTTPS="--split-pos=3"
|
||||||
|
|
||||||
|
Options for DPI desync attack are configured separately:
|
||||||
|
|
||||||
|
DESYNC_MARK=0x40000000
|
||||||
|
NFQWS_OPT_DESYNC="--dpi-desync --dpi-desync-ttl=0 --dpi-desync-fooling=badsum --dpi-desync-fwmark=$DESYNC_MARK"
|
||||||
|
|
||||||
|
|
||||||
|
The GETLIST parameter tells the install_easy.sh installer which script to call
|
||||||
|
to update the list of blocked ip or hosts.
|
||||||
|
Its called via get_config.sh from scheduled tasks (crontab or systemd timer).
|
||||||
|
Put here the name of the script that you will use to update the lists.
|
||||||
|
If not, then the parameter should be commented out.
|
||||||
|
|
||||||
|
You can individually disable ipv4 or ipv6. If the parameter is commented out or not equal to "1",
|
||||||
|
use of the protocol is permitted.
|
||||||
|
#DISABLE_IPV4=1
|
||||||
|
DISABLE_IPV6=1
|
||||||
|
|
||||||
|
The number of threads for mdig multithreaded DNS resolver (1..100).
|
||||||
|
The more of them, the faster, but will your DNS server be offended by hammering ?
|
||||||
|
MDIG_THREADS=30
|
||||||
|
|
||||||
|
The following settings are not relevant for openwrt :
|
||||||
|
|
||||||
|
If your system works as a router, then you need to enter the names of the internal and external interfaces:
|
||||||
|
IFACE_LAN = eth0
|
||||||
|
IFACE_WAN = eth1
|
||||||
|
IMPORTANT: configuring routing, masquerade, etc. not a zapret task.
|
||||||
|
Only modes that intercept transit traffic are enabled.
|
||||||
|
|
||||||
|
The INIT_APPLY_FW=1 parameter enables the init script to independently apply iptables rules.
|
||||||
|
With other values or if the parameter is commented out, the rules will not be applied.
|
||||||
|
This is useful if you have a firewall management system, in the settings of which you should tie the rules.
|
||||||
|
|
||||||
|
Screwing to the firewall control system or your launch system
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
If you use some kind of firewall management system, then it may conflict with an existing startup script.
|
||||||
|
When re-applying the rules, it could break the iptables settings from the zapret.
|
||||||
|
In this case, the rules for iptables should be screwed to your firewall separately from running tpws or nfqws.
|
||||||
|
|
||||||
|
The following calls allow you to apply or remove iptables rules separately:
|
||||||
|
|
||||||
|
/opt/zapret/init.d/sysv/zapret start-fw
|
||||||
|
/opt/zapret/init.d/sysv/zapret stop-fw
|
||||||
|
|
||||||
|
And you can start or stop the demons separately from the firewall:
|
||||||
|
|
||||||
|
/opt/zapret/init.d/sysv/zapret start-daemons
|
||||||
|
/opt/zapret/init.d/sysv/zapret stop-daemons
|
||||||
|
|
||||||
|
|
||||||
|
Simple install to desktop linux system
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
Simple install works on most modern linux distributions with systemd.
|
||||||
|
Run install_easy.sh and answer its questions.
|
||||||
|
|
||||||
|
Simple install to openwrt
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
install_easy.sh also works on openwrt but there're additional challenges.
|
||||||
|
They are mainly about possibly low flash free space.
|
||||||
|
Simple install will not work if it has no space to install itself and required packages from the repo.
|
||||||
|
|
||||||
|
Another challenge would be to bring zapret to the router. You can download zip from github and use it.
|
||||||
|
Do not repack zip contents in the Windows, because this way you break chmod and links.
|
||||||
|
Install openssh-sftp-server and unzip to openwrt and use sftp to transfer the file.
|
||||||
|
|
||||||
|
The best way to start is to put zapret dir to /tmp and run /tmp/zapret/install_easy.sh from there.
|
||||||
|
After installation remove /tmp/zapret to free RAM.
|
||||||
|
|
||||||
|
The absolute minimum for openwrt is 64/8 system, 64/16 is comfortable, 128/extroot is recommended.
|
||||||
|
|
||||||
|
|
||||||
|
Android
|
||||||
|
-------
|
||||||
|
|
||||||
|
Its not possible to use nfqws and tpws in transparent proxy mode without root privileges.
|
||||||
|
Without root tpws can run in --socks mode.
|
||||||
|
|
||||||
|
I have no NFQUEUE presence statistics in stock android kernels, but its present on my MTK device.
|
||||||
|
If NFQUEUE is present nfqws works.
|
||||||
|
|
||||||
|
There's no ipset support unless you run custom kernel. In common case task of bringing up ipset
|
||||||
|
on android is ranging from "not easy" to "almost impossible", unless you find working kernel
|
||||||
|
image for your device.
|
||||||
|
|
||||||
|
Android does not use /etc/passwd, tpws --user won't work. There's replacement.
|
||||||
|
Use numeric uids in --uid option.
|
||||||
|
Its recommended to use gid 3003 (AID_INET), otherwise tpws will not have inet access.
|
||||||
|
Example : --uid 1:3003
|
||||||
|
In iptables use : "! --uid-owner 1" instead of "! --uid-owner tpws".
|
||||||
|
|
||||||
|
Write your own shell script with iptables and tpws, run it using your root manager.
|
||||||
|
Autorun scripts are here :
|
||||||
|
magisk : /data/adb/service.d
|
||||||
|
supersu : /system/su.d
|
||||||
|
|
||||||
|
I haven't checked whether android can kill iptable rules at its own will during wifi connection/disconnection,
|
||||||
|
mobile data on/off, ...
|
||||||
|
|
||||||
|
|
||||||
|
Https blocking bypass
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
As a rule, DPI tricks do not help to bypass https blocking.
|
||||||
|
You have to redirect traffic through a third-party host.
|
||||||
|
It is proposed to use transparent redirect through socks5 using iptables + redsocks, or iptables + iproute + vpn.
|
||||||
|
Redsocks variant is described in https.txt.
|
||||||
|
iproute + wireguard - in wireguard_iproute_openwrt.txt.
|
||||||
|
(they are russian)
|
||||||
|
|
||||||
|
SOMETIMES (but not often) a tls handshake split trick works.
|
||||||
|
Try MODE=..._https
|
||||||
|
May be you're lucky.
|
||||||
|
|
||||||
|
MORE OFTEN DPI desync attack work, but it may require some manual tuning.
|
1132
docs/readme.txt
Normal file
1132
docs/readme.txt
Normal file
File diff suppressed because it is too large
Load Diff
133
docs/wireguard/010-wg-mod.patch
Normal file
133
docs/wireguard/010-wg-mod.patch
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
Index: WireGuard-0.0.20190123/src/cookie.c
|
||||||
|
===================================================================
|
||||||
|
--- WireGuard-0.0.20190123.orig/src/cookie.c
|
||||||
|
+++ WireGuard-0.0.20190123/src/cookie.c
|
||||||
|
@@ -193,6 +193,8 @@ void wg_cookie_message_create(struct mes
|
||||||
|
xchacha20poly1305_encrypt(dst->encrypted_cookie, cookie, COOKIE_LEN,
|
||||||
|
macs->mac1, COOKIE_LEN, dst->nonce,
|
||||||
|
checker->cookie_encryption_key);
|
||||||
|
+ // MOD : randomize trash
|
||||||
|
+ dst->header.trash = gen_trash();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wg_cookie_message_consume(struct message_handshake_cookie *src,
|
||||||
|
Index: WireGuard-0.0.20190123/src/messages.h
|
||||||
|
===================================================================
|
||||||
|
--- WireGuard-0.0.20190123.orig/src/messages.h
|
||||||
|
+++ WireGuard-0.0.20190123/src/messages.h
|
||||||
|
@@ -53,23 +53,41 @@ enum limits {
|
||||||
|
MAX_QUEUED_PACKETS = 1024 /* TODO: replace this with DQL */
|
||||||
|
};
|
||||||
|
|
||||||
|
+/*
|
||||||
|
enum message_type {
|
||||||
|
- MESSAGE_INVALID = 0,
|
||||||
|
- MESSAGE_HANDSHAKE_INITIATION = 1,
|
||||||
|
- MESSAGE_HANDSHAKE_RESPONSE = 2,
|
||||||
|
- MESSAGE_HANDSHAKE_COOKIE = 3,
|
||||||
|
- MESSAGE_DATA = 4
|
||||||
|
+ MESSAGE_INVALID = 0,
|
||||||
|
+ MESSAGE_HANDSHAKE_INITIATION = 1,
|
||||||
|
+ MESSAGE_HANDSHAKE_RESPONSE = 2,
|
||||||
|
+ MESSAGE_HANDSHAKE_COOKIE = 3,
|
||||||
|
+ MESSAGE_DATA = 4
|
||||||
|
};
|
||||||
|
+*/
|
||||||
|
+
|
||||||
|
+// MOD : message type
|
||||||
|
+enum message_type {
|
||||||
|
+ MESSAGE_INVALID = 0xE319CCD0,
|
||||||
|
+ MESSAGE_HANDSHAKE_INITIATION = 0x48ADE198,
|
||||||
|
+ MESSAGE_HANDSHAKE_RESPONSE = 0xFCA6A8F3,
|
||||||
|
+ MESSAGE_HANDSHAKE_COOKIE = 0x64A3BB18,
|
||||||
|
+ MESSAGE_DATA = 0x391820AA
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+// MOD : generate fast trash without true RNG
|
||||||
|
+__le32 gen_trash(void);
|
||||||
|
|
||||||
|
struct message_header {
|
||||||
|
- /* The actual layout of this that we want is:
|
||||||
|
- * u8 type
|
||||||
|
- * u8 reserved_zero[3]
|
||||||
|
- *
|
||||||
|
- * But it turns out that by encoding this as little endian,
|
||||||
|
- * we achieve the same thing, and it makes checking faster.
|
||||||
|
- */
|
||||||
|
- __le32 type;
|
||||||
|
+ /* The actual layout of this that we want is:
|
||||||
|
+ * u8 type
|
||||||
|
+ * u8 reserved_zero[3]
|
||||||
|
+ *
|
||||||
|
+ * But it turns out that by encoding this as little endian,
|
||||||
|
+ * we achieve the same thing, and it makes checking faster.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+ // MOD : trash field to change message size and add 4 byte offset to all fields
|
||||||
|
+ __le32 trash;
|
||||||
|
+
|
||||||
|
+ __le32 type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct message_macs {
|
||||||
|
Index: WireGuard-0.0.20190123/src/noise.c
|
||||||
|
===================================================================
|
||||||
|
--- WireGuard-0.0.20190123.orig/src/noise.c
|
||||||
|
+++ WireGuard-0.0.20190123/src/noise.c
|
||||||
|
@@ -17,6 +17,24 @@
|
||||||
|
#include <linux/highmem.h>
|
||||||
|
#include <crypto/algapi.h>
|
||||||
|
|
||||||
|
+
|
||||||
|
+// MOD : trash generator
|
||||||
|
+__le32 gtrash = 0;
|
||||||
|
+__le32 gen_trash(void)
|
||||||
|
+{
|
||||||
|
+ if (gtrash)
|
||||||
|
+ gtrash = gtrash*1103515243 + 12345;
|
||||||
|
+ else
|
||||||
|
+ // first value is true random
|
||||||
|
+ get_random_bytes_wait(>rash, sizeof(gtrash));
|
||||||
|
+ return gtrash;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/* This implements Noise_IKpsk2:
|
||||||
|
*
|
||||||
|
* <- s
|
||||||
|
@@ -515,6 +533,10 @@ wg_noise_handshake_create_initiation(str
|
||||||
|
&handshake->entry);
|
||||||
|
|
||||||
|
handshake->state = HANDSHAKE_CREATED_INITIATION;
|
||||||
|
+
|
||||||
|
+ // MOD : randomize trash
|
||||||
|
+ dst->header.trash = gen_trash();
|
||||||
|
+
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
out:
|
||||||
|
@@ -655,6 +677,10 @@ bool wg_noise_handshake_create_response(
|
||||||
|
&handshake->entry);
|
||||||
|
|
||||||
|
handshake->state = HANDSHAKE_CREATED_RESPONSE;
|
||||||
|
+
|
||||||
|
+ // MOD : randomize trash
|
||||||
|
+ dst->header.trash = gen_trash();
|
||||||
|
+
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
out:
|
||||||
|
Index: WireGuard-0.0.20190123/src/send.c
|
||||||
|
===================================================================
|
||||||
|
--- WireGuard-0.0.20190123.orig/src/send.c
|
||||||
|
+++ WireGuard-0.0.20190123/src/send.c
|
||||||
|
@@ -200,6 +200,10 @@ static bool encrypt_packet(struct sk_buf
|
||||||
|
header->header.type = cpu_to_le32(MESSAGE_DATA);
|
||||||
|
header->key_idx = keypair->remote_index;
|
||||||
|
header->counter = cpu_to_le64(PACKET_CB(skb)->nonce);
|
||||||
|
+
|
||||||
|
+ // MOD : randomize trash
|
||||||
|
+ header->header.trash = gen_trash();
|
||||||
|
+
|
||||||
|
pskb_put(skb, trailer, trailer_len);
|
||||||
|
|
||||||
|
/* Now we can encrypt the scattergather segments */
|
244
docs/wireguard/wireguard-mod.txt
Normal file
244
docs/wireguard/wireguard-mod.txt
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
Посвящено возможной блокировке в РФ VPN протоколов через DPI.
|
||||||
|
Предпосылками являются последние законодательные акты и во всю сочащиеся "секретные" записки.
|
||||||
|
В РФ разрабатываются и готовятся к применению более продвинутые решения по блокировке трафика.
|
||||||
|
Вполне вероятно будут резать стандартные VPN протоколы. Нам надо быть к этому готовыми.
|
||||||
|
|
||||||
|
Один из возможных и перспективных путей решения данного вопроса - кустомная модификация
|
||||||
|
исходников VPN с целью незначительного изменения протокола, ломающего стандартные модули обнаружения в DPI.
|
||||||
|
Это относительно сложно, доступно только для гиков.
|
||||||
|
Никто не будет разрабатывать специальные модули обнаружения в DPI, если только кто-то не сделает простое и
|
||||||
|
удобное решение для всех, и его станут широко применять. Но это маловероятно, и даже если и так,
|
||||||
|
то всегда можно модифицировать протокол чуток по другому. Делать моды для DPI несравненно дольше
|
||||||
|
и дороже, чем клепать на коленке изменения протокола для wireguard.
|
||||||
|
|
||||||
|
|
||||||
|
ЗАМЕЧЕНИЕ : альтернативой модификации конечного софта для VPN является использование "навесных"
|
||||||
|
обфускаторов. см : https://github.com/bol-van/ipobfs
|
||||||
|
|
||||||
|
|
||||||
|
Рассмотрю что нам надо пропатчить в wireguard. Модифицированный wireguard проверен на виртуалках
|
||||||
|
с десктопным linux, он работает, сообщения в wireshark действительно не вписываются в стандартный
|
||||||
|
протокол и не опознаются.
|
||||||
|
|
||||||
|
Wireguard протокол очень простой. Все сообщения описаны в messages.h
|
||||||
|
Поставим себе целью сделать 2 простые модификации :
|
||||||
|
1) Добавим в начало всех сообщений немного мусора, чтобы изменить размер сообщений и смещения полей
|
||||||
|
2) Изменим коды типов сообщений
|
||||||
|
Этого может быть вполне достаточно для обмана DPI
|
||||||
|
|
||||||
|
--messages.h--------------------------
|
||||||
|
/*
|
||||||
|
enum message_type {
|
||||||
|
MESSAGE_INVALID = 0,
|
||||||
|
MESSAGE_HANDSHAKE_INITIATION = 1,
|
||||||
|
MESSAGE_HANDSHAKE_RESPONSE = 2,
|
||||||
|
MESSAGE_HANDSHAKE_COOKIE = 3,
|
||||||
|
MESSAGE_DATA = 4
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
// MOD : message type
|
||||||
|
enum message_type {
|
||||||
|
MESSAGE_INVALID = 0xE319CCD0,
|
||||||
|
MESSAGE_HANDSHAKE_INITIATION = 0x48ADE198,
|
||||||
|
MESSAGE_HANDSHAKE_RESPONSE = 0xFCA6A8F3,
|
||||||
|
MESSAGE_HANDSHAKE_COOKIE = 0x64A3BB18,
|
||||||
|
MESSAGE_DATA = 0x391820AA
|
||||||
|
};
|
||||||
|
|
||||||
|
// MOD : generate fast trash without true RNG
|
||||||
|
__le32 gen_trash(void);
|
||||||
|
|
||||||
|
struct message_header {
|
||||||
|
/* The actual layout of this that we want is:
|
||||||
|
* u8 type
|
||||||
|
* u8 reserved_zero[3]
|
||||||
|
*
|
||||||
|
* But it turns out that by encoding this as little endian,
|
||||||
|
* we achieve the same thing, and it makes checking faster.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// MOD : trash field to change message size and add 4 byte offset to all fields
|
||||||
|
__le32 trash;
|
||||||
|
|
||||||
|
__le32 type;
|
||||||
|
};
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
Напишем функцию для генерации trash. Функция должна быть быстрая, важно не замедлить скорость.
|
||||||
|
Мы не расчитываем, что нас будут специально ловить, иначе бы пришлось делать полноценный обфускатор.
|
||||||
|
Задача лишь сломать стандартный модуль обнаружения протокола wireguard. Потому истинная рандомность
|
||||||
|
trash не важна.
|
||||||
|
Но все же немного "трэша" не повредит. Гонки между тредами так же пофигистичны. Это же трэш.
|
||||||
|
|
||||||
|
--noise.c-----------------------------
|
||||||
|
// MOD : trash generator
|
||||||
|
__le32 gtrash = 0;
|
||||||
|
__le32 gen_trash(void)
|
||||||
|
{
|
||||||
|
if (gtrash)
|
||||||
|
gtrash = gtrash*1103515243 + 12345;
|
||||||
|
else
|
||||||
|
// first value is true random
|
||||||
|
get_random_bytes_wait(>rash, sizeof(gtrash));
|
||||||
|
return gtrash;
|
||||||
|
}
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
Теперь осталось найти все места, где создаются сообщения и внести туда заполнение поля trash.
|
||||||
|
Сообщений всего 4. Их можно найти по присваиванию полю type одного из значений enum message_type.
|
||||||
|
|
||||||
|
2 места в noise.c в функциях wg_noise_handshake_create_initiation и wg_noise_handshake_create_response,
|
||||||
|
1 место в cookie.c в функции wg_cookie_message_create
|
||||||
|
Дописываем в конец инициализации структуры сообщения :
|
||||||
|
|
||||||
|
--------------------------------------
|
||||||
|
// MOD : randomize trash
|
||||||
|
dst->header.trash = gen_trash();
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
и 1 место в send.c в функции encrypt_packet
|
||||||
|
|
||||||
|
--------------------------------------
|
||||||
|
// MOD : randomize trash
|
||||||
|
header->header.trash = gen_trash();
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
Вот и весь патчинг. Полный patch (версия wireguard 0.0.20190123) лежит в 010-wg-mod.patch.
|
||||||
|
Патчинг кода - самое простое. Для десктопного linux дальше все просто.
|
||||||
|
Пересобираем через make, устанавливаем через make install, перегружаем
|
||||||
|
модуль wireguard, перезапускаем интерфейсы, и все готово.
|
||||||
|
|
||||||
|
Настоящий геморой начнется когда вы это попытаетесь засунуть на роутер под openwrt.
|
||||||
|
Одна из больших проблем linux - отсутствие совместимости драйверов на уровне бинариков.
|
||||||
|
Поэтому собирать необходимо в точности под вашу версию ядра и в точности под его .config.
|
||||||
|
Вам придется либо полностью самостоятельно собирать всю прошивку, либо найти SDK в точности
|
||||||
|
от вашей версии прошивки для вашей архитектуры и собрать модуль с помощью этого SDK.
|
||||||
|
Последний вариант более легкий.
|
||||||
|
Для сборки вам понадобится система на linux x86_64. Ее можно установить в виртуалке.
|
||||||
|
Теоретически можно пользоваться WSL из win10, но на практике там очень медленное I/O,
|
||||||
|
по крайней мере на старых версиях win10. Безумно медленное. Будете собирать вечность.
|
||||||
|
Может в новых win10 что-то и улучшили, но я бы сразу расчитывал на полноценный linux.
|
||||||
|
|
||||||
|
Находим здесь вашу версию : https://downloads.openwrt.org/
|
||||||
|
Скачиваем файл openwrt-sdk-*.tar.xz или lede-sdk-*.tar.xz
|
||||||
|
Например : https://downloads.openwrt.org/releases/18.06.2/targets/ar71xx/generic/openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64.tar.xz
|
||||||
|
Если ваша версия непонятна или стара, то проще будет найти последнюю прошивку и перешить роутер.
|
||||||
|
Распаковываем SDK. Следующими командами можно собрать оригинальный вариант wireguard :
|
||||||
|
|
||||||
|
# scripts/feeds update -a
|
||||||
|
# scripts/feeds install -a
|
||||||
|
# make defconfig
|
||||||
|
# make -j 4 package/wireguard/compile
|
||||||
|
|
||||||
|
Сборка будет довольно долгой. Ведь придется подтащить ядро, собрать его, собрать зависимости.
|
||||||
|
"-j 4" означает использовать 4 потока. Впишите вместо 4 количество доступных cpu cores.
|
||||||
|
|
||||||
|
Получим следующие файлы :
|
||||||
|
|
||||||
|
openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/bin/targets/ar71xx/generic/packages/kmod-wireguard_4.9.152+0.0.20190123-1_mips_24kc.ipk
|
||||||
|
openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/bin/packages/mips_24kc/base/wireguard-tools_0.0.20190123-1_mips_24kc.ipk
|
||||||
|
|
||||||
|
Но это будет оригинальный wireguard. Нам нужен патченый.
|
||||||
|
Установим quilt и mc для нормального редактора вместо vim :
|
||||||
|
|
||||||
|
# sudo apt-get update
|
||||||
|
# sudo apt-get install quilt mc
|
||||||
|
|
||||||
|
# make package/wireguard/clean
|
||||||
|
# make package/wireguard/prepare V=s QUILT=1
|
||||||
|
|
||||||
|
|
||||||
|
Сорцы приготовлены для сборки в :
|
||||||
|
openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/build_dir/target-mips_24kc_musl/linux-ar71xx_generic/WireGuard-0.0.20190123/src
|
||||||
|
|
||||||
|
# cd build_dir/target-mips_24kc_musl/linux-ar71xx_generic/WireGuard-0.0.20190123/src
|
||||||
|
# quilt push -a
|
||||||
|
# quilt new 010-wg-mod.patch
|
||||||
|
# export EDITOR=mcedit
|
||||||
|
|
||||||
|
Далее будет открываться редактор mcedit, в который нужно вносить изменения в каждый файл :
|
||||||
|
|
||||||
|
# quilt edit messages.h
|
||||||
|
# quilt edit cookie.c
|
||||||
|
# quilt edit noise.c
|
||||||
|
# quilt edit send.c
|
||||||
|
# quilt diff
|
||||||
|
# quilt refresh
|
||||||
|
|
||||||
|
Получили файл патча в :
|
||||||
|
openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/build_dir/target-mips_24kc_musl/linux-ar71xx_generic/WireGuard-0.0.20190123/patches/010-wg-mod.patch
|
||||||
|
|
||||||
|
Выходим в корень SDK.
|
||||||
|
|
||||||
|
# make package/wireguard/compile V=99
|
||||||
|
|
||||||
|
Если не было ошибок, то получили измененные ipk.
|
||||||
|
Патч можно зафиксировать в описании пакета :
|
||||||
|
|
||||||
|
# make package/wireguard/update
|
||||||
|
|
||||||
|
Получим :
|
||||||
|
openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/feeds/base/package/network/services/wireguard/patches/010-wg-mod.patch
|
||||||
|
При последующей очистке и пересборке он будет автоматом применяться.
|
||||||
|
|
||||||
|
|
||||||
|
АЛЬТЕРНАТИВА : можно не возиться с quilt.
|
||||||
|
сделайте
|
||||||
|
# make package/wireguard/clean
|
||||||
|
# make package/wireguard/prepare
|
||||||
|
и напрямую модифицируйте или копируйте файлы в
|
||||||
|
openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/build_dir/target-mips_24kc_musl/linux-ar71xx_generic/WireGuard-0.0.20190123/src
|
||||||
|
затем
|
||||||
|
# make package/wireguard/compile
|
||||||
|
|
||||||
|
Если нужно поменять версию wireguard, то идите в
|
||||||
|
openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/feeds/base/package/network/services/wireguard/Makefile
|
||||||
|
поменяйте там версию в PKG_VERSION на последнюю из : https://git.zx2c4.com/WireGuard
|
||||||
|
скачайте tar.xz с этой версией , вычислите его sha256sum, впишите в PKG_HASH
|
||||||
|
|
||||||
|
1 раз где-нибудь пропатчите файлы последней версии wireguard в текстовом редакторе, скопируйте в build_dir,
|
||||||
|
сделайте версию для openwrt. эти же файлы скопируйте на ваш сервер с десктопным linux, сделайте там make / make install
|
||||||
|
|
||||||
|
Но имейте в виду, что build_dir - локация для временных файлов.
|
||||||
|
make clean оттуда все снесет, включая ваши модификации. Модифицированные файлы лучше сохранить отдельно,
|
||||||
|
чтобы потом было легко скопировать обратно.
|
||||||
|
|
||||||
|
Полученные ipk копируем на роутер в /tmp, устанавливаем через
|
||||||
|
# cd /tmp
|
||||||
|
# rm -r /tmp/opkg-lists
|
||||||
|
# opkg install *.ipk
|
||||||
|
Если требует зависимостей, то
|
||||||
|
# opkg update
|
||||||
|
# opkg install .... <зависимости>
|
||||||
|
# rm -r /tmp/opkg-lists
|
||||||
|
# opkg install *.ipk
|
||||||
|
|
||||||
|
В /tmp/opkg-lists opkg хранит кэш списка пакетов. Если попытаться установить файл ipk, и такой же пакет
|
||||||
|
найдется в репозитории, opkg будет устанавливать из репозитория. А нам это не надо.
|
||||||
|
|
||||||
|
# rmmod wireguard
|
||||||
|
# kmodloader
|
||||||
|
# dmesg | tail
|
||||||
|
должны увидеть что-то вроде :
|
||||||
|
[8985.415490] wireguard: WireGuard 0.0.20190123 loaded. See www.wireguard.com for information.
|
||||||
|
[8985.424178] wireguard: Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||||
|
значит модуль загрузился
|
||||||
|
|
||||||
|
Могут понадобиться ключи opkg --force-reinstall, --force-depends.
|
||||||
|
--force-depends поможет при несоответствии hash версии ядра. То есть версия x.x.x та же самая, но hash конфигурации разный.
|
||||||
|
При несоответствии x.x.x вы что-то делаете не так, работать это не будет.
|
||||||
|
Например : 4.14.56-1-b1186491495127cc6ff81d29c00a91fc, 4.14.56-1-3f8a21a63974cfb7ee67e41f2d4b805d
|
||||||
|
Это свидетельствует о несоответствии .config ядра при сборке прошивки и в SDK.
|
||||||
|
Если несоответствие легкое, то может все прокатить, но при более серьезной разнице в .config модуль может не загрузиться
|
||||||
|
или вызвать стабильные или хаотические падения ядра и перезагрузки (включая вариант беcконечной перезагрузки - bootloop).
|
||||||
|
Так что перед --force-depends убедитесь, что знаете как лечится такая ситуация, и не стоит это делать при отсутствии физического
|
||||||
|
доступа к девайсу.
|
||||||
|
|
||||||
|
Когда поднимите линк, и вдруг ничего не будет работать, то посмотрите в wireshark udp пакеты
|
||||||
|
на порт endpoint. Они не должны начинаться с 0,1,2,3,4. В первых 4 байтах должен быть рандом,
|
||||||
|
в следующих 4 байтах - значения из измененного enum message_type. Если пакет все еще начинается с 0..4,
|
||||||
|
значит модуль wireguard оригинальный, что-то не собралось, не скопировалось, не перезапустилось.
|
||||||
|
В противном случае должен подняться линк, пинги ходить. Значит вы победили, поздравляю.
|
||||||
|
Регулятору будет намного сложнее поймать ваш VPN.
|
519
docs/wireguard/wireguard_iproute_openwrt.txt
Normal file
519
docs/wireguard/wireguard_iproute_openwrt.txt
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
Есть возможность поднять свой VPN сервер ? Не хотим использовать redsocks ?
|
||||||
|
Хотим завертывать на VPN только часть трафика ?
|
||||||
|
Например, из ipset zapret только порт tcp:443, из ipban - весь трафик, не только tcp ?
|
||||||
|
Да, с VPN такое возможно.
|
||||||
|
Опишу понятийно как настраивается policy based routing в openwrt на примере wireguard.
|
||||||
|
Вместо wireguard можно использовать openvpn или любой другой. Но wireguard прекрасен сразу несколькими вещами.
|
||||||
|
Главная из которых - в разы большая скорость, даже немного превышающая ipsec.
|
||||||
|
Ведь openvpn основан на tun, а tun - всегда в разы медленнее решения в kernel mode,
|
||||||
|
и если для PC оно может быть не так актуально, для soho роутеров - более чем.
|
||||||
|
Wireguard может дать 50 mbps там, где openvpn еле тащит 10.
|
||||||
|
Но есть и дополнительное требование. Wireguard работает в ядре, значит ядро должно
|
||||||
|
быть под вашим контролем. vps на базе openvz не подойдет ! Нужен xen, kvm,
|
||||||
|
любой другой вариант, где загружается ваше собственное ядро, а не используется
|
||||||
|
общее, разделяемое на множество vps. В openvz вам никто не даст лезть в ядро.
|
||||||
|
|
||||||
|
Если вдруг окажется, что основные VPN протоколы блокируется DPI, включая wireguard,
|
||||||
|
то стоит смотреть в сторону либо обфускации трафика до состояния нераспознаваемого
|
||||||
|
мусора, либо маскировки под TLS (лучше на порт 443). Скорость, конечно, вы потеряете, но это
|
||||||
|
та самая ситуация, которая описывается словами "медленно или никак".
|
||||||
|
Маскированные под TLS протоколы DPI может распознать двумя действиями :
|
||||||
|
пассивно через анализ статистических характеристик пакетов (время, размер, периодичность, ..)
|
||||||
|
или активно через подключение к вашему серверу от себя и попытку поговорить с сервером по
|
||||||
|
известным протоколам (называется active probing). Если вы подключаетесь к серверу
|
||||||
|
с фиксированных IP, то активный пробинг можно надежно заблокировать через ограничение
|
||||||
|
диапазонов IP адресов, с которых можно подключаться к серверу. В ином случае можно использовать
|
||||||
|
технику "port knocking".
|
||||||
|
Перспективным направлением так же считаю легкую собственную модификацию исходников
|
||||||
|
существующих VPN с целью незначительного изменения протокола, которая ломает стандартные
|
||||||
|
модули обнаружения в DPI. В wireguard можно добавить в начало пакета handshake лишнее поле,
|
||||||
|
заполненное случайным мусором. Разумеется, в таком случае требуется держать измененную версию
|
||||||
|
как на сервере, так и на клиенте. Если затея срабатывает, то вы получаете максимальную
|
||||||
|
скорость, при этом полностью нагибая регулятора.
|
||||||
|
Полезная инфа по теме : https://habr.com/ru/post/415977/
|
||||||
|
|
||||||
|
Понятийно необходимо выполнить следующие шаги :
|
||||||
|
1) Поднять vpn сервер.
|
||||||
|
2) Настроить vpn клиент. Результат этого шага - получение поднятого интерфейса vpn.
|
||||||
|
Будь то wireguard, openvpn или любой другой тип vpn.
|
||||||
|
3) Создать такую схему маршрутизации, при которой пакеты, помечаемые особым mark,
|
||||||
|
попадают на vpn, а остальные идут обычным способом.
|
||||||
|
4) Создать правила, выставляющие mark для всего трафика, который необходимо рулить на vpn.
|
||||||
|
Критерии могут быть любые, ограниченные лишь возможностями iptables и вашим воображением.
|
||||||
|
|
||||||
|
Будем считать наш vpn сервер находится на ip 91.15.68.202.
|
||||||
|
Вешать его будем на udp порт 12345. На этот же порт будем вешать и клиентов.
|
||||||
|
Сервер работает под debian 9. Клиент работает под openwrt.
|
||||||
|
Для vpn отведем подсеть 192.168.254.0/24.
|
||||||
|
|
||||||
|
--- Поднятие сервера ---
|
||||||
|
|
||||||
|
На сервере должны быть установлены заголовки ядра (linux-headers-...) и компилятор gcc.
|
||||||
|
Качаем последний tar.xz с wireguard отсюда : https://git.zx2c4.com/WireGuard/
|
||||||
|
|
||||||
|
# tar xf WireGuard*.tar.xz
|
||||||
|
# cd WireGuard-*/src
|
||||||
|
# make
|
||||||
|
# strip --strip-debug wireguard.ko
|
||||||
|
# sudo make install
|
||||||
|
|
||||||
|
wireguard основан на понятии криптороутинга. Каждый пир (сервер - тоже пир)
|
||||||
|
имеет пару открытый/закрытый ключ. Закрытый ключ остается у пира,
|
||||||
|
открытый прописывается у его партнера. Каждый пир авторизует другого
|
||||||
|
по знанию приватного ключа, соответствующего прописанному у него публичному ключу.
|
||||||
|
Протокол построен таким образом, что на все неправильные udp пакеты не следует ответа.
|
||||||
|
Не знаешь приватный ключ ? Не смог послать правильный запрос ? Долбись сколько влезет,
|
||||||
|
я тебе ничего не отвечу. Это защищает от активного пробинга со стороны DPI и просто
|
||||||
|
экономит ресурсы.
|
||||||
|
Значит первым делом нужно создать 2 пары ключей : для сервера и для клиента.
|
||||||
|
wg genkey генерит приватный ключ, wg pubkey получает из него публичный ключ.
|
||||||
|
|
||||||
|
# wg genkey
|
||||||
|
oAUkmhoREtFQ5D5yZmeHEgYaSWCcLYlKe2jBP7EAGV0=
|
||||||
|
# echo oAUkmhoREtFQ5D5yZmeHEgYaSWCcLYlKe2jBP7EAGV0= | wg pubkey
|
||||||
|
bCdDaPYSTBZVO1HTmKD+Tztuf3PbOWGDWfz7Lb1E6C4=
|
||||||
|
# wg genkey
|
||||||
|
OKXX0TSlyjJmGt3/yHlHxi0AqjJ0vh+Msne3qEHk0VM=
|
||||||
|
# echo OKXX0TSlyjJmGt3/yHlHxi0AqjJ0vh+Msne3qEHk0VM= | wg pubkey
|
||||||
|
EELdA2XzjcKxtriOCPBXMOgxlkgpbRdIyjtc3aIpkxg=
|
||||||
|
|
||||||
|
Пишем конфиг
|
||||||
|
--/etc/wireguard/wgvps.conf-------------------
|
||||||
|
[Interface]
|
||||||
|
PrivateKey = OKXX0TSlyjJmGt3/yHlHxi0AqjJ0vh+Msne3qEHk0VM=
|
||||||
|
ListenPort = 12345
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
#Endpoint =
|
||||||
|
PublicKey = bCdDaPYSTBZVO1HTmKD+Tztuf3PbOWGDWfz7Lb1E6C4=
|
||||||
|
AllowedIPs = 192.168.254.3
|
||||||
|
PersistentKeepalive=20
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
Wireguard - минималистичный vpn. В нем нет никаких средств для автоконфигурации ip.
|
||||||
|
Все придется прописывать руками.
|
||||||
|
В wgvps.conf должны быть перечислены все пиры с их публичными ключами,
|
||||||
|
а так же прописаны допустимые для них ip адреса.
|
||||||
|
Назначим нашему клиенту 192.168.254.3. Сервер будет иметь ip 192.168.254.1.
|
||||||
|
Endpoint должен быть прописан хотя бы на одном пире.
|
||||||
|
Если endpoint настроен для пира, то wireguard будет периодически пытаться к нему подключиться.
|
||||||
|
В схеме клиент/сервер у сервера можно не прописывать endpoint-ы пиров, что позволит
|
||||||
|
менять ip и быть за nat. Endpoint пира настраивается динамически после успешной фазы
|
||||||
|
проверки ключа.
|
||||||
|
|
||||||
|
Включаем маршрутизцию :
|
||||||
|
# echo net.ipv4.ip_forward = 1 >>/etc/sysctl.conf
|
||||||
|
# sysctl -p
|
||||||
|
|
||||||
|
Интерфейс конфигурится стандартно для дебианоподобных систем :
|
||||||
|
|
||||||
|
--/etc/network/interfaces.d/wgvps-------------
|
||||||
|
auto wgvps
|
||||||
|
iface wgvps inet static
|
||||||
|
address 192.168.254.1
|
||||||
|
netmask 255.255.255.0
|
||||||
|
pre-up ip link add $IFACE type wireguard
|
||||||
|
pre-up wg setconf $IFACE /etc/wireguard/$IFACE.conf
|
||||||
|
post-up iptables -t nat -A POSTROUTING -o eth0 -s 192.168.254.0/24 -j MASQUERADE
|
||||||
|
post-up iptables -A FORWARD -o eth0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
|
||||||
|
post-down iptables -D FORWARD -o eth0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
|
||||||
|
post-down iptables -t nat -D POSTROUTING -o eth0 -s 192.168.254.0/24 -j MASQUERADE
|
||||||
|
post-down ip link del $IFACE
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
Поднятие через ifup wgvps, опускание через ifdown wgvps.
|
||||||
|
При поднятии интерфейса заодно настраивается nat. eth0 здесь означает интерфейс vpn сервера с инетовским ip адресом.
|
||||||
|
Если у вас какая-то система управления фаерволом, то надо настройку nat прикручивать туда.
|
||||||
|
Пример написан для простейшего случая, когда никаких ограничений нет, таблицы iptables пустые.
|
||||||
|
Чтобы посмотреть текущие настройки wireguard, запустите 'wg' без параметров.
|
||||||
|
|
||||||
|
|
||||||
|
--- Поднятие клиента ---
|
||||||
|
|
||||||
|
# opkg update
|
||||||
|
# opkg install wireguard
|
||||||
|
|
||||||
|
Добавляем записи в конфиги.
|
||||||
|
|
||||||
|
--/etc/config/network--------------------------
|
||||||
|
config interface 'wgvps'
|
||||||
|
option proto 'wireguard'
|
||||||
|
option auto '1'
|
||||||
|
option private_key 'oAUkmhoREtFQ5D5yZmeHEgYaSWCcLYlKe2jBP7EAGV0='
|
||||||
|
option listen_port '12345'
|
||||||
|
option metric '9'
|
||||||
|
option mtu '1420'
|
||||||
|
|
||||||
|
config wireguard_wgvps
|
||||||
|
option public_key 'EELdA2XzjcKxtriOCPBXMOgxlkgpbRdIyjtc3aIpkxg=
|
||||||
|
list allowed_ips '0.0.0.0/0'
|
||||||
|
option endpoint_host '91.15.68.202'
|
||||||
|
option endpoint_port '12345'
|
||||||
|
option route_allowed_ips '0'
|
||||||
|
option persistent_keepalive '20'
|
||||||
|
|
||||||
|
config interface 'wgvps_ip'
|
||||||
|
option proto 'static'
|
||||||
|
option ifname '@wgvps'
|
||||||
|
list ipaddr '192.168.254.3/24'
|
||||||
|
|
||||||
|
config route
|
||||||
|
option interface 'wgvps'
|
||||||
|
option target '0.0.0.0/0'
|
||||||
|
option table '100'
|
||||||
|
|
||||||
|
config rule
|
||||||
|
option mark '0x800/0x800'
|
||||||
|
option priority '100'
|
||||||
|
option lookup '100'
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
--/etc/config/firewall--------------------------
|
||||||
|
config zone
|
||||||
|
option name 'tunvps'
|
||||||
|
option output 'ACCEPT'
|
||||||
|
option input 'REJECT'
|
||||||
|
option masq '1'
|
||||||
|
option mtu_fix '1'
|
||||||
|
option forward 'REJECT'
|
||||||
|
option network 'wgvps wgvps_ip'
|
||||||
|
|
||||||
|
config forwarding
|
||||||
|
option dest 'tunvps'
|
||||||
|
option src 'lan'
|
||||||
|
|
||||||
|
config rule
|
||||||
|
option name 'Allow-ICMP-tunvps'
|
||||||
|
option src 'tunvps'
|
||||||
|
option proto 'icmp'
|
||||||
|
option target 'ACCEPT'
|
||||||
|
|
||||||
|
config rule
|
||||||
|
option target 'ACCEPT'
|
||||||
|
option src 'wan'
|
||||||
|
option proto 'udp'
|
||||||
|
option family 'ipv4'
|
||||||
|
option src_port '12345'
|
||||||
|
option src_ip '91.15.68.202'
|
||||||
|
option name 'WG-VPS'
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
Что тут было сделано :
|
||||||
|
*) Настроен интерфейс wireguard. Указан собственный приватный ключ.
|
||||||
|
*) Настроен пир-партнер с указанием его публичнго ключа и endpoint (ip:port нашего сервера)
|
||||||
|
такая настройка заставит периодически долбиться на сервер по указанному ip
|
||||||
|
route_allowed_ip '0' запрещает автоматическое создание маршрута
|
||||||
|
allowed_ips '0.0.0.0/0' разрешает пакеты с любым адресом источника.
|
||||||
|
ведь мы собираемся подключаться к любым ip в инете
|
||||||
|
persistent_keepalive '20' помогает исключить дропание mapping на nat-е, если мы сидим за ним,
|
||||||
|
да и вообще полезная вещь, чтобы не было подвисших пиров
|
||||||
|
*) Статическая конфигурация ip интерфейса wgvps.
|
||||||
|
*) Маршрут default route на wgvps в отдельной таблице маршрутизации с номером 100. Аналог команды ip route add .. table 100
|
||||||
|
*) Правило использовать таблицу 100 при выставлении в mark бита 0x800. Аналог команды ip rule.
|
||||||
|
*) Отдельная зона фаервола для VPN - 'tunvps'. В принципе ее можно не создавать, можете приписать интерфейс к зоне wan.
|
||||||
|
Но в случае с отдельной зоной можно настроить особые правила на подключения с vpn сервера в сторону клиента.
|
||||||
|
*) Разрешение форвардинга между локалкой за роутером и wgvps.
|
||||||
|
*) Разрешение принимать icmp от vpn сервера, включая пинги. ICMP жизненно важны для правильного функционирования ip сети !
|
||||||
|
*) И обязательно проткнуть дырку в фаерволе, чтобы принимать пакеты wireguard со стороны инетовского ip vpn сервера.
|
||||||
|
|
||||||
|
# fw3 restart
|
||||||
|
# ifup wgvps
|
||||||
|
# ifconfig wgvps
|
||||||
|
# ping 192.168.254.1
|
||||||
|
|
||||||
|
Если все хорошо, должны ходить пинги.
|
||||||
|
С сервера не помешает :
|
||||||
|
# ping 192.168.254.3
|
||||||
|
|
||||||
|
|
||||||
|
--- Маркировка трафика ---
|
||||||
|
|
||||||
|
Завернем на vpn все из ipset zapret на tcp:443 и все из ipban.
|
||||||
|
OUTPUT относится к исходящим с роутера пакетам, PREROUTING - ко всем остальным.
|
||||||
|
Если с самого роутера ничего заруливать не надо, можно опустить все до команд с PREROUTING.
|
||||||
|
|
||||||
|
--/etc/firewall.user----------------------------
|
||||||
|
. /opt/zapret/init.d/openwrt/functions
|
||||||
|
|
||||||
|
create_ipset no-update
|
||||||
|
|
||||||
|
network_find_wan_all wan_iface
|
||||||
|
for ext_iface in $wan_iface; do
|
||||||
|
network_get_device DEVICE $ext_iface
|
||||||
|
ipt OUTPUT -t mangle -o $DEVICE -p tcp --dport 443 -m set --match-set zapret dst -j MARK --set-mark 0x800/0x800
|
||||||
|
ipt OUTPUT -t mangle -o $DEVICE -m set --match-set ipban dst -j MARK --set-mark 0x800/0x800
|
||||||
|
done
|
||||||
|
|
||||||
|
network_get_device DEVICE lan
|
||||||
|
ipt PREROUTING -t mangle -i $DEVICE -p tcp --dport 443 -m set --match-set zapret dst -j MARK --set-mark 0x800/0x800
|
||||||
|
ipt PREROUTING -t mangle -i $DEVICE -m set --match-set ipban dst -j MARK --set-mark 0x800/0x800
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
# fw3 restart
|
||||||
|
|
||||||
|
|
||||||
|
--- По поводу двойного NAT ---
|
||||||
|
|
||||||
|
В описанной конфигурации nat выполняется дважды : на роутере-клиенте происходит замена адреса источника из LAN
|
||||||
|
на 192.168.254.3 и на сервере замена 192.168.254.3 на внешний адрес сервера в инете.
|
||||||
|
Зачем так делать ? Исключительно для простоты настройки. Но если вы готовы чуток еще поднапрячься и не хотите двойного nat,
|
||||||
|
то можете вписать в /etc/config/firewall "masq '0'", на сервер дописать маршрут до вашей подсети lan.
|
||||||
|
Чтобы не делать это для каждого клиента, можно отвести под всех клиентов диапазон 192.168.0.0-192.168.127.255
|
||||||
|
и прописать его одним маршрутом.
|
||||||
|
|
||||||
|
--/etc/network/interfaces.d/wgvps-------------
|
||||||
|
post-up ip route add dev $IFACE 192.168.0.0/17
|
||||||
|
post-down ip route del dev $IFACE 192.168.0.0/17
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
Так же необходимо указать wireguard дополнительные разрешенные ip для peer :
|
||||||
|
|
||||||
|
--/etc/wireguard/wgvps.conf-------------------
|
||||||
|
[Peer]
|
||||||
|
PublicKey = bCdDaPYSTBZVO1HTmKD+Tztuf3PbOWGDWfz7Lb1E6C4=
|
||||||
|
AllowedIPs = 192.168.254.3, 192.168.2.0/24
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
Всем клиентам придется назначать различные диапазоны адресов в lan и индивидуально прописывать AllowedIPs
|
||||||
|
для каждого peer.
|
||||||
|
|
||||||
|
# ifdown wgvps ; ifup wgvps
|
||||||
|
|
||||||
|
На клиенте разрешим форвард icmp, чтобы работал пинг и корректно определялось mtu.
|
||||||
|
|
||||||
|
--/etc/config/firewall--------------------------
|
||||||
|
config rule
|
||||||
|
option name 'Allow-ICMP-tunvps'
|
||||||
|
option src 'tunvps'
|
||||||
|
option dest 'lan'
|
||||||
|
option proto 'icmp'
|
||||||
|
option target 'ACCEPT'
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
Существуют еще два неочевидных нюанса.
|
||||||
|
|
||||||
|
Первый из них касается пакетов с самого роутера (цепочка OUTPUT).
|
||||||
|
Адрес источника выбирается по особому алгоритму, если программа явно его не задала, еще до этапа iptables.
|
||||||
|
Он берется с интерфейса, куда бы пошел пакет при нормальном раскладе.
|
||||||
|
Обратная маршрутизация с VPN станет невозможной, да и wireguard такие пакеты порежет, поскольку они не вписываются в AllowedIPs.
|
||||||
|
Никаким мистическим образом автоматом source address не поменяется.
|
||||||
|
В прошлом варианте настройки проблема решалось через маскарад. Сейчас же маскарада нет.
|
||||||
|
Потому все же придется его делать в случае, когда пакет изначально направился бы через wan,
|
||||||
|
а мы его завертываем на VPN. Помечаем такие пакеты марком 0x1000.
|
||||||
|
Если вам не актуальны исходящие с самого роутера, то можно ничего не менять.
|
||||||
|
|
||||||
|
Другой нюанс связан с обработкой проброшенных на vps портов, соединения по которым приходят как входящие с интерфейса wgvps.
|
||||||
|
Представьте себе, что вы пробросили порт 2222. Кто-то подключается с адреса 1.2.3.4. Вам приходит пакет SYN 1.2.3.4:51723=>192.168.2.2:2222.
|
||||||
|
По правилам маршрутизации он пойдет в локалку. 192.168.2.2 его обработает, ответит пакетом ACK 192.168.2.2:2222=>1.2.3.4:51723.
|
||||||
|
Этот пакет придет на роутер. И куда он дальше пойдет ? Если он не занесен в ipban, то согласно правилам машрутизации
|
||||||
|
он пойдет по WAN интерфейсу, а не по исходному wgvps.
|
||||||
|
Чтобы решить эту проблему, необходимо воспользоваться CONNMARK. Существуют 2 отдельных марка : fwmark и connmark.
|
||||||
|
connmark относится к соединению, fwmark - к пакету. Трэкингом соединений занимается conntrack.
|
||||||
|
Посмотреть его таблицу можно командой "conntrack -L". Там же найдете connmark : mark=xxxx.
|
||||||
|
Как только видим приходящий с wgvps пакет с новым соединением, отмечаем его connmark как 0x800/0x800.
|
||||||
|
При этом fwmark не меняется, иначе бы пакет тут же бы завернулся обратно на wgvps согласно ip rule.
|
||||||
|
Если к нам приходит пакет с какого-то другого интерфейса, то восстанавливаем его connmark в fwmark по маске 0x800.
|
||||||
|
И теперь он подпадает под правило ip rule, заворачиваясь на wgvps, что и требовалось.
|
||||||
|
|
||||||
|
--/etc/firewall.user----------------------------
|
||||||
|
. /opt/zapret/init.d/openwrt/functions
|
||||||
|
|
||||||
|
create_ipset no-update
|
||||||
|
|
||||||
|
network_find_wan_all wan_iface
|
||||||
|
for ext_iface in $wan_iface; do
|
||||||
|
network_get_device DEVICE $ext_iface
|
||||||
|
ipt OUTPUT -t mangle -o $DEVICE -p tcp --dport 443 -m set --match-set zapret dst -j MARK --set-mark 0x800/0x800
|
||||||
|
ipt OUTPUT -t mangle -o $DEVICE -m set --match-set ipban dst -j MARK --set-mark 0x800/0x800
|
||||||
|
ipt OUTPUT -t mangle -o $DEVICE -j MARK --set-mark 0x1000/0x1000
|
||||||
|
done
|
||||||
|
|
||||||
|
network_get_device DEVICE lan
|
||||||
|
ipt PREROUTING -t mangle -i $DEVICE -p tcp --dport 443 -m set --match-set zapret dst -j MARK --set-mark 0x800/0x800
|
||||||
|
ipt PREROUTING -t mangle -i $DEVICE -m set --match-set ipban dst -j MARK --set-mark 0x800/0x800
|
||||||
|
|
||||||
|
# do masquerade for OUTPUT to ensure correct outgoing address
|
||||||
|
ipt postrouting_tunvps_rule -t nat -m mark --mark 0x1000/0x1000 -j MASQUERADE
|
||||||
|
|
||||||
|
# incoming from wgvps
|
||||||
|
network_get_device DEVICE wgvps
|
||||||
|
ipt PREROUTING -t mangle ! -i $DEVICE -j CONNMARK --restore-mark --nfmask 0x800 --ctmask 0x800
|
||||||
|
ipt PREROUTING -t mangle -i $DEVICE -m conntrack --ctstate NEW -j CONNMARK --set-xmark 0x800/0x800
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# fw3 restart
|
||||||
|
|
||||||
|
Сейчас уже можно с vpn сервера пингануть ip адрес внутри локалки клиента. Пинги должны ходить.
|
||||||
|
|
||||||
|
Отсутствие двойного NAT значительно облегчает проброс портов с внешнего IP vpn сервера в локалку какого-либо клиента.
|
||||||
|
Для этого надо выполнить 2 действия : добавить разрешение в фаервол на клиенте и сделать dnat на сервере.
|
||||||
|
Пример форварда портов 5001 и 5201 на 192.168.2.2 :
|
||||||
|
|
||||||
|
--/etc/config/firewall--------------------------
|
||||||
|
config rule
|
||||||
|
option target 'ACCEPT'
|
||||||
|
option src 'tunvps'
|
||||||
|
option dest 'lan'
|
||||||
|
option proto 'tcp udp'
|
||||||
|
option dest_port '5001 5201'
|
||||||
|
option dest_ip '192.168.2.2'
|
||||||
|
option name 'IPERF'
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
# fw3 restart
|
||||||
|
|
||||||
|
--/etc/network/interfaces.d/wgvps-------------
|
||||||
|
post-up iptables -t nat -A PREROUTING -i eth0 -p tcp -m multiport --dports 5001,5201 -j DNAT --to-destination 192.168.2.2
|
||||||
|
post-up iptables -t nat -A POSTROUTING -o $IFACE -d 192.168.2.2 -p tcp -m multiport --dports 5001,5201 -j MASQUERADE
|
||||||
|
post-up iptables -t nat -A PREROUTING -i eth0 -p udp -m multiport --dports 5001,5201 -j DNAT --to-destination 192.168.2.2
|
||||||
|
post-up iptables -t nat -A POSTROUTING -o $IFACE -d 192.168.2.2 -p udp -m multiport --dports 5001,5201 -j MASQUERADE
|
||||||
|
post-down iptables -t nat -D PREROUTING -i eth0 -p tcp -m multiport --dports 5001,5201 -j DNAT --to-destination 192.168.2.2
|
||||||
|
post-down iptables -t nat -D POSTROUTING -o $IFACE -d 192.168.2.2 -p tcp -m multiport --dports 5001,5201 -j MASQUERADE
|
||||||
|
post-down iptables -t nat -D PREROUTING -i eth0 -p udp -m multiport --dports 5001,5201 -j DNAT --to-destination 192.168.2.2
|
||||||
|
post-down iptables -t nat -D POSTROUTING -o $IFACE -d 192.168.2.2 -p udp -m multiport --dports 5001,5201 -j MASQUERADE
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
# ifdown wgvps ; ifup wgvps
|
||||||
|
|
||||||
|
Пример приведен для iperf и iperf3, чтобы показать как пробрасывать несколько портов tcp+udp с минимальным количеством команд.
|
||||||
|
Проброс tcp и udp порта так же необходим для полноценной работы bittorrent клиента, чтобы работали входящие.
|
||||||
|
|
||||||
|
--- Как мне отправлять на vpn весь трафик с bittorrent ? ---
|
||||||
|
|
||||||
|
Можно поступить так : посмотрите порт в настройках torrent клиента, убедитесь, что не поставлено "случайный порт",
|
||||||
|
добавьте на роутер правило маркировки по порту источника.
|
||||||
|
Но мне предпочтительно иное решение. На windows есть замечательная возможность
|
||||||
|
прописать правило установки поля качества обслуживания в заголовках ip пакетов в зависимости от процесса-источника.
|
||||||
|
Для windows 7/2008R2 необходимо будет установить ключик реестра и перезагрузить комп :
|
||||||
|
# reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\QoS /v "Do not use NLA" /t REG_SZ /d "1"
|
||||||
|
Редактировать политику можно в : gpedit.msc -> Computer Configuration -> Windows Settings -> Policy-based QoS
|
||||||
|
На win 10 ключик реестра больше не работает, правила qos в gpedit применяются только для профиля домена.
|
||||||
|
Необходимо пользоваться командой powershell New-NetQosPolicy. Гуглите хелп по ней. Пример :
|
||||||
|
# powershell New-NetQosPolicy -Name "torrent" -AppPathNameMatchCondition "qbittorrent.exe" -DSCPAction 1
|
||||||
|
Однозначно требуется проверка в wireshark или netmon успешности установки поля dscp. Если там по-прежнему 0x00,
|
||||||
|
значит что-то не сработало. 0x04 означает DSCP=1 (dscp находится в старших 6 битах).
|
||||||
|
|
||||||
|
На роутере в фаер прописываем правило :
|
||||||
|
|
||||||
|
--/etc/config/firewall--------------------------
|
||||||
|
config rule
|
||||||
|
option target 'MARK'
|
||||||
|
option src 'lan'
|
||||||
|
option proto 'all'
|
||||||
|
option extra '-m dscp --dscp 1'
|
||||||
|
option name 'route-dscp-1'
|
||||||
|
option set_mark '0x0800/0x0800'
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
# fw3 restart
|
||||||
|
|
||||||
|
Теперь все с полем dscp "1" идет на vpn. Клиент сам решает какой трафик ему нужно забрасывать
|
||||||
|
на vpn, перенастраивать роутер не нужно.
|
||||||
|
На linux клиенте проще всего будет выставлять dscp в iptables по номеру порта источника :
|
||||||
|
|
||||||
|
--/etc/rc.local---------------------------------
|
||||||
|
iptables -A OUTPUT -t mangle -p tcp --sport 23444 -j DSCP --set-dscp 1
|
||||||
|
iptables -A OUTPUT -t mangle -p udp --sport 23444 -j DSCP --set-dscp 1
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
можно привязываться к pid процесса, но тогда нужно перенастраивать iptables при каждом перезапуске
|
||||||
|
торент клиента, это требует рута, и все становится очень неудобно.
|
||||||
|
|
||||||
|
|
||||||
|
--- Автоматизация проброса портов через miniupnd ---
|
||||||
|
|
||||||
|
Да, его тоже можно использовать на vps. Только как всегда есть нюансы.
|
||||||
|
|
||||||
|
miniupnpd поддерживает 3 протокола IGD : upnp,nat-pmp и pcp.
|
||||||
|
upnp и pcp работают через мультикаст, который не пройдет через wgvps.
|
||||||
|
nat-pmp работает через посылку специальных сообщений на udp:5351 на default gateway.
|
||||||
|
Обычно их обслуживает miniupnpd на роутере. При создании lease miniupnpd добавляет
|
||||||
|
правила для проброса портов в цепочку iptables MINIUPNPD, при потери lease - убирает.
|
||||||
|
|
||||||
|
udp:5351 можно перенаправить на vpn сервер через DNAT, чтобы их обрабатывал miniupnpd там.
|
||||||
|
Но вы должны иметь однозначный критерий перенаправления.
|
||||||
|
Если вы решили завернуть на vpn все, то проблем нет. Пробрасываем udp:5351 безусловно.
|
||||||
|
Если у вас идет перенаправление только с торрент, то необходимо к условию перенаправления
|
||||||
|
добавить условия, выделяющие torrent трафик из прочего. Или по dscp, или по sport.
|
||||||
|
Чтобы запросы от остальных программ обрабатывались miniupnpd на роутере.
|
||||||
|
Если какая-то программа создаст lease не там, где нужно, то входящий трафик до нее не дойдет.
|
||||||
|
|
||||||
|
На роутере стоит запретить протокол upnp, чтобы торрент клиент не удовлетворился запросом,
|
||||||
|
обслуженным по upnp на роутере, и пытался использовать nat-pmp.
|
||||||
|
|
||||||
|
--/etc/config/upnp--------------------------
|
||||||
|
config upnpd 'config'
|
||||||
|
.....
|
||||||
|
option enable_upnp '0'
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
/etc/init.d/miniupnpd restart
|
||||||
|
|
||||||
|
Делаем проброс порта на роутере.
|
||||||
|
Для простоты изложения будем считать, что на vpn у нас завернут весь трафик.
|
||||||
|
Если это не так, то следует добавить фильтр в "config redirect".
|
||||||
|
Заодно выделяем диапазон портов для торрент клиентов.
|
||||||
|
Порт в торент клиенте следует прописать какой-то из этого диапазона.
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
config redirect
|
||||||
|
option enabled '1'
|
||||||
|
option target 'DNAT'
|
||||||
|
option src 'lan'
|
||||||
|
option dest 'tunvps'
|
||||||
|
option proto 'udp'
|
||||||
|
option src_dport '5351'
|
||||||
|
option dest_ip '192.168.254.1'
|
||||||
|
option dest_port '5351'
|
||||||
|
option name 'NAT-PMP'
|
||||||
|
option reflection '0'
|
||||||
|
config rule
|
||||||
|
option enabled '1'
|
||||||
|
option target 'ACCEPT'
|
||||||
|
option src 'tunvps'
|
||||||
|
option dest 'lan'
|
||||||
|
option name 'tunvps-torrent'
|
||||||
|
option dest_port '28000-28009'
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
fw3 reload
|
||||||
|
|
||||||
|
|
||||||
|
На сервере :
|
||||||
|
|
||||||
|
apt install miniupnpd
|
||||||
|
|
||||||
|
--- /etc/miniupnpd/miniupnpd.conf --------
|
||||||
|
enable_natpmp=yes
|
||||||
|
enable_upnp=no
|
||||||
|
lease_file=/var/log/upnp.leases
|
||||||
|
system_uptime=yes
|
||||||
|
clean_ruleset_threshold=10
|
||||||
|
clean_ruleset_interval=600
|
||||||
|
force_igd_desc_v1=no
|
||||||
|
listening_ip=192.168.254.1/16
|
||||||
|
ext_ifname=eth0
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
systemctl restart miniupnpd
|
||||||
|
|
||||||
|
listening_ip прописан именно таким образом, чтобы обозначить диапазон разрешенных IP.
|
||||||
|
С других IP он не будет обрабатывать запросы на редирект.
|
||||||
|
В ext_ifname впишите название inet интерфейса на сервере.
|
||||||
|
|
||||||
|
Запускаем торрент клиент. Попутно смотрим в tcpdump весь путь udp:5351 до сервера и обратно.
|
||||||
|
Смотрим syslog сервера на ругань от miniupnpd.
|
||||||
|
Если все ок, то можем проверить редиректы : iptables -t nat -nL MINIUPNPD
|
||||||
|
С какого-нибудь другого хоста (не vpn сервер, не ваше подключение) можно попробовать telnet-нуться на проброшенный порт.
|
||||||
|
Должно установиться соединение. Или качайте торент и смотрите в пирах флаг "I" (incoming).
|
||||||
|
Если "I" есть и по ним идет закачка, значит все в порядке.
|
||||||
|
|
||||||
|
ОСОБЕННОСТЬ НОВЫХ DEBIAN : по умолчанию используются iptables-nft. miniupnpd работает с iptables-legacy.
|
||||||
|
ЛЕЧЕНИЕ : update-alternatives --set iptables /usr/sbin/iptables-legacy
|
||||||
|
|
||||||
|
|
||||||
|
--- А если не заработало ? ---
|
||||||
|
|
||||||
|
Мануал пишется не как копипастная инструкция, а как помощь уже соображающему.
|
||||||
|
В руки вам ifconfig, ip, iptables, tcpdump, ping. В умелых руках творят чудеса.
|
8
init.d/openwrt/90-zapret
Normal file
8
init.d/openwrt/90-zapret
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
ZAPRET=/etc/init.d/zapret
|
||||||
|
[ -x "$ZAPRET" ] && [ "$INTERFACE" = "lan" ] && {
|
||||||
|
[ "$ACTION" = "ifup" ] && {
|
||||||
|
$ZAPRET enabled && $ZAPRET start
|
||||||
|
}
|
||||||
|
}
|
20
init.d/openwrt/custom
Normal file
20
init.d/openwrt/custom
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# this script contain your special code to launch daemons and configure firewall
|
||||||
|
# use helpers from "functions" file and "zapret" init script
|
||||||
|
# in case of upgrade keep this file only, do not modify others
|
||||||
|
|
||||||
|
zapret_custom_daemons()
|
||||||
|
{
|
||||||
|
# PLACEHOLDER
|
||||||
|
echo !!! NEED ATTENTION !!!
|
||||||
|
echo Start daemon\(s\)
|
||||||
|
echo Study how other sections work
|
||||||
|
|
||||||
|
run_daemon 1 /bin/sleep 20
|
||||||
|
}
|
||||||
|
zapret_custom_firewall()
|
||||||
|
{
|
||||||
|
# PLACEHOLDER
|
||||||
|
echo !!! NEED ATTENTION !!!
|
||||||
|
echo Configure iptables for required actions
|
||||||
|
echo Study how other sections work
|
||||||
|
}
|
3
init.d/openwrt/firewall.zapret
Normal file
3
init.d/openwrt/firewall.zapret
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
. /opt/zapret/init.d/openwrt/functions
|
||||||
|
|
||||||
|
zapret_apply_firewall
|
320
init.d/openwrt/functions
Normal file
320
init.d/openwrt/functions
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
. /lib/functions/network.sh
|
||||||
|
|
||||||
|
[ -n "$ZAPRET_BASE" ] || ZAPRET_BASE=/opt/zapret
|
||||||
|
. "$ZAPRET_BASE/config"
|
||||||
|
|
||||||
|
QNUM=200
|
||||||
|
TPPORT_HTTP=1188
|
||||||
|
TPPORT_HTTPS=1189
|
||||||
|
TPWS_USER=daemon
|
||||||
|
[ -n "$DESYNC_MARK" ] || DESYNC_MARK=0x40000000
|
||||||
|
|
||||||
|
# max wait time for the link local ipv6 on the LAN interface
|
||||||
|
LINKLOCAL_WAIT_SEC=5
|
||||||
|
|
||||||
|
IPSET_CR="$ZAPRET_BASE/ipset/create_ipset.sh"
|
||||||
|
|
||||||
|
CUSTOM_SCRIPT="$ZAPRET_BASE/init.d/openwrt/custom"
|
||||||
|
[ -f "$CUSTOM_SCRIPT" ] && . "$CUSTOM_SCRIPT"
|
||||||
|
|
||||||
|
IPSET_EXCLUDE="-m set ! --match-set nozapret"
|
||||||
|
IPSET_EXCLUDE6="-m set ! --match-set nozapret6"
|
||||||
|
|
||||||
|
exists()
|
||||||
|
{
|
||||||
|
which "$1" >/dev/null 2>/dev/null
|
||||||
|
}
|
||||||
|
existf()
|
||||||
|
{
|
||||||
|
type "$1" >/dev/null 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# can be multiple ipv6 outgoing interfaces
|
||||||
|
# uplink from isp, tunnelbroker, vpn, ...
|
||||||
|
# want them all. who knows what's the real one that blocks sites
|
||||||
|
# dont want any manual configuration - want to do it automatically
|
||||||
|
# standard network_find_wan[6] return only the first
|
||||||
|
# we use low level function from network.sh to avoid this limitation
|
||||||
|
# it can change theoretically and stop working
|
||||||
|
|
||||||
|
network_find_wan_all()
|
||||||
|
{
|
||||||
|
__network_ifstatus "$1" "" "[@.route[@.target='0.0.0.0' && !@.table]].interface" "" 10 2>/dev/null && return
|
||||||
|
network_find_wan $1
|
||||||
|
}
|
||||||
|
network_find_wan6_all()
|
||||||
|
{
|
||||||
|
__network_ifstatus "$1" "" "[@.route[@.target='::' && !@.table]].interface" "" 10 2>/dev/null && return
|
||||||
|
network_find_wan6 $1
|
||||||
|
}
|
||||||
|
|
||||||
|
ipt()
|
||||||
|
{
|
||||||
|
iptables -C "$@" 2>/dev/null || iptables -I "$@"
|
||||||
|
}
|
||||||
|
ipt6()
|
||||||
|
{
|
||||||
|
ip6tables -C "$@" 2>/dev/null || ip6tables -I "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# there's no route_localnet for ipv6
|
||||||
|
# the best we can is to route to link local of the incoming interface
|
||||||
|
# OUTPUT - can DNAT to ::1
|
||||||
|
# PREROUTING - can't DNAT to ::1. can DNAT to link local of -i interface or to any global addr
|
||||||
|
# not a good idea to expose tpws to the world (bind to ::)
|
||||||
|
|
||||||
|
get_ipv6_linklocal()
|
||||||
|
{
|
||||||
|
# $1 - interface name. if empty - any interface
|
||||||
|
if exists ip ; then
|
||||||
|
local dev
|
||||||
|
[ -n "$1" ] && dev="dev $1"
|
||||||
|
ip addr show $dev | sed -e 's/^.*inet6 \([^ ]*\)\/[0-9]* scope link.*$/\1/;t;d' | head -n 1
|
||||||
|
else
|
||||||
|
ifconfig $1 | sed -re 's/^.*inet6 addr: ([^ ]*)\/[0-9]* Scope:Link.*$/\1/;t;d' | head -n 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
get_ipv6_global()
|
||||||
|
{
|
||||||
|
# $1 - interface name. if empty - any interface
|
||||||
|
if exists ip ; then
|
||||||
|
local dev
|
||||||
|
[ -n "$1" ] && dev="dev $1"
|
||||||
|
ip addr show $dev | sed -e 's/^.*inet6 \([^ ]*\)\/[0-9]* scope global.*$/\1/;t;d' | head -n 1
|
||||||
|
else
|
||||||
|
ifconfig $1 | sed -re 's/^.*inet6 addr: ([^ ]*)\/[0-9]* Scope:Global.*$/\1/;t;d' | head -n 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dnat6_target()
|
||||||
|
{
|
||||||
|
# get target ip address for DNAT. prefer link locals
|
||||||
|
# tpws should be as inaccessible from outside as possible
|
||||||
|
# link local address can appear not immediately after ifup
|
||||||
|
|
||||||
|
# DNAT6_TARGET=- means attempt was made but address was not found (to avoid multiple re-attempts)
|
||||||
|
|
||||||
|
[ -n "$DNAT6_TARGET" ] || {
|
||||||
|
# no reason to query if its down
|
||||||
|
network_is_up lan || return
|
||||||
|
|
||||||
|
local DEVICE
|
||||||
|
network_get_device DEVICE lan
|
||||||
|
|
||||||
|
local ct=0
|
||||||
|
while
|
||||||
|
DNAT6_TARGET=$(get_ipv6_linklocal $DEVICE)
|
||||||
|
[ -n "$DNAT6_TARGET" ] && break
|
||||||
|
[ "$ct" -ge "$LINKLOCAL_WAIT_SEC" ] && break
|
||||||
|
echo waiting for the link local for another $(($LINKLOCAL_WAIT_SEC - $ct)) seconds ...
|
||||||
|
ct=$(($ct+1))
|
||||||
|
sleep 1
|
||||||
|
do :; done
|
||||||
|
|
||||||
|
[ -n "$DNAT6_TARGET" ] || {
|
||||||
|
echo no link local. getting global
|
||||||
|
DNAT6_TARGET=$(get_ipv6_global $DEVICE)
|
||||||
|
[ -n "$DNAT6_TARGET" ] || {
|
||||||
|
echo could not get any address
|
||||||
|
DNAT6_TARGET=-
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fw_nfqws_pre4()
|
||||||
|
{
|
||||||
|
# $1 - filter ipv4
|
||||||
|
# $2 - queue number
|
||||||
|
|
||||||
|
local DEVICE wan_iface
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || {
|
||||||
|
network_find_wan_all wan_iface
|
||||||
|
for ext_iface in $wan_iface; do
|
||||||
|
network_get_device DEVICE $ext_iface
|
||||||
|
ipt PREROUTING -t mangle -i $DEVICE -p tcp $1 $IPSET_EXCLUDE src -j NFQUEUE --queue-num $2 --queue-bypass
|
||||||
|
done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_nfqws_pre6()
|
||||||
|
{
|
||||||
|
# $1 - filter ipv6
|
||||||
|
# $2 - queue number
|
||||||
|
|
||||||
|
local DEVICE wan_iface
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
network_find_wan6_all wan_iface
|
||||||
|
for ext_iface in $wan_iface; do
|
||||||
|
network_get_device DEVICE $ext_iface
|
||||||
|
ipt6 PREROUTING -t mangle -i $DEVICE -p tcp $1 $IPSET_EXCLUDE6 src -j NFQUEUE --queue-num $2 --queue-bypass
|
||||||
|
done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_nfqws_pre()
|
||||||
|
{
|
||||||
|
# $1 - filter ipv4
|
||||||
|
# $2 - filter ipv6
|
||||||
|
# $3 - queue number
|
||||||
|
|
||||||
|
fw_nfqws_pre4 "$1" $3
|
||||||
|
fw_nfqws_pre6 "$2" $3
|
||||||
|
}
|
||||||
|
fw_nfqws_post4()
|
||||||
|
{
|
||||||
|
# $1 - filter ipv4
|
||||||
|
# $2 - queue number
|
||||||
|
|
||||||
|
local DEVICE wan_iface
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || {
|
||||||
|
network_find_wan_all wan_iface
|
||||||
|
for ext_iface in $wan_iface; do
|
||||||
|
network_get_device DEVICE $ext_iface
|
||||||
|
ipt POSTROUTING -t mangle -o $DEVICE -p tcp $1 $IPSET_EXCLUDE dst -j NFQUEUE --queue-num $2 --queue-bypass
|
||||||
|
done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_nfqws_post6()
|
||||||
|
{
|
||||||
|
# $1 - filter ipv6
|
||||||
|
# $2 - queue number
|
||||||
|
|
||||||
|
local DEVICE wan_iface
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
network_find_wan6_all wan_iface
|
||||||
|
for ext_iface in $wan_iface; do
|
||||||
|
network_get_device DEVICE $ext_iface
|
||||||
|
ipt6 POSTROUTING -t mangle -o $DEVICE -p tcp $1 $IPSET_EXCLUDE6 dst -j NFQUEUE --queue-num $2 --queue-bypass
|
||||||
|
done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_nfqws_post()
|
||||||
|
{
|
||||||
|
# $1 - filter ipv4
|
||||||
|
# $2 - filter ipv6
|
||||||
|
# $3 - queue number
|
||||||
|
|
||||||
|
fw_nfqws_post4 "$1" $3
|
||||||
|
fw_nfqws_post6 "$2" $3
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IPT_OWNER="-m owner ! --uid-owner $TPWS_USER"
|
||||||
|
fw_tpws4()
|
||||||
|
{
|
||||||
|
# $1 - filter ipv6
|
||||||
|
# $2 - tpws port
|
||||||
|
|
||||||
|
local DEVICE wan_iface
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || {
|
||||||
|
network_find_wan_all wan_iface
|
||||||
|
for ext_iface in $wan_iface; do
|
||||||
|
network_get_device DEVICE $ext_iface
|
||||||
|
ipt OUTPUT -t nat -o $DEVICE $IPT_OWNER -p tcp $1 $IPSET_EXCLUDE dst -j DNAT --to 127.0.0.1:$2
|
||||||
|
done
|
||||||
|
ipt prerouting_lan_rule -t nat -p tcp $1 $IPSET_EXCLUDE dst -j DNAT --to 127.0.0.1:$2
|
||||||
|
network_get_device DEVICE lan
|
||||||
|
sysctl -qw net.ipv4.conf.$DEVICE.route_localnet=1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_tpws6()
|
||||||
|
{
|
||||||
|
# $1 - filter ipv6
|
||||||
|
# $2 - tpws port
|
||||||
|
|
||||||
|
local DEVICE wan_iface
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
network_find_wan6_all wan_iface
|
||||||
|
for ext_iface in $wan_iface; do
|
||||||
|
network_get_device DEVICE $ext_iface
|
||||||
|
ipt6 OUTPUT -t nat -o $DEVICE $IPT_OWNER -p tcp $1 $IPSET_EXCLUDE6 dst -j DNAT --to [::1]:$2
|
||||||
|
done
|
||||||
|
network_get_device DEVICE lan
|
||||||
|
dnat6_target
|
||||||
|
[ "$DNAT6_TARGET" != "-" ] && ipt6 PREROUTING -t nat -i $DEVICE -p tcp $1 $IPSET_EXCLUDE6 dst -j DNAT --to [$DNAT6_TARGET]:$2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_tpws()
|
||||||
|
{
|
||||||
|
# $1 - filter ipv4
|
||||||
|
# $2 - filter ipv6
|
||||||
|
# $3 - tpws port
|
||||||
|
|
||||||
|
fw_tpws4 "$1" $3
|
||||||
|
fw_tpws6 "$2" $3
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
create_ipset()
|
||||||
|
{
|
||||||
|
echo "Creating ipset"
|
||||||
|
"$IPSET_CR" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
zapret_apply_firewall()
|
||||||
|
{
|
||||||
|
local rule
|
||||||
|
local synack="--tcp-flags SYN,ACK SYN,ACK"
|
||||||
|
local ipset_zapret="-m set --match-set zapret"
|
||||||
|
local ipset_zapret6="-m set --match-set zapret6"
|
||||||
|
local desync="-m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4 -m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
|
||||||
|
|
||||||
|
# always create ipsets. ip_exclude ipset is required
|
||||||
|
create_ipset no-update
|
||||||
|
|
||||||
|
case "${MODE}" in
|
||||||
|
tpws_hostlist|tpws_all)
|
||||||
|
fw_tpws "--dport 80" "--dport 80" $TPPORT_HTTP
|
||||||
|
;;
|
||||||
|
tpws_ipset)
|
||||||
|
fw_tpws "--dport 80 $ipset_zapret dst" "--dport 80 $ipset_zapret6 dst" $TPPORT_HTTP
|
||||||
|
;;
|
||||||
|
tpws_ipset_https)
|
||||||
|
fw_tpws "--dport 80 $ipset_zapret dst" "--dport 80 $ipset_zapret6 dst" $TPPORT_HTTP
|
||||||
|
fw_tpws "--dport 443 $ipset_zapret dst" "--dport 443 $ipset_zapret6 dst" $TPPORT_HTTPS
|
||||||
|
;;
|
||||||
|
tpws_all_https)
|
||||||
|
fw_tpws "--dport 80" "--dport 80" $TPPORT_HTTP
|
||||||
|
fw_tpws "--dport 443" "--dport 443" $TPPORT_HTTPS
|
||||||
|
;;
|
||||||
|
nfqws_ipset)
|
||||||
|
fw_nfqws_pre "--sport 80 $synack $ipset_zapret src" "--sport 80 $synack $ipset_zapret6 src" $QNUM
|
||||||
|
fw_nfqws_post "--dport 80 $ipset_zapret dst" "--dport 80 $ipset_zapret6 dst" $QNUM
|
||||||
|
;;
|
||||||
|
nfqws_ipset_https)
|
||||||
|
rule="-m multiport --sports 80,443 $synack"
|
||||||
|
fw_nfqws_pre "$rule $ipset_zapret src" "$rule $ipset_zapret6 src" $QNUM
|
||||||
|
fw_nfqws_post "--dport 80 $ipset_zapret dst" "--dport 80 $ipset_zapret6 dst" $QNUM
|
||||||
|
;;
|
||||||
|
nfqws_all)
|
||||||
|
fw_nfqws_pre "--sport 80 $synack" "--sport 80 $synack" $QNUM
|
||||||
|
fw_nfqws_post "--dport 80" "--dport 80" $QNUM
|
||||||
|
;;
|
||||||
|
nfqws_all_https)
|
||||||
|
rule="-m multiport --sports 80,443 $synack"
|
||||||
|
fw_nfqws_pre "$rule" "$rule" $QNUM
|
||||||
|
fw_nfqws_post "--dport 80" "--dport 80" $QNUM
|
||||||
|
;;
|
||||||
|
nfqws_all_desync|nfqws_hostlist_desync)
|
||||||
|
rule="-m multiport --sports 80,443 $synack"
|
||||||
|
fw_nfqws_pre "$rule" "$rule" $QNUM
|
||||||
|
fw_nfqws_post "$desync" "$desync" $QNUM
|
||||||
|
;;
|
||||||
|
nfqws_ipset_desync)
|
||||||
|
rule="-m multiport --sports 80,443 $synack"
|
||||||
|
fw_nfqws_pre "$rule $ipset_zapret src" "$rule $ipset_zapret6 src" $QNUM
|
||||||
|
fw_nfqws_post "$desync $ipset_zapret dst" "$desync $ipset_zapret6 dst" $QNUM
|
||||||
|
;;
|
||||||
|
custom)
|
||||||
|
existf zapret_custom_firewall && zapret_custom_firewall
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
90
init.d/openwrt/zapret
Executable file
90
init.d/openwrt/zapret
Executable file
@ -0,0 +1,90 @@
|
|||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
|
||||||
|
USE_PROCD=1
|
||||||
|
# after network
|
||||||
|
START=21
|
||||||
|
|
||||||
|
ZAPRET_BASE=/opt/zapret
|
||||||
|
. "$ZAPRET_BASE/init.d/openwrt/functions"
|
||||||
|
|
||||||
|
# !!!!! in openwrt firewall rules are configured separately
|
||||||
|
|
||||||
|
PIDDIR=/var/run
|
||||||
|
|
||||||
|
QNUM=200
|
||||||
|
NFQWS_USER=daemon
|
||||||
|
NFQWS="$ZAPRET_BASE/nfq/nfqws"
|
||||||
|
NFQWS_OPT_BASE="--qnum=$QNUM --user=$NFQWS_USER"
|
||||||
|
|
||||||
|
TPWS_USER=daemon
|
||||||
|
TPPORT_HTTP=1188
|
||||||
|
TPPORT_HTTPS=1189
|
||||||
|
TPWS="$ZAPRET_BASE/tpws/tpws"
|
||||||
|
HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts.txt.gz"
|
||||||
|
[ -f "$HOSTLIST" ] || HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts-user.txt"
|
||||||
|
TPWS_OPT_BASE="--user=$TPWS_USER --bind-addr=127.0.0.1"
|
||||||
|
TPWS_OPT_BASE6="--user=$TPWS_USER --bind-addr=::1"
|
||||||
|
# first wait for lan to ifup, then wait for bind-wait-ip-linklocal seconds for link local address and bind-wait-ip for any ipv6 as the worst case
|
||||||
|
TPWS_OPT_BASE6_PRE="--user=$TPWS_USER --bind-linklocal=prefer --bind-wait-ifup=30 --bind-wait-ip=30 --bind-wait-ip-linklocal=3"
|
||||||
|
TPWS_OPT_BASE_HTTP="--port=$TPPORT_HTTP"
|
||||||
|
TPWS_OPT_BASE_HTTPS="--port=$TPPORT_HTTPS"
|
||||||
|
|
||||||
|
run_daemon()
|
||||||
|
{
|
||||||
|
# $1 - daemon string id or number. can use 1,2,3,...
|
||||||
|
# $2 - daemon
|
||||||
|
# $3 - daemon args
|
||||||
|
# use $PIDDIR/$DAEMONBASE$1.pid as pidfile
|
||||||
|
local DAEMONBASE=$(basename $2)
|
||||||
|
echo "Starting daemon $1: $2 $3"
|
||||||
|
procd_open_instance
|
||||||
|
procd_set_param command $2 $3
|
||||||
|
procd_set_param pidfile $PIDDIR/$DAEMONBASE$1.pid
|
||||||
|
procd_close_instance
|
||||||
|
}
|
||||||
|
|
||||||
|
run_tpws()
|
||||||
|
{
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || run_daemon $1 $TPWS "$TPWS_OPT_BASE $2"
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
run_daemon $((60+$1)) $TPWS "$TPWS_OPT_BASE6 $2"
|
||||||
|
network_get_device DEVICE lan
|
||||||
|
[ -n "$DEVICE" ] && run_daemon $((660+$1)) $TPWS "$TPWS_OPT_BASE6_PRE --bind-iface6=$DEVICE $2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop_tpws()
|
||||||
|
{
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || stop_daemon $1 $TPWS
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
stop_daemon $((60+$1)) $TPWS
|
||||||
|
stop_daemon $((660+$1)) $TPWS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
case "${MODE}" in
|
||||||
|
tpws_hostlist)
|
||||||
|
run_tpws 1 "$TPWS_OPT_BASE_HTTP $TPWS_OPT_HTTP --hostlist=$HOSTLIST"
|
||||||
|
;;
|
||||||
|
tpws_ipset|tpws_all)
|
||||||
|
run_tpws 1 "$TPWS_OPT_BASE_HTTP $TPWS_OPT_HTTP"
|
||||||
|
;;
|
||||||
|
tpws_ipset_https|tpws_all_https)
|
||||||
|
run_tpws 1 "$TPWS_OPT_BASE_HTTP $TPWS_OPT_HTTP"
|
||||||
|
run_tpws 2 "$TPWS_OPT_BASE_HTTPS $TPWS_OPT_HTTPS"
|
||||||
|
;;
|
||||||
|
nfqws_ipset|nfqws_ipset_https|nfqws_all|nfqws_all_https)
|
||||||
|
run_daemon 1 $NFQWS "$NFQWS_OPT_BASE $NFQWS_OPT"
|
||||||
|
;;
|
||||||
|
nfqws_ipset_desync|nfqws_all_desync)
|
||||||
|
run_daemon 1 $NFQWS "$NFQWS_OPT_BASE $NFQWS_OPT_DESYNC"
|
||||||
|
;;
|
||||||
|
nfqws_hostlist_desync)
|
||||||
|
run_daemon 1 $NFQWS "$NFQWS_OPT_BASE $NFQWS_OPT_DESYNC --hostlist=$HOSTLIST"
|
||||||
|
;;
|
||||||
|
custom)
|
||||||
|
existf zapret_custom_daemons && zapret_custom_daemons $1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
13
init.d/systemd/zapret-list-update.service
Normal file
13
init.d/systemd/zapret-list-update.service
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=zapret ip/host list update
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=no
|
||||||
|
IgnoreSIGPIPE=no
|
||||||
|
KillMode=control-group
|
||||||
|
GuessMainPID=no
|
||||||
|
RemainAfterExit=no
|
||||||
|
ExecStart=/opt/zapret/ipset/get_config.sh
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
11
init.d/systemd/zapret-list-update.timer
Normal file
11
init.d/systemd/zapret-list-update.timer
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=zapret ip/host list update timer
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=*-*-2,4,6,8,10,12,14,16,18,20,22,24,26,28,30 00:00:00
|
||||||
|
RandomizedDelaySec=86400
|
||||||
|
Persistent=true
|
||||||
|
Unit=zapret-list-update.service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
17
init.d/systemd/zapret.service
Normal file
17
init.d/systemd/zapret.service
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[Unit]
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=forking
|
||||||
|
Restart=no
|
||||||
|
TimeoutSec=30sec
|
||||||
|
IgnoreSIGPIPE=no
|
||||||
|
KillMode=none
|
||||||
|
GuessMainPID=no
|
||||||
|
RemainAfterExit=no
|
||||||
|
ExecStart=/opt/zapret/init.d/sysv/zapret start
|
||||||
|
ExecStop=/opt/zapret/init.d/sysv/zapret stop
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
24
init.d/sysv/custom
Normal file
24
init.d/sysv/custom
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# this script contain your special code to launch daemons and configure firewall
|
||||||
|
# use helpers from "functions" file
|
||||||
|
# in case of upgrade keep this file only, do not modify others
|
||||||
|
|
||||||
|
zapret_custom_daemons()
|
||||||
|
{
|
||||||
|
# $1 - 1 - run, 0 - stop
|
||||||
|
|
||||||
|
# PLACEHOLDER
|
||||||
|
echo !!! NEED ATTENTION !!!
|
||||||
|
echo Start daemon\(s\)
|
||||||
|
echo Study how other sections work
|
||||||
|
|
||||||
|
do_daemon $1 1 /bin/sleep 20
|
||||||
|
}
|
||||||
|
zapret_custom_firewall()
|
||||||
|
{
|
||||||
|
# $1 - 1 - run, 0 - stop
|
||||||
|
|
||||||
|
# PLACEHOLDER
|
||||||
|
echo !!! NEED ATTENTION !!!
|
||||||
|
echo Configure iptables for required actions
|
||||||
|
echo Study how other sections work
|
||||||
|
}
|
486
init.d/sysv/functions
Normal file
486
init.d/sysv/functions
Normal file
@ -0,0 +1,486 @@
|
|||||||
|
# init script functions library for desktop linux systems
|
||||||
|
|
||||||
|
[ -n "$ZAPRET_BASE" ] || ZAPRET_BASE=/opt/zapret
|
||||||
|
# SHOULD EDIT config
|
||||||
|
. "$ZAPRET_BASE/config"
|
||||||
|
|
||||||
|
PIDDIR=/var/run
|
||||||
|
|
||||||
|
IPSET_CR="$ZAPRET_BASE/ipset/create_ipset.sh"
|
||||||
|
|
||||||
|
WS_USER=tpws
|
||||||
|
|
||||||
|
QNUM=200
|
||||||
|
NFQWS="$ZAPRET_BASE/nfq/nfqws"
|
||||||
|
NFQWS_OPT_BASE="--qnum=$QNUM --user=$WS_USER"
|
||||||
|
|
||||||
|
TPPORT_HTTP=1188
|
||||||
|
TPPORT_HTTPS=1189
|
||||||
|
TPWS="$ZAPRET_BASE/tpws/tpws"
|
||||||
|
HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts.txt.gz"
|
||||||
|
[ -f "$HOSTLIST" ] || HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts-user.txt"
|
||||||
|
TPWS_OPT_BASE="--user=$WS_USER --bind-addr=127.0.0.1"
|
||||||
|
TPWS_OPT_BASE6="--user=$WS_USER --bind-addr=::1"
|
||||||
|
# first wait for lan to ifup, then wait for bind-wait-ip-linklocal seconds for link local address and bind-wait-ip for any ipv6 as the worst case
|
||||||
|
TPWS_OPT_BASE6_PRE="--user=$WS_USER --bind-linklocal=prefer --bind-wait-ifup=30 --bind-wait-ip=30 --bind-wait-ip-linklocal=3"
|
||||||
|
TPWS_OPT_BASE_HTTP="--port=$TPPORT_HTTP"
|
||||||
|
TPWS_OPT_BASE_HTTPS="--port=$TPPORT_HTTPS"
|
||||||
|
|
||||||
|
[ -n "$IFACE_WAN" ] && IPT_OWAN="-o $IFACE_WAN"
|
||||||
|
[ -n "$IFACE_WAN" ] && IPT_IWAN="-i $IFACE_WAN"
|
||||||
|
[ -n "$IFACE_LAN" ] && IPT_ILAN="-i $IFACE_LAN"
|
||||||
|
|
||||||
|
[ -n "$DESYNC_MARK" ] || DESYNC_MARK=0x40000000
|
||||||
|
|
||||||
|
# max wait time for the link local ipv6 on the LAN interface
|
||||||
|
LINKLOCAL_WAIT_SEC=5
|
||||||
|
|
||||||
|
CUSTOM_SCRIPT="$ZAPRET_BASE/init.d/sysv/custom"
|
||||||
|
[ -f "$CUSTOM_SCRIPT" ] && . "$CUSTOM_SCRIPT"
|
||||||
|
|
||||||
|
IPSET_EXCLUDE="-m set ! --match-set nozapret"
|
||||||
|
IPSET_EXCLUDE6="-m set ! --match-set nozapret6"
|
||||||
|
|
||||||
|
exists()
|
||||||
|
{
|
||||||
|
which "$1" >/dev/null 2>/dev/null
|
||||||
|
}
|
||||||
|
existf()
|
||||||
|
{
|
||||||
|
type "$1" >/dev/null 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
on_off_function()
|
||||||
|
{
|
||||||
|
# $1 : function name on
|
||||||
|
# $2 : function name off
|
||||||
|
# $3 : 0 - off, 1 - on
|
||||||
|
local F="$1"
|
||||||
|
[ "$3" = "1" ] || F="$2"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
"$F" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ipt()
|
||||||
|
{
|
||||||
|
iptables -C "$@" 2>/dev/null || iptables -I "$@"
|
||||||
|
}
|
||||||
|
ipt_del()
|
||||||
|
{
|
||||||
|
iptables -C "$@" 2>/dev/null && iptables -D "$@"
|
||||||
|
}
|
||||||
|
ipt_add_del()
|
||||||
|
{
|
||||||
|
on_off_function ipt ipt_del "$@"
|
||||||
|
}
|
||||||
|
ipt6()
|
||||||
|
{
|
||||||
|
ip6tables -C "$@" 2>/dev/null || ip6tables -I "$@"
|
||||||
|
}
|
||||||
|
ipt6_del()
|
||||||
|
{
|
||||||
|
ip6tables -C "$@" 2>/dev/null && ip6tables -D "$@"
|
||||||
|
}
|
||||||
|
ipt6_add_del()
|
||||||
|
{
|
||||||
|
on_off_function ipt6 ipt6_del "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# there's no route_localnet for ipv6
|
||||||
|
# the best we can is to route to link local of the incoming interface
|
||||||
|
# OUTPUT - can DNAT to ::1
|
||||||
|
# PREROUTING - can't DNAT to ::1. can DNAT to link local of -i interface or to any global addr
|
||||||
|
# not a good idea to expose tpws to the world (bind to ::)
|
||||||
|
|
||||||
|
get_ipv6_linklocal()
|
||||||
|
{
|
||||||
|
# $1 - interface name. if empty - any interface
|
||||||
|
local dev
|
||||||
|
[ -n "$1" ] && dev="dev $1"
|
||||||
|
ip addr show $dev | sed -e 's/^.*inet6 \([^ ]*\)\/[0-9]* scope link.*$/\1/;t;d' | head -n 1
|
||||||
|
}
|
||||||
|
get_ipv6_global()
|
||||||
|
{
|
||||||
|
# $1 - interface name. if empty - any interface
|
||||||
|
local dev
|
||||||
|
[ -n "$1" ] && dev="dev $1"
|
||||||
|
ip addr show $dev | sed -e 's/^.*inet6 \([^ ]*\)\/[0-9]* scope global.*$/\1/;t;d' | head -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
iface_is_up()
|
||||||
|
{
|
||||||
|
# $1 - interface name
|
||||||
|
[ -f /sys/class/net/$1/operstate ] || return
|
||||||
|
local state
|
||||||
|
read state </sys/class/net/$1/operstate
|
||||||
|
[ "$state" != "down" ]
|
||||||
|
}
|
||||||
|
wait_ifup()
|
||||||
|
{
|
||||||
|
# $1 - interface name
|
||||||
|
local ct=0
|
||||||
|
while
|
||||||
|
iface_is_up $1 && return
|
||||||
|
[ "$ct" -ge "$IFUP_WAIT_SEC" ] && break
|
||||||
|
echo waiting for ifup of $1 for another $(($IFUP_WAIT_SEC - $ct)) seconds ...
|
||||||
|
ct=$(($ct+1))
|
||||||
|
sleep 1
|
||||||
|
do :; done
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dnat6_target()
|
||||||
|
{
|
||||||
|
# get target ip address for DNAT. prefer link locals
|
||||||
|
# tpws should be as inaccessible from outside as possible
|
||||||
|
# link local address can appear not immediately after ifup
|
||||||
|
|
||||||
|
# DNAT6_TARGET=- means attempt was made but address was not found (to avoid multiple re-attempts)
|
||||||
|
[ -n "$DNAT6_TARGET" ] || {
|
||||||
|
local ct=0
|
||||||
|
while
|
||||||
|
DNAT6_TARGET=$(get_ipv6_linklocal $IFACE_LAN)
|
||||||
|
[ -n "$DNAT6_TARGET" ] && break
|
||||||
|
[ "$ct" -ge "$LINKLOCAL_WAIT_SEC" ] && break
|
||||||
|
echo waiting for the link local for another $(($LINKLOCAL_WAIT_SEC - $ct)) seconds ...
|
||||||
|
ct=$(($ct+1))
|
||||||
|
sleep 1
|
||||||
|
do :; done
|
||||||
|
|
||||||
|
[ -n "$DNAT6_TARGET" ] || {
|
||||||
|
echo no link local. getting global
|
||||||
|
DNAT6_TARGET=$(get_ipv6_global $IFACE_LAN)
|
||||||
|
[ -n "$DNAT6_TARGET" ] || {
|
||||||
|
echo could not get any address
|
||||||
|
DNAT6_TARGET=-
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print_op()
|
||||||
|
{
|
||||||
|
if [ "$1" = "1" ]; then
|
||||||
|
echo "Adding ip$4tables rule for $3 : $2"
|
||||||
|
else
|
||||||
|
echo "Deleting ip$4tables rule for $3 : $2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
fw_tpws4()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
# $2 - iptable filter for ipv4
|
||||||
|
# $3 - tpws port
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || {
|
||||||
|
print_op $1 "$2" "tpws"
|
||||||
|
[ -n "$IFACE_LAN" ] && {
|
||||||
|
ipt_add_del $1 PREROUTING -t nat $IPT_ILAN -p tcp $2 $IPSET_EXCLUDE dst -j DNAT --to 127.0.0.1:$3
|
||||||
|
}
|
||||||
|
ipt_add_del $1 OUTPUT -t nat $IPT_OWAN -m owner ! --uid-owner $WS_USER -p tcp $2 $IPSET_EXCLUDE dst -j DNAT --to 127.0.0.1:$3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_tpws6()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
# $2 - iptable filter for ipv6
|
||||||
|
# $3 - tpws port
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
print_op $1 "$2" "tpws" 6
|
||||||
|
[ -n "$IFACE_LAN" ] && {
|
||||||
|
dnat6_target
|
||||||
|
[ "$DNAT6_TARGET" != "-" ] && ipt6_add_del $1 PREROUTING -t nat $IPT_ILAN -p tcp $2 $IPSET_EXCLUDE6 dst -j DNAT --to [$DNAT6_TARGET]:$3
|
||||||
|
}
|
||||||
|
ipt6_add_del $1 OUTPUT -t nat $IPT_OWAN -m owner ! --uid-owner $WS_USER -p tcp $2 $IPSET_EXCLUDE6 dst -j DNAT --to [::1]:$3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_tpws()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
# $2 - iptable filter for ipv4
|
||||||
|
# $3 - iptable filter for ipv6
|
||||||
|
# $4 - tpws port
|
||||||
|
fw_tpws4 $1 "$2" $4
|
||||||
|
fw_tpws6 $1 "$3" $4
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fw_nfqws_pre4()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
# $2 - iptable filter for ipv4
|
||||||
|
# $3 - queue number
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || {
|
||||||
|
print_op $1 "$2" "nfqws prerouting"
|
||||||
|
ipt_add_del $1 PREROUTING -t mangle $IPT_IWAN -p tcp $2 $IPSET_EXCLUDE src -j NFQUEUE --queue-num $3 --queue-bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_nfqws_pre6()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
# $2 - iptable filter for ipv6
|
||||||
|
# $3 - queue number
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
print_op $1 "$2" "nfqws prerouting" 6
|
||||||
|
ipt6_add_del $1 PREROUTING -t mangle $IPT_IWAN -p tcp $2 $IPSET_EXCLUDE6 src -j NFQUEUE --queue-num $3 --queue-bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_nfqws_pre()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
# $2 - iptable filter for ipv4
|
||||||
|
# $3 - iptable filter for ipv6
|
||||||
|
# $4 - queue number
|
||||||
|
fw_nfqws_pre4 $1 "$2" $4
|
||||||
|
fw_nfqws_pre6 $1 "$3" $4
|
||||||
|
}
|
||||||
|
fw_nfqws_post4()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
# $2 - iptable filter for ipv4
|
||||||
|
# $3 - queue number
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || {
|
||||||
|
print_op $1 "$2" "nfqws postrouting"
|
||||||
|
ipt_add_del $1 POSTROUTING -t mangle $IPT_OWAN -p tcp $2 $IPSET_EXCLUDE dst -j NFQUEUE --queue-num $3 --queue-bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_nfqws_post6()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
# $2 - iptable filter for ipv6
|
||||||
|
# $3 - queue number
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
print_op $1 "$2" "nfqws postrouting" 6
|
||||||
|
ipt6_add_del $1 POSTROUTING -t mangle $IPT_OWAN -p tcp $2 $IPSET_EXCLUDE6 dst -j NFQUEUE --queue-num $3 --queue-bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fw_nfqws_post()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
# $2 - iptable filter for ipv4
|
||||||
|
# $3 - iptable filter for ipv6
|
||||||
|
# $4 - queue number
|
||||||
|
fw_nfqws_post4 $1 "$2" $4
|
||||||
|
fw_nfqws_post6 $1 "$3" $4
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
run_daemon()
|
||||||
|
{
|
||||||
|
# $1 - daemon number : 1,2,3,...
|
||||||
|
# $2 - daemon
|
||||||
|
# $3 - daemon args
|
||||||
|
# use $PIDDIR/$DAEMONBASE$1.pid as pidfile
|
||||||
|
|
||||||
|
local DAEMONBASE=$(basename $2)
|
||||||
|
local PIDFILE=$PIDDIR/$DAEMONBASE$1.pid
|
||||||
|
echo "Starting daemon $1: $2 $3"
|
||||||
|
if exists start-stop-daemon ; then
|
||||||
|
start-stop-daemon --start --pidfile "$PIDFILE" --background --make-pidfile --exec "$2" -- $3
|
||||||
|
else
|
||||||
|
if [ -f "$PIDFILE" ] && pgrep -F "$PIDFILE" "$DAEMONBASE" >/dev/null; then
|
||||||
|
echo already running
|
||||||
|
else
|
||||||
|
"$2" $3 >/dev/null 2>/dev/null &
|
||||||
|
PID=$!
|
||||||
|
if [ -n "$PID" ]; then
|
||||||
|
echo $PID >$PIDFILE
|
||||||
|
else
|
||||||
|
echo could not start daemon $1 : $2 $3
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
stop_daemon()
|
||||||
|
{
|
||||||
|
# $1 - daemon number : 1,2,3,...
|
||||||
|
# $2 - daemon
|
||||||
|
# use $PIDDIR/$DAEMONBASE$1.pid as pidfile
|
||||||
|
local DAEMONBASE=$(basename $2)
|
||||||
|
local PIDFILE=$PIDDIR/$DAEMONBASE$1.pid
|
||||||
|
echo "Stopping daemon $1: $2"
|
||||||
|
if exists start-stop-daemon ; then
|
||||||
|
start-stop-daemon --stop --pidfile "$PIDFILE" --exec "$2"
|
||||||
|
else
|
||||||
|
if [ -f "$PIDFILE" ]; then
|
||||||
|
read PID <"$PIDFILE"
|
||||||
|
kill $PID
|
||||||
|
rm -f "$PIDFILE"
|
||||||
|
else
|
||||||
|
echo no pidfile : $PIDFILE
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
do_daemon()
|
||||||
|
{
|
||||||
|
# $1 - 1 - run, 0 - stop
|
||||||
|
on_off_function run_daemon stop_daemon "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
prepare_user()
|
||||||
|
{
|
||||||
|
# $WS_USER is required to prevent redirection of the traffic originating from TPWS itself
|
||||||
|
# otherwise infinite loop will occur
|
||||||
|
# also its good idea not to run tpws as root
|
||||||
|
id -u $WS_USER >/dev/null 2>/dev/null || useradd --no-create-home --system --shell /bin/false $WS_USER
|
||||||
|
}
|
||||||
|
prepare_tpws()
|
||||||
|
{
|
||||||
|
prepare_user
|
||||||
|
|
||||||
|
# otherwise linux kernel will treat 127.0.0.1 as "martian" ip and refuse routing to it
|
||||||
|
# NOTE : kernels <3.6 do not have this feature. consider upgrading or change DNAT to REDIRECT and do not bind to 127.0.0.1
|
||||||
|
[ -n "$IFACE_LAN" ] && sysctl -qw net.ipv4.conf.$IFACE_LAN.route_localnet=1
|
||||||
|
}
|
||||||
|
prepare_nfqws()
|
||||||
|
{
|
||||||
|
prepare_user
|
||||||
|
}
|
||||||
|
do_tpws()
|
||||||
|
{
|
||||||
|
# $1 : 1 - run, 0 - stop
|
||||||
|
# $2 : daemon number
|
||||||
|
# $3 : daemon args
|
||||||
|
|
||||||
|
[ "$1" = "1" ] && prepare_tpws
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || do_daemon $1 $2 $TPWS "$TPWS_OPT_BASE $3"
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
do_daemon $1 $((60+$2)) $TPWS "$TPWS_OPT_BASE6 $3"
|
||||||
|
[ -n "$IFACE_LAN" ] && do_daemon $1 $((660+$2)) $TPWS "$TPWS_OPT_BASE6_PRE --bind-iface6=$IFACE_LAN $3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do_nfqws()
|
||||||
|
{
|
||||||
|
# $1 : 1 - run, 0 - stop
|
||||||
|
# $2 : daemon number
|
||||||
|
# $3 : daemon args
|
||||||
|
|
||||||
|
[ "$1" = "1" ] && prepare_nfqws
|
||||||
|
do_daemon $1 $2 $NFQWS "$NFQWS_OPT_BASE $3"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
create_ipset()
|
||||||
|
{
|
||||||
|
echo "Creating ipset"
|
||||||
|
"$IPSET_CR" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
zapret_do_firewall()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
local rule
|
||||||
|
local synack="--tcp-flags SYN,ACK SYN,ACK"
|
||||||
|
local ipset_zapret="-m set --match-set zapret"
|
||||||
|
local ipset_zapret6="-m set --match-set zapret6"
|
||||||
|
local desync="-m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4 -m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
|
||||||
|
|
||||||
|
# always create ipsets. ip_exclude ipset is required
|
||||||
|
[ "$1" != "1" ] || create_ipset no-update
|
||||||
|
|
||||||
|
case "${MODE}" in
|
||||||
|
tpws_hostlist|tpws_all)
|
||||||
|
fw_tpws $1 "--dport 80" "--dport 80" $TPPORT_HTTP
|
||||||
|
;;
|
||||||
|
tpws_ipset)
|
||||||
|
[ "$1" = "1" ] && prepare_tpws
|
||||||
|
fw_tpws $1 "--dport 80 $ipset_zapret dst" "--dport 80 $ipset_zapret6 dst" $TPPORT_HTTP
|
||||||
|
;;
|
||||||
|
tpws_ipset_https)
|
||||||
|
[ "$1" = "1" ] && prepare_tpws
|
||||||
|
fw_tpws $1 "--dport 80 $ipset_zapret dst" "--dport 80 $ipset_zapret6 dst" $TPPORT_HTTP
|
||||||
|
fw_tpws $1 "--dport 443 $ipset_zapret dst" "--dport 443 $ipset_zapret6 dst" $TPPORT_HTTPS
|
||||||
|
;;
|
||||||
|
tpws_all_https)
|
||||||
|
[ "$1" = "1" ] && prepare_tpws
|
||||||
|
fw_tpws $1 "--dport 80" "--dport 80" $TPPORT_HTTP
|
||||||
|
fw_tpws $1 "--dport 443" "--dport 443" $TPPORT_HTTPS
|
||||||
|
;;
|
||||||
|
nfqws_ipset)
|
||||||
|
fw_nfqws_pre $1 "--sport 80 $synack $ipset_zapret src" "--sport 80 $synack $ipset_zapret6 src" $QNUM
|
||||||
|
fw_nfqws_post $1 "--dport 80 $ipset_zapret dst" "--dport 80 $ipset_zapret6 dst" $QNUM
|
||||||
|
;;
|
||||||
|
nfqws_ipset_https)
|
||||||
|
rule="-m multiport --sports 80,443 $synack"
|
||||||
|
fw_nfqws_pre $1 "$rule $ipset_zapret src" "$rule $ipset_zapret6 src" $QNUM
|
||||||
|
fw_nfqws_post $1 "--dport 80 $ipset_zapret dst" "--dport 80 $ipset_zapret6 dst" $QNUM
|
||||||
|
;;
|
||||||
|
nfqws_all)
|
||||||
|
fw_nfqws_pre $1 "--sport 80 $synack" "--sport 80 $synack" $QNUM
|
||||||
|
fw_nfqws_post $1 "--dport 80" "--dport 80" $QNUM
|
||||||
|
;;
|
||||||
|
nfqws_all_https)
|
||||||
|
rule="-m multiport --sports 80,443 $synack"
|
||||||
|
fw_nfqws_pre $1 "$rule" "$rule" $QNUM
|
||||||
|
fw_nfqws_post $1 "--dport 80" "--dport 80" $QNUM
|
||||||
|
;;
|
||||||
|
nfqws_all_desync|nfqws_hostlist_desync)
|
||||||
|
rule="-m multiport --sports 80,443 $synack"
|
||||||
|
fw_nfqws_pre $1 "$rule" "$rule" $QNUM
|
||||||
|
fw_nfqws_post $1 "$desync" "$desync" $QNUM
|
||||||
|
;;
|
||||||
|
nfqws_ipset_desync)
|
||||||
|
rule="-m multiport --sports 80,443 $synack"
|
||||||
|
fw_nfqws_pre $1 "$rule $ipset_zapret src" "$rule $ipset_zapret6 src" $QNUM
|
||||||
|
fw_nfqws_post $1 "$desync $ipset_zapret dst" "$desync $ipset_zapret6 dst" $QNUM
|
||||||
|
;;
|
||||||
|
custom)
|
||||||
|
existf zapret_custom_firewall && zapret_custom_firewall $1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
zapret_apply_firewall()
|
||||||
|
{
|
||||||
|
zapret_do_firewall 1 "$@"
|
||||||
|
}
|
||||||
|
zapret_unapply_firewall()
|
||||||
|
{
|
||||||
|
zapret_do_firewall 0 "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
zapret_do_daemons()
|
||||||
|
{
|
||||||
|
# $1 - 1 - run, 0 - stop
|
||||||
|
|
||||||
|
case "${MODE}" in
|
||||||
|
tpws_hostlist)
|
||||||
|
do_tpws $1 1 "$TPWS_OPT_BASE_HTTP $TPWS_OPT_HTTP --hostlist=$HOSTLIST"
|
||||||
|
;;
|
||||||
|
tpws_ipset|tpws_all)
|
||||||
|
do_tpws $1 1 "$TPWS_OPT_BASE_HTTP $TPWS_OPT_HTTP"
|
||||||
|
;;
|
||||||
|
tpws_ipset_https|tpws_all_https)
|
||||||
|
do_tpws $1 1 "$TPWS_OPT_BASE_HTTP $TPWS_OPT_HTTP"
|
||||||
|
do_tpws $1 2 "$TPWS_OPT_BASE_HTTPS $TPWS_OPT_HTTPS"
|
||||||
|
;;
|
||||||
|
nfqws_ipset|nfqws_ipset_https|nfqws_all|nfqws_all_https)
|
||||||
|
do_nfqws $1 1 "$NFQWS_OPT"
|
||||||
|
;;
|
||||||
|
nfqws_ipset_desync|nfqws_all_desync)
|
||||||
|
do_nfqws $1 1 "$NFQWS_OPT_DESYNC"
|
||||||
|
;;
|
||||||
|
nfqws_hostlist_desync)
|
||||||
|
do_nfqws $1 1 "$NFQWS_OPT_DESYNC --hostlist=$HOSTLIST"
|
||||||
|
;;
|
||||||
|
custom)
|
||||||
|
existf zapret_custom_daemons && zapret_custom_daemons $1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
zapret_run_daemons()
|
||||||
|
{
|
||||||
|
zapret_do_daemons 1 "$@"
|
||||||
|
}
|
||||||
|
zapret_stop_daemons()
|
||||||
|
{
|
||||||
|
zapret_do_daemons 0 "$@"
|
||||||
|
}
|
48
init.d/sysv/zapret
Executable file
48
init.d/sysv/zapret
Executable file
@ -0,0 +1,48 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: zapret
|
||||||
|
# Required-Start: $local_fs $network
|
||||||
|
# Required-Stop: $local_fs $network
|
||||||
|
# Default-Start: 2 3 4 5
|
||||||
|
# Default-Stop: 0 1 6
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
ZAPRET_BASE=/opt/zapret
|
||||||
|
. "$ZAPRET_BASE/init.d/sysv/functions"
|
||||||
|
|
||||||
|
NAME=zapret
|
||||||
|
DESC=anti-zapret
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
zapret_run_daemons
|
||||||
|
[ "$INIT_APPLY_FW" != "1" ] || zapret_apply_firewall
|
||||||
|
;;
|
||||||
|
|
||||||
|
stop)
|
||||||
|
zapret_stop_daemons
|
||||||
|
[ "$INIT_APPLY_FW" != "1" ] || zapret_unapply_firewall
|
||||||
|
;;
|
||||||
|
|
||||||
|
start-fw)
|
||||||
|
zapret_apply_firewall
|
||||||
|
;;
|
||||||
|
stop-fw)
|
||||||
|
zapret_unapply_firewall
|
||||||
|
;;
|
||||||
|
|
||||||
|
start-daemons)
|
||||||
|
zapret_run_daemons
|
||||||
|
;;
|
||||||
|
stop-daemons)
|
||||||
|
zapret_stop_daemons
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
N=/etc/init.d/$NAME
|
||||||
|
echo "Usage: $N {start|stop|start-fw|stop-fw|start-daemons|stop-daemons}" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
64
install_bin.sh
Executable file
64
install_bin.sh
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
BINS=binaries
|
||||||
|
BINDIR=$EXEDIR/$BINS
|
||||||
|
|
||||||
|
check_dir()
|
||||||
|
{
|
||||||
|
local exe=$BINDIR/$1/ip2net
|
||||||
|
if [ -f "$exe" ]; then
|
||||||
|
if [ -x "$exe" ]; then
|
||||||
|
echo 0.0.0.0 | "$exe" 1>/dev/null 2>/dev/null
|
||||||
|
else
|
||||||
|
echo "$exe is not executable. set proper chmod."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "$exe is absent"
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# link or copy executables. uncomment either ln or cp, comment other
|
||||||
|
ccp()
|
||||||
|
{
|
||||||
|
local F=$(basename $1)
|
||||||
|
[ -d "$EXEDIR/$2" ] || mkdir "$EXEDIR/$2"
|
||||||
|
[ -f "$EXEDIR/$2/$F" ] && rm -f "$EXEDIR/$2/$F"
|
||||||
|
ln -fs "../$BINS/$1" "$EXEDIR/$2" && echo linking : "../$BINS/$1" =\> "$EXEDIR/$2"
|
||||||
|
#cp -f "$BINDIR/$1" "$EXEDIR/$2" && echo copying : "$BINDIR/$1" =\> "$EXEDIR/$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
ARCHLIST="my x86_64 x86 aarch64 armhf mips64r2-msb mips32r1-lsb mips32r1-msb ppc"
|
||||||
|
|
||||||
|
if [ "$1" = "getarch" ]; then
|
||||||
|
for arch in $ARCHLIST
|
||||||
|
do
|
||||||
|
[ -d "$BINDIR/$arch" ] || continue
|
||||||
|
if check_dir $arch; then
|
||||||
|
echo $arch
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
for arch in $ARCHLIST
|
||||||
|
do
|
||||||
|
[ -d "$BINDIR/$arch" ] || continue
|
||||||
|
if check_dir $arch; then
|
||||||
|
echo $arch is OK
|
||||||
|
echo installing binaries ...
|
||||||
|
ccp $arch/ip2net ip2net
|
||||||
|
ccp $arch/mdig mdig
|
||||||
|
ccp $arch/nfqws nfq
|
||||||
|
ccp $arch/tpws tpws
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo $arch is NOT OK
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 1
|
815
install_easy.sh
Executable file
815
install_easy.sh
Executable file
@ -0,0 +1,815 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# automated script for easy installing zapret
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
ZAPRET_BASE=/opt/zapret
|
||||||
|
ZAPRET_CONFIG=$EXEDIR/config
|
||||||
|
|
||||||
|
. "$ZAPRET_CONFIG"
|
||||||
|
|
||||||
|
GET_LIST="$EXEDIR/ipset/get_config.sh"
|
||||||
|
GET_LIST_PREFIX=/ipset/get_
|
||||||
|
INIT_SCRIPT=/etc/init.d/zapret
|
||||||
|
|
||||||
|
SYSTEMD_SYSTEM_DIR=/lib/systemd/system
|
||||||
|
[ -d "$SYSTEMD_SYSTEM_DIR" ] || SYSTEMD_SYSTEM_DIR=/usr/lib/systemd/system
|
||||||
|
|
||||||
|
exists()
|
||||||
|
{
|
||||||
|
which $1 >/dev/null 2>/dev/null
|
||||||
|
}
|
||||||
|
whichq()
|
||||||
|
{
|
||||||
|
which $1 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
exitp()
|
||||||
|
{
|
||||||
|
local A
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo press enter to continue
|
||||||
|
read A
|
||||||
|
exit $1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ $(id -u) -ne "0" ] && {
|
||||||
|
echo root is required
|
||||||
|
exists sudo && exec sudo "$0"
|
||||||
|
exists su && exec su -c "$0"
|
||||||
|
echo su or sudo not found
|
||||||
|
exitp 2
|
||||||
|
}
|
||||||
|
|
||||||
|
read_yes_no()
|
||||||
|
{
|
||||||
|
# $1 - default (Y/N)
|
||||||
|
local A
|
||||||
|
read A
|
||||||
|
[ -z "$A" ] || ([ "$A" != "Y" ] && [ "$A" != "y" ] && [ "$A" != "N" ] && [ "$A" != "n" ]) && A=$1
|
||||||
|
[ "$A" = "Y" ] || [ "$A" = "y" ]
|
||||||
|
}
|
||||||
|
ask_yes_no()
|
||||||
|
{
|
||||||
|
# $1 - default (Y/N)
|
||||||
|
# $2 - text
|
||||||
|
echo -n "$2 (default : $1) (Y/N) ? "
|
||||||
|
read_yes_no $1
|
||||||
|
}
|
||||||
|
|
||||||
|
on_off_function()
|
||||||
|
{
|
||||||
|
# $1 : function name on
|
||||||
|
# $2 : function name off
|
||||||
|
# $3 : 0 - off, 1 - on
|
||||||
|
local F="$1"
|
||||||
|
[ "$3" = "1" ] || F="$2"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
"$F" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_dir_inode()
|
||||||
|
{
|
||||||
|
local dir="$1"
|
||||||
|
[ -L "$dir" ] && dir=$(readlink -f "$dir")
|
||||||
|
ls -id "$dir" | awk '{print $1}'
|
||||||
|
}
|
||||||
|
|
||||||
|
md5file()
|
||||||
|
{
|
||||||
|
md5sum "$1" | cut -f1 -d ' '
|
||||||
|
}
|
||||||
|
|
||||||
|
random()
|
||||||
|
{
|
||||||
|
# $1 - min, $2 - max
|
||||||
|
local r rs
|
||||||
|
if [ -c /dev/urandom ]; then
|
||||||
|
read rs </dev/urandom
|
||||||
|
else
|
||||||
|
rs="$RANDOM$RANDOM$(date)"
|
||||||
|
fi
|
||||||
|
# shells use signed int64
|
||||||
|
r=1$(echo $rs | md5sum | sed 's/[^0-9]//g' | head -c 17)
|
||||||
|
echo $(( ($r % ($2-$1+1)) + $1 ))
|
||||||
|
}
|
||||||
|
|
||||||
|
check_system()
|
||||||
|
{
|
||||||
|
echo \* checking system
|
||||||
|
|
||||||
|
SYSTEM=""
|
||||||
|
SYSTEMCTL=$(whichq systemctl)
|
||||||
|
|
||||||
|
if [ -x "$SYSTEMCTL" ] ; then
|
||||||
|
SYSTEM=systemd
|
||||||
|
elif [ -f "/etc/openwrt_release" ] && exists opkg && exists uci ; then
|
||||||
|
SYSTEM=openwrt
|
||||||
|
else
|
||||||
|
echo system is not either systemd based or openwrt
|
||||||
|
exitp 5
|
||||||
|
fi
|
||||||
|
echo system is based on $SYSTEM
|
||||||
|
}
|
||||||
|
|
||||||
|
check_bins()
|
||||||
|
{
|
||||||
|
echo \* checking executables
|
||||||
|
|
||||||
|
local arch=$(get_bin_arch)
|
||||||
|
[ "$FORCE_BUILD" = "1" ] && {
|
||||||
|
echo forced build mode
|
||||||
|
if [ "$arch" = "my" ]; then
|
||||||
|
echo already compiled
|
||||||
|
else
|
||||||
|
arch=""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
if [ -n "$arch" ] ; then
|
||||||
|
echo found architecture "\"$arch\""
|
||||||
|
elif [ -f "$EXEDIR/Makefile" ] && exists make; then
|
||||||
|
echo trying to compile
|
||||||
|
make -C "$EXEDIR" || {
|
||||||
|
echo could not compile
|
||||||
|
exitp 8
|
||||||
|
}
|
||||||
|
echo compiled
|
||||||
|
else
|
||||||
|
echo build tools not found
|
||||||
|
exitp 8
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
call_install_bin()
|
||||||
|
{
|
||||||
|
"$EXEDIR/install_bin.sh" $1
|
||||||
|
}
|
||||||
|
get_bin_arch()
|
||||||
|
{
|
||||||
|
call_install_bin getarch
|
||||||
|
}
|
||||||
|
|
||||||
|
install_binaries()
|
||||||
|
{
|
||||||
|
echo \* installing binaries
|
||||||
|
|
||||||
|
call_install_bin || {
|
||||||
|
echo compatible binaries not found
|
||||||
|
exitp 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
find_str_in_list()
|
||||||
|
{
|
||||||
|
[ -n "$1" ] && {
|
||||||
|
for v in $2; do
|
||||||
|
[ "$v" = "$1" ] && return
|
||||||
|
done
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
ask_list()
|
||||||
|
{
|
||||||
|
# $1 - mode var
|
||||||
|
# $2 - space separated value list
|
||||||
|
# $3 - (optional) default value
|
||||||
|
local M_DEFAULT
|
||||||
|
eval M_DEFAULT="\$$1"
|
||||||
|
local M_ALL=$M_DEFAULT
|
||||||
|
local M=""
|
||||||
|
local m
|
||||||
|
|
||||||
|
[ -n "$3" ] && { find_str_in_list "$M_DEFAULT" "$2" || M_DEFAULT="$3" ;}
|
||||||
|
|
||||||
|
n=1
|
||||||
|
for m in $2; do
|
||||||
|
echo $n : $m
|
||||||
|
n=$(($n+1))
|
||||||
|
done
|
||||||
|
echo -n "your choice (default : $M_DEFAULT) : "
|
||||||
|
read m
|
||||||
|
[ -n "$m" ] && M=$(echo $2 | cut -d ' ' -f$m 2>/dev/null)
|
||||||
|
[ -z "$M" ] && M="$M_DEFAULT"
|
||||||
|
echo selected : $M
|
||||||
|
eval $1="$M"
|
||||||
|
|
||||||
|
[ "$M" != "$M_OLD" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
write_config_var()
|
||||||
|
{
|
||||||
|
# $1 - mode var
|
||||||
|
local M
|
||||||
|
eval M="\$$1"
|
||||||
|
|
||||||
|
if [ -n "$M" ]; then
|
||||||
|
sed -ri "s/^#?$1=.*$/$1=$M/" "$EXEDIR/config"
|
||||||
|
else
|
||||||
|
# write with comment at the beginning
|
||||||
|
sed -ri "s/^#?$1=.*$/#$1=/" "$EXEDIR/config"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
select_mode()
|
||||||
|
{
|
||||||
|
echo select MODE :
|
||||||
|
ask_list MODE "tpws_ipset tpws_ipset_https tpws_all tpws_all_https tpws_hostlist nfqws_ipset nfqws_ipset_https nfqws_all nfqws_all_https nfqws_all_desync nfqws_ipset_desync nfqws_hostlist_desync ipset custom" tpws_ipset_https && write_config_var MODE
|
||||||
|
}
|
||||||
|
select_getlist()
|
||||||
|
{
|
||||||
|
# do not touch this in custom mode
|
||||||
|
[ "$MODE" = "custom" ] && return
|
||||||
|
|
||||||
|
if [ "${MODE%hostlist*}" != "$MODE" ] || [ "${MODE%ipset*}" != "$MODE" ]; then
|
||||||
|
if ask_yes_no Y "do you want to auto download ip/host list"; then
|
||||||
|
if [ "${MODE%hostlist*}" != "$MODE" ] ; then
|
||||||
|
local GL_OLD=$GETLIST
|
||||||
|
GETLIST="get_reestr_hostlist.sh"
|
||||||
|
[ "$GL_OLD" != "$GET_LIST" ] && write_config_var GETLIST
|
||||||
|
else
|
||||||
|
GETLISTS="get_user.sh get_antifilter_ip.sh get_antifilter_ipsmart.sh get_antifilter_ipsum.sh get_reestr_ip.sh get_reestr_combined.sh get_reestr_resolve.sh"
|
||||||
|
GETLIST_DEF="get_antifilter_ipsmart.sh"
|
||||||
|
ask_list GETLIST "$GETLISTS" "$GETLIST_DEF" && write_config_var GETLIST
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
GETLIST=""
|
||||||
|
write_config_var GETLIST
|
||||||
|
}
|
||||||
|
select_ipv6()
|
||||||
|
{
|
||||||
|
local T=N
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV6" != '1' ] && T=Y
|
||||||
|
local old6=$DISABLE_IPV6
|
||||||
|
if ask_yes_no $T "enable ipv6 support"; then
|
||||||
|
DISABLE_IPV6=0
|
||||||
|
else
|
||||||
|
DISABLE_IPV6=1
|
||||||
|
fi
|
||||||
|
[ "$old6" != "$DISABLE_IPV6" ] && write_config_var DISABLE_IPV6
|
||||||
|
}
|
||||||
|
|
||||||
|
ask_config()
|
||||||
|
{
|
||||||
|
select_mode
|
||||||
|
select_getlist
|
||||||
|
}
|
||||||
|
|
||||||
|
ask_iface()
|
||||||
|
{
|
||||||
|
# $1 - var to ask
|
||||||
|
ask_list $1 "$(ls /sys/class/net)" && write_config_var $1
|
||||||
|
}
|
||||||
|
|
||||||
|
select_router_iface()
|
||||||
|
{
|
||||||
|
local T=N
|
||||||
|
[ -n "$IFACE_LAN" ] && [ -n "$IFACE_WAN" ] && T=Y
|
||||||
|
local old_lan=$IFACE_LAN
|
||||||
|
local old_wan=$IFACE_WAN
|
||||||
|
|
||||||
|
if ask_yes_no $T "is this system a router"; then
|
||||||
|
echo LAN interface :
|
||||||
|
ask_iface IFACE_LAN
|
||||||
|
echo WAN interface :
|
||||||
|
ask_iface IFACE_WAN
|
||||||
|
else
|
||||||
|
[ -n "$old_lan" ] && {
|
||||||
|
IFACE_LAN=""
|
||||||
|
write_config_var IFACE_LAN
|
||||||
|
}
|
||||||
|
[ -n "$old_wan" ] && {
|
||||||
|
IFACE_WAN=""
|
||||||
|
write_config_var IFACE_WAN
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
ask_config_desktop()
|
||||||
|
{
|
||||||
|
select_router_iface
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_all()
|
||||||
|
{
|
||||||
|
cp -R "$1" "$2"
|
||||||
|
[ -d "$2/tmp" ] || mkdir "$2/tmp"
|
||||||
|
}
|
||||||
|
copy_minimal()
|
||||||
|
{
|
||||||
|
local ARCH=$(get_bin_arch)
|
||||||
|
local BINDIR="$1/binaries/$ARCH"
|
||||||
|
|
||||||
|
[ -d "$2" ] || mkdir -p "$2"
|
||||||
|
|
||||||
|
mkdir "$2/tpws" "$2/nfq" "$2/ip2net" "$2/mdig" "$2/binaries" "$2/binaries/$ARCH" "$2/tmp"
|
||||||
|
cp -R "$1/ipset" "$2"
|
||||||
|
cp -R "$1/init.d" "$2"
|
||||||
|
cp "$1/config" "$1/install_easy.sh" "$1/uninstall_easy.sh" "$1/install_bin.sh" "$2"
|
||||||
|
cp "$BINDIR/tpws" "$BINDIR/nfqws" "$BINDIR/ip2net" "$BINDIR/mdig" "$2/binaries/$ARCH"
|
||||||
|
}
|
||||||
|
|
||||||
|
_backup_settings()
|
||||||
|
{
|
||||||
|
local i=0
|
||||||
|
for f in "$@"; do
|
||||||
|
[ -f "$ZAPRET_BASE/$f" ] && cp -f "$ZAPRET_BASE/$f" "/tmp/zapret-bkp-$i"
|
||||||
|
i=$(($i+1))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
_restore_settings()
|
||||||
|
{
|
||||||
|
local i=0
|
||||||
|
for f in "$@"; do
|
||||||
|
[ -f "/tmp/zapret-bkp-$i" ] && mv -f "/tmp/zapret-bkp-$i" "$ZAPRET_BASE/$f"
|
||||||
|
i=$(($i+1))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
backup_restore_settings()
|
||||||
|
{
|
||||||
|
# $1 - 1 - backup, 0 - restore
|
||||||
|
local mode=$1
|
||||||
|
on_off_function _backup_settings _restore_settings $mode "config" "init.d/sysv/custom" "init.d/openwrt/custom"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_location()
|
||||||
|
{
|
||||||
|
# $1 - copy function
|
||||||
|
|
||||||
|
echo \* checking location
|
||||||
|
|
||||||
|
# use inodes in case something is linked
|
||||||
|
[ -d "$ZAPRET_BASE" ] && [ $(get_dir_inode "$EXEDIR") = $(get_dir_inode "$ZAPRET_BASE") ] || {
|
||||||
|
echo easy install is supported only from default location : $ZAPRET_BASE
|
||||||
|
echo currently its run from $EXEDIR
|
||||||
|
if ask_yes_no N "do you want the installer to copy it for you"; then
|
||||||
|
local keep=N
|
||||||
|
if [ -d "$ZAPRET_BASE" ]; then
|
||||||
|
echo installer found existing $ZAPRET_BASE
|
||||||
|
echo directory needs to be replaced. config and custom scripts can be kept or replaced with clean version
|
||||||
|
if ask_yes_no N "do you want to delete all files there and copy this version"; then
|
||||||
|
ask_yes_no Y "keep config and custom scripts" && keep=Y
|
||||||
|
[ "$keep" = "Y" ] && backup_restore_settings 1
|
||||||
|
rm -r "$ZAPRET_BASE"
|
||||||
|
else
|
||||||
|
echo refused to overwrite $ZAPRET_BASE. exiting
|
||||||
|
exitp 3
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
local B=$(dirname "$ZAPRET_BASE")
|
||||||
|
[ -d "$B" ] || mkdir -p "$B"
|
||||||
|
$1 "$EXEDIR" "$ZAPRET_BASE"
|
||||||
|
[ "$keep" = "Y" ] && backup_restore_settings 0
|
||||||
|
echo relaunching itself from $ZAPRET_BASE
|
||||||
|
exec $ZAPRET_BASE/$(basename $0)
|
||||||
|
else
|
||||||
|
echo copying aborted. exiting
|
||||||
|
exitp 3
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
echo running from $EXEDIR
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
check_prerequisites_linux()
|
||||||
|
{
|
||||||
|
echo \* checking prerequisites
|
||||||
|
|
||||||
|
if exists ipset && exists curl ; then
|
||||||
|
echo everything is present
|
||||||
|
else
|
||||||
|
echo \* installing prerequisites
|
||||||
|
|
||||||
|
APTGET=$(whichq apt-get)
|
||||||
|
YUM=$(whichq yum)
|
||||||
|
PACMAN=$(whichq pacman)
|
||||||
|
ZYPPER=$(whichq zypper)
|
||||||
|
EOPKG=$(whichq eopkg)
|
||||||
|
if [ -x "$APTGET" ] ; then
|
||||||
|
"$APTGET" update
|
||||||
|
"$APTGET" install -y --no-install-recommends ipset curl dnsutils || {
|
||||||
|
echo could not install prerequisites
|
||||||
|
exitp 6
|
||||||
|
}
|
||||||
|
elif [ -x "$YUM" ] ; then
|
||||||
|
"$YUM" -y install curl ipset || {
|
||||||
|
echo could not install prerequisites
|
||||||
|
exitp 6
|
||||||
|
}
|
||||||
|
elif [ -x "$PACMAN" ] ; then
|
||||||
|
"$PACMAN" -Syy
|
||||||
|
"$PACMAN" --noconfirm -S ipset curl || {
|
||||||
|
echo could not install prerequisites
|
||||||
|
exitp 6
|
||||||
|
}
|
||||||
|
elif [ -x "$ZYPPER" ] ; then
|
||||||
|
"$ZYPPER" --non-interactive install ipset curl || {
|
||||||
|
echo could not install prerequisites
|
||||||
|
exitp 6
|
||||||
|
}
|
||||||
|
elif [ -x "$EOPKG" ] ; then
|
||||||
|
"$EOPKG" -y install ipset curl || {
|
||||||
|
echo could not install prerequisites
|
||||||
|
exitp 6
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo supported package manager not found
|
||||||
|
echo you must manually install : ipset curl
|
||||||
|
exitp 5
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
service_install_systemd()
|
||||||
|
{
|
||||||
|
echo \* installing zapret service
|
||||||
|
|
||||||
|
rm -f "$INIT_SCRIPT"
|
||||||
|
ln -fs "$EXEDIR/init.d/systemd/zapret.service" "$SYSTEMD_SYSTEM_DIR"
|
||||||
|
"$SYSTEMCTL" daemon-reload
|
||||||
|
"$SYSTEMCTL" enable zapret || {
|
||||||
|
echo could not enable systemd service
|
||||||
|
exitp 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service_stop_systemd()
|
||||||
|
{
|
||||||
|
echo \* stopping zapret service
|
||||||
|
|
||||||
|
"$SYSTEMCTL" daemon-reload
|
||||||
|
"$SYSTEMCTL" disable zapret
|
||||||
|
"$SYSTEMCTL" stop zapret
|
||||||
|
}
|
||||||
|
|
||||||
|
service_start_systemd()
|
||||||
|
{
|
||||||
|
echo \* starting zapret service
|
||||||
|
|
||||||
|
"$SYSTEMCTL" start zapret || {
|
||||||
|
echo could not start zapret service
|
||||||
|
exitp 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_install_systemd()
|
||||||
|
{
|
||||||
|
echo \* installing zapret-list-update timer
|
||||||
|
|
||||||
|
"$SYSTEMCTL" disable zapret-list-update.timer
|
||||||
|
"$SYSTEMCTL" stop zapret-list-update.timer
|
||||||
|
ln -fs "$EXEDIR/init.d/systemd/zapret-list-update.service" "$SYSTEMD_SYSTEM_DIR"
|
||||||
|
ln -fs "$EXEDIR/init.d/systemd/zapret-list-update.timer" "$SYSTEMD_SYSTEM_DIR"
|
||||||
|
"$SYSTEMCTL" daemon-reload
|
||||||
|
"$SYSTEMCTL" enable zapret-list-update.timer || {
|
||||||
|
echo could not enable zapret-list-update.timer
|
||||||
|
exitp 20
|
||||||
|
}
|
||||||
|
"$SYSTEMCTL" start zapret-list-update.timer || {
|
||||||
|
echo could not start zapret-list-update.timer
|
||||||
|
exitp 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
download_list()
|
||||||
|
{
|
||||||
|
[ -x "$GET_LIST" ] && {
|
||||||
|
echo \* downloading blocked ip/host list
|
||||||
|
|
||||||
|
# can be txt or txt.gz
|
||||||
|
"$EXEDIR/ipset/clear_lists.sh"
|
||||||
|
"$GET_LIST" || {
|
||||||
|
echo could not download ip list
|
||||||
|
exitp 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crontab_del_quiet()
|
||||||
|
{
|
||||||
|
exists crontab || return
|
||||||
|
|
||||||
|
CRONTMP=/tmp/cron.tmp
|
||||||
|
crontab -l >$CRONTMP
|
||||||
|
if grep -q "$GET_IPLIST_PREFIX" $CRONTMP; then
|
||||||
|
grep -v "$GET_IPLIST_PREFIX" $CRONTMP >$CRONTMP.2
|
||||||
|
crontab $CRONTMP.2
|
||||||
|
rm -f $CRONTMP.2
|
||||||
|
fi
|
||||||
|
rm -f $CRONTMP
|
||||||
|
}
|
||||||
|
|
||||||
|
crontab_add()
|
||||||
|
{
|
||||||
|
# $1 - hour min
|
||||||
|
# $2 - hour max
|
||||||
|
[ -x "$GET_LIST" ] && {
|
||||||
|
echo \* adding crontab entry
|
||||||
|
|
||||||
|
CRONTMP=/tmp/cron.tmp
|
||||||
|
crontab -l >$CRONTMP
|
||||||
|
if grep -q "$GET_LIST_PREFIX" $CRONTMP; then
|
||||||
|
echo some entries already exist in crontab. check if this is corrent :
|
||||||
|
grep "$GET_LIST_PREFIX" $CRONTMP
|
||||||
|
else
|
||||||
|
echo "$(random 0 59) $(random $1 $2) */2 * * $GET_LIST" >>$CRONTMP
|
||||||
|
crontab $CRONTMP
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f $CRONTMP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
install_systemd()
|
||||||
|
{
|
||||||
|
INIT_SCRIPT_SRC=$EXEDIR/init.d/sysv/zapret
|
||||||
|
|
||||||
|
check_bins
|
||||||
|
check_location copy_all
|
||||||
|
check_prerequisites_linux
|
||||||
|
service_stop_systemd
|
||||||
|
install_binaries
|
||||||
|
select_ipv6
|
||||||
|
ask_config_desktop
|
||||||
|
ask_config
|
||||||
|
service_install_systemd
|
||||||
|
download_list
|
||||||
|
# in case its left from old version of zapret
|
||||||
|
crontab_del_quiet
|
||||||
|
# now we use systemd timers
|
||||||
|
timer_install_systemd
|
||||||
|
service_start_systemd
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
check_kmod()
|
||||||
|
{
|
||||||
|
[ -f "/lib/modules/$(uname -r)/$1.ko" ]
|
||||||
|
}
|
||||||
|
check_package_exists_openwrt()
|
||||||
|
{
|
||||||
|
[ -n "$(opkg list $1)" ]
|
||||||
|
}
|
||||||
|
check_package_openwrt()
|
||||||
|
{
|
||||||
|
[ -n "$(opkg list-installed $1)" ]
|
||||||
|
}
|
||||||
|
check_packages_openwrt()
|
||||||
|
{
|
||||||
|
for pkg in $@; do
|
||||||
|
check_package_openwrt $pkg || return
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
check_prerequisites_openwrt()
|
||||||
|
{
|
||||||
|
echo \* checking prerequisites
|
||||||
|
|
||||||
|
local PKGS="iptables-mod-extra iptables-mod-nfqueue iptables-mod-filter iptables-mod-ipopt iptables-mod-conntrack-extra ipset curl"
|
||||||
|
[ "$DISABLE_IPV6" != "1" ] && PKGS="$PKGS ip6tables-mod-nat"
|
||||||
|
local UPD=0
|
||||||
|
|
||||||
|
if check_packages_openwrt $PKGS ; then
|
||||||
|
echo everything is present
|
||||||
|
else
|
||||||
|
echo \* installing prerequisites
|
||||||
|
|
||||||
|
opkg update
|
||||||
|
UPD=1
|
||||||
|
opkg install $PKGS || {
|
||||||
|
echo could not install prerequisites
|
||||||
|
exitp 6
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -x "/usr/bin/gzip" ] || {
|
||||||
|
echo your system uses default busybox gzip. its several times slower than gnu gzip.
|
||||||
|
echo ip/host list scripts will run much faster with gnu gzip
|
||||||
|
echo installer can install gnu gzip but it requires about 100 Kb space
|
||||||
|
if ask_yes_no N "do you want to install gnu gzip"; then
|
||||||
|
[ "$UPD" = "0" ] && {
|
||||||
|
opkg update
|
||||||
|
UPD=1
|
||||||
|
}
|
||||||
|
opkg install gzip
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
[ -x "/usr/bin/grep" ] || {
|
||||||
|
echo your system uses default busybox grep. its damn infinite slow with -f option
|
||||||
|
echo get_combined.sh will be severely impacted
|
||||||
|
echo installer can install gnu grep but it requires about 0.5 Mb space
|
||||||
|
if ask_yes_no N "do you want to install gnu grep"; then
|
||||||
|
[ "$UPD" = "0" ] && {
|
||||||
|
opkg update
|
||||||
|
UPD=1
|
||||||
|
}
|
||||||
|
opkg install grep
|
||||||
|
|
||||||
|
# someone reported device partially fail if /bin/grep is absent
|
||||||
|
# grep package deletes /bin/grep
|
||||||
|
[ -f /bin/grep ] || ln -s busybox /bin/grep
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openwrt_fw_section_find()
|
||||||
|
{
|
||||||
|
# $1 - fw include postfix
|
||||||
|
# echoes section number
|
||||||
|
|
||||||
|
i=0
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
path=$(uci -q get firewall.@include[$i].path)
|
||||||
|
[ -n "$path" ] || break
|
||||||
|
[ "$path" = "$OPENWRT_FW_INCLUDE$1" ] && {
|
||||||
|
echo $i
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i=$(($i+1))
|
||||||
|
done
|
||||||
|
false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
openwrt_fw_section_del()
|
||||||
|
{
|
||||||
|
# $1 - fw include postfix
|
||||||
|
|
||||||
|
local id=$(openwrt_fw_section_find $1)
|
||||||
|
[ -n "$id" ] && {
|
||||||
|
uci delete firewall.@include[$id] && uci commit firewall
|
||||||
|
rm -f "$OPENWRT_FW_INCLUDE$1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
openwrt_fw_section_add()
|
||||||
|
{
|
||||||
|
openwrt_fw_section_find ||
|
||||||
|
{
|
||||||
|
uci add firewall include >/dev/null || return
|
||||||
|
echo -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
openwrt_fw_section_configure()
|
||||||
|
{
|
||||||
|
local id=$(openwrt_fw_section_add $1)
|
||||||
|
[ -z "$id" ] ||
|
||||||
|
! uci set firewall.@include[$id].path="$OPENWRT_FW_INCLUDE" ||
|
||||||
|
! uci set firewall.@include[$id].reload="1" ||
|
||||||
|
! uci commit firewall &&
|
||||||
|
{
|
||||||
|
echo could not add firewall include
|
||||||
|
exitp 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
install_openwrt_firewall()
|
||||||
|
{
|
||||||
|
echo \* installing firewall script $1
|
||||||
|
|
||||||
|
[ -n "MODE" ] || {
|
||||||
|
echo should specify MODE in $ZAPRET_CONFIG
|
||||||
|
exitp 7
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "linking : $FW_SCRIPT_SRC => $OPENWRT_FW_INCLUDE"
|
||||||
|
ln -fs "$FW_SCRIPT_SRC" "$OPENWRT_FW_INCLUDE"
|
||||||
|
|
||||||
|
openwrt_fw_section_configure $1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
restart_openwrt_firewall()
|
||||||
|
{
|
||||||
|
echo \* restarting firewall
|
||||||
|
|
||||||
|
fw3 -q restart || {
|
||||||
|
echo could not restart firewall
|
||||||
|
exitp 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_openwrt_firewall()
|
||||||
|
{
|
||||||
|
echo \* removing firewall script
|
||||||
|
|
||||||
|
openwrt_fw_section_del
|
||||||
|
# from old zapret versions. now we use single include
|
||||||
|
openwrt_fw_section_del 6
|
||||||
|
}
|
||||||
|
|
||||||
|
install_openwrt_iface_hook()
|
||||||
|
{
|
||||||
|
echo \* installing ifup hook
|
||||||
|
|
||||||
|
ln -fs "$OPENWRT_IFACE_HOOK" /etc/hotplug.d/iface
|
||||||
|
}
|
||||||
|
|
||||||
|
deoffload_openwrt_firewall()
|
||||||
|
{
|
||||||
|
echo \* checking flow offloading
|
||||||
|
|
||||||
|
local mod=0
|
||||||
|
local fo=$(uci -q get firewall.@defaults[0].flow_offloading)
|
||||||
|
local fo_hw=$(uci -q get firewall.@defaults[0].flow_offloading_hw)
|
||||||
|
|
||||||
|
if [ "$fo_hw" = "1" ] ; then
|
||||||
|
echo hardware flow offloading detected. its incompatible with zapret. disabling
|
||||||
|
uci set firewall.@defaults[0].flow_offloading_hw=0
|
||||||
|
mod=1
|
||||||
|
else
|
||||||
|
echo hardware flow offloading disabled. ok
|
||||||
|
fi
|
||||||
|
if [ "$fo" = "1" ] ; then
|
||||||
|
echo -n "software flow offloading detected. "
|
||||||
|
if [ "${MODE%nfqws*}" != "$MODE" ]; then
|
||||||
|
echo its incompatible with nfqws tcp data tampering. disabling
|
||||||
|
uci set firewall.@defaults[0].flow_offloading=0
|
||||||
|
mod=1
|
||||||
|
else
|
||||||
|
echo its compatible with selected options. not disabling
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo software flow offloading disabled. ok
|
||||||
|
fi
|
||||||
|
[ "$mod" = "1" ] && uci commit firewall
|
||||||
|
}
|
||||||
|
|
||||||
|
install_sysv_init()
|
||||||
|
{
|
||||||
|
# $1 - "0"=disable
|
||||||
|
echo \* installing init script
|
||||||
|
|
||||||
|
[ -x "$INIT_SCRIPT" ] && {
|
||||||
|
"$INIT_SCRIPT" stop
|
||||||
|
"$INIT_SCRIPT" disable
|
||||||
|
}
|
||||||
|
ln -fs "$INIT_SCRIPT_SRC" "$INIT_SCRIPT"
|
||||||
|
[ "$1" != "0" ] && "$INIT_SCRIPT" enable
|
||||||
|
}
|
||||||
|
|
||||||
|
service_start_sysv()
|
||||||
|
{
|
||||||
|
echo \* starting zapret service
|
||||||
|
|
||||||
|
"$INIT_SCRIPT" start || {
|
||||||
|
echo could not start zapret service
|
||||||
|
exitp 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
install_openwrt()
|
||||||
|
{
|
||||||
|
INIT_SCRIPT_SRC=$EXEDIR/init.d/openwrt/zapret
|
||||||
|
FW_SCRIPT_SRC=$EXEDIR/init.d/openwrt/firewall.zapret
|
||||||
|
OPENWRT_FW_INCLUDE=/etc/firewall.zapret
|
||||||
|
OPENWRT_IFACE_HOOK=$EXEDIR/init.d/openwrt/90-zapret
|
||||||
|
|
||||||
|
check_bins
|
||||||
|
check_location copy_minimal
|
||||||
|
select_ipv6
|
||||||
|
check_prerequisites_openwrt
|
||||||
|
install_binaries
|
||||||
|
ask_config
|
||||||
|
install_sysv_init
|
||||||
|
# can be previous firewall preventing access
|
||||||
|
remove_openwrt_firewall
|
||||||
|
restart_openwrt_firewall
|
||||||
|
download_list
|
||||||
|
# router system : works 24/7. night is the best time
|
||||||
|
crontab_add 0 6
|
||||||
|
service_start_sysv
|
||||||
|
install_openwrt_iface_hook
|
||||||
|
install_openwrt_firewall
|
||||||
|
deoffload_openwrt_firewall
|
||||||
|
restart_openwrt_firewall
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# build binaries, do not use precompiled
|
||||||
|
[ "$1" = "make" ] && FORCE_BUILD=1
|
||||||
|
|
||||||
|
check_system
|
||||||
|
|
||||||
|
case $SYSTEM in
|
||||||
|
systemd)
|
||||||
|
install_systemd
|
||||||
|
;;
|
||||||
|
openwrt)
|
||||||
|
install_openwrt
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
|
||||||
|
exitp 0
|
12
ip2net/Makefile
Normal file
12
ip2net/Makefile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
CC ?= gcc
|
||||||
|
CFLAGS += -std=c99 -s -O3
|
||||||
|
LIBS =
|
||||||
|
SRC_FILES = *.c
|
||||||
|
|
||||||
|
all: ip2net
|
||||||
|
|
||||||
|
ip2net: $(SRC_FILES)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f ip2net *.o
|
396
ip2net/ip2net.c
Normal file
396
ip2net/ip2net.c
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
// group ipv4/ipv6 list from stdout into subnets
|
||||||
|
// each line must contain either ip or ip/bitcount
|
||||||
|
// valid ip/bitcount and ip1-ip2 are passed through without modification
|
||||||
|
// ips are groupped into subnets
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include "qsort.h"
|
||||||
|
|
||||||
|
#define ALLOC_STEP 16384
|
||||||
|
|
||||||
|
// minimum subnet fill percent is PCTMULT/PCTDIV (for example 3/4)
|
||||||
|
#define DEFAULT_PCTMULT 3
|
||||||
|
#define DEFAULT_PCTDIV 4
|
||||||
|
// subnet search range in "zero bit count"
|
||||||
|
// means search start from /(32-ZCT_MAX) to /(32-ZCT_MIN)
|
||||||
|
#define DEFAULT_V4_ZCT_MAX 10 // /22
|
||||||
|
#define DEFAULT_V4_ZCT_MIN 2 // /30
|
||||||
|
#define DEFAULT_V6_ZCT_MAX 72 // /56
|
||||||
|
#define DEFAULT_V6_ZCT_MIN 64 // /64
|
||||||
|
// must be no less than N ipv6 in subnet
|
||||||
|
#define DEFAULT_V6_THRESHOLD 5
|
||||||
|
|
||||||
|
static int ucmp(const void * a, const void * b, void *arg)
|
||||||
|
{
|
||||||
|
if (*(uint32_t*)a < *(uint32_t*)b)
|
||||||
|
return -1;
|
||||||
|
else if (*(uint32_t*)a > *(uint32_t*)b)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static uint32_t mask_from_bitcount(uint32_t zct)
|
||||||
|
{
|
||||||
|
return ~((1 << zct) - 1);
|
||||||
|
}
|
||||||
|
// make presorted array unique. return number of unique items.
|
||||||
|
// 1,1,2,3,3,0,0,0 (ct=8) => 1,2,3,0 (ct=4)
|
||||||
|
static uint32_t unique(uint32_t *pu, uint32_t ct)
|
||||||
|
{
|
||||||
|
uint32_t i, j, u;
|
||||||
|
for (i = j = 0; j < ct; i++)
|
||||||
|
{
|
||||||
|
u = pu[j++];
|
||||||
|
for (; j < ct && pu[j] == u; j++);
|
||||||
|
pu[i] = u;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int cmp6(const void * a, const void * b, void *arg)
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < sizeof(((struct in6_addr *)0)->s6_addr); i++)
|
||||||
|
{
|
||||||
|
if (((struct in6_addr *)a)->s6_addr[i] < ((struct in6_addr *)b)->s6_addr[i])
|
||||||
|
return -1;
|
||||||
|
else if (((struct in6_addr *)a)->s6_addr[i] > ((struct in6_addr *)b)->s6_addr[i])
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// make presorted array unique. return number of unique items.
|
||||||
|
static uint32_t unique6(struct in6_addr *pu, uint32_t ct)
|
||||||
|
{
|
||||||
|
uint32_t i, j, k;
|
||||||
|
for (i = j = 0; j < ct; i++)
|
||||||
|
{
|
||||||
|
for (k = j++; j < ct && !memcmp(pu + j, pu + k, sizeof(struct in6_addr)); j++);
|
||||||
|
pu[i] = pu[k];
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
static void mask_from_bitcount6(uint32_t zct, struct in6_addr *a)
|
||||||
|
{
|
||||||
|
if (zct >= 128)
|
||||||
|
memset(a->s6_addr,0x00,16);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int32_t n = (127 - zct) >> 3;
|
||||||
|
memset(a->s6_addr,0xFF,n);
|
||||||
|
memset(a->s6_addr+n,0x00,16-n);
|
||||||
|
a->s6_addr[n] = ~((1 << (zct & 7)) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// result = a & b
|
||||||
|
static void ip6_and(const struct in6_addr *a, const struct in6_addr *b, struct in6_addr *result)
|
||||||
|
{
|
||||||
|
((uint64_t*)result->s6_addr)[0] = ((uint64_t*)a->s6_addr)[0] & ((uint64_t*)b->s6_addr)[0];
|
||||||
|
((uint64_t*)result->s6_addr)[1] = ((uint64_t*)a->s6_addr)[1] & ((uint64_t*)b->s6_addr)[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rtrim(char *s)
|
||||||
|
{
|
||||||
|
if (s)
|
||||||
|
for (char *p = s + strlen(s) - 1; p >= s && (*p == '\n' || *p == '\r'); p--) *p = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct params_s
|
||||||
|
{
|
||||||
|
bool ipv6;
|
||||||
|
uint32_t pctmult, pctdiv; // for v4
|
||||||
|
uint32_t zct_min, zct_max; // for v4 and v6
|
||||||
|
uint32_t v6_threshold; // for v6
|
||||||
|
} params;
|
||||||
|
|
||||||
|
|
||||||
|
static void exithelp()
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
" -4\t\t\t\t; ipv4 list (default)\n"
|
||||||
|
" -6\t\t\t\t; ipv6 list\n"
|
||||||
|
" --prefix-length=min[-max]\t; consider prefix lengths from 'min' to 'max'. examples : 22-30 (ipv4), 56-64 (ipv6)\n"
|
||||||
|
" --v4-threshold=mul/div\t\t; ipv4 only : include subnets with more than mul/div ips. example : 3/4\n"
|
||||||
|
" --v6-threshold=N\t\t; ipv6 only : include subnets with more than N v6 ips. example : 5\n"
|
||||||
|
);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_params(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int option_index = 0;
|
||||||
|
int v, i;
|
||||||
|
uint32_t plen1=-1, plen2=-1;
|
||||||
|
|
||||||
|
memset(¶ms, 0, sizeof(params));
|
||||||
|
params.pctmult = DEFAULT_PCTMULT;
|
||||||
|
params.pctdiv = DEFAULT_PCTDIV;
|
||||||
|
params.v6_threshold = DEFAULT_V6_THRESHOLD;
|
||||||
|
|
||||||
|
const struct option long_options[] = {
|
||||||
|
{ "help",no_argument,0,0 },// optidx=0
|
||||||
|
{ "h",no_argument,0,0 },// optidx=1
|
||||||
|
{ "4",no_argument,0,0 },// optidx=2
|
||||||
|
{ "6",no_argument,0,0 },// optidx=3
|
||||||
|
{ "prefix-length",required_argument,0,0 },// optidx=4
|
||||||
|
{ "v4-threshold",required_argument,0,0 },// optidx=5
|
||||||
|
{ "v6-threshold",required_argument,0,0 },// optidx=6
|
||||||
|
{ NULL,0,NULL,0 }
|
||||||
|
};
|
||||||
|
while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1)
|
||||||
|
{
|
||||||
|
if (v) exithelp();
|
||||||
|
switch (option_index)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
exithelp();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
params.ipv6 = false;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
params.ipv6 = true;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
i = sscanf(optarg,"%u-%u",&plen1,&plen2);
|
||||||
|
if (i == 1) plen2 = plen1;
|
||||||
|
if (!i || plen2<plen1 || !plen1 || !plen2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "invalid parameter for prefix-length : %s\n", optarg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
i = sscanf(optarg, "%u/%u", ¶ms.pctmult, ¶ms.pctdiv);
|
||||||
|
if (i!=2 || params.pctdiv<2 || params.pctmult<1 || params.pctmult>=params.pctdiv)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "invalid parameter for v4-threshold : %s\n", optarg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
i = sscanf(optarg, "%u", ¶ms.v6_threshold);
|
||||||
|
if (i != 1 || params.v6_threshold<1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "invalid parameter for v6-threshold : %s\n", optarg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (plen1 != -1 && (!params.ipv6 && (plen1>31 || plen2>31) || params.ipv6 && (plen1>127 || plen2>127)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "invalid parameter for prefix-length\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
params.zct_min = params.ipv6 ? plen2==-1 ? DEFAULT_V6_ZCT_MIN : 128-plen2 : plen2==-1 ? DEFAULT_V4_ZCT_MIN : 32-plen2;
|
||||||
|
params.zct_max = params.ipv6 ? plen1==-1 ? DEFAULT_V6_ZCT_MAX : 128-plen1 : plen1==-1 ? DEFAULT_V4_ZCT_MAX : 32-plen1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char str[256],d;
|
||||||
|
uint32_t ipct = 0, iplist_size = 0, pos = 0, p, zct, ip_ct, pos_end;
|
||||||
|
|
||||||
|
parse_params(argc, argv);
|
||||||
|
|
||||||
|
if (params.ipv6) // ipv6
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
struct in6_addr a, *iplist = NULL, *iplist_new;
|
||||||
|
|
||||||
|
while (fgets(str, sizeof(str), stdin))
|
||||||
|
{
|
||||||
|
rtrim(str);
|
||||||
|
d = 0;
|
||||||
|
if ((s = strchr(str, '/')) || (s = strchr(str, '-')))
|
||||||
|
{
|
||||||
|
d = *s;
|
||||||
|
*s = '\0';
|
||||||
|
}
|
||||||
|
if (inet_pton(AF_INET6, str, &a))
|
||||||
|
{
|
||||||
|
if (d=='/')
|
||||||
|
{
|
||||||
|
// we have subnet ip6/y
|
||||||
|
// output it as is
|
||||||
|
*s = d;
|
||||||
|
if (sscanf(s + 1, "%u", &zct) && zct!=128)
|
||||||
|
{
|
||||||
|
if (zct<128) printf("%s\n", str);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (d=='-')
|
||||||
|
{
|
||||||
|
*s = d;
|
||||||
|
if (inet_pton(AF_INET6, s+1, &a)) printf("%s\n", str);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ipct >= iplist_size)
|
||||||
|
{
|
||||||
|
iplist_size += ALLOC_STEP;
|
||||||
|
iplist_new = (struct in6_addr*)(iplist ? realloc(iplist, sizeof(*iplist)*iplist_size) : malloc(sizeof(*iplist)*iplist_size));
|
||||||
|
if (!iplist_new)
|
||||||
|
{
|
||||||
|
free(iplist);
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
iplist = iplist_new;
|
||||||
|
}
|
||||||
|
iplist[ipct++] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gnu_quicksort(iplist, ipct, sizeof(*iplist), cmp6, NULL);
|
||||||
|
ipct = unique6(iplist, ipct);
|
||||||
|
|
||||||
|
/*
|
||||||
|
for(uint32_t i=0;i<ipct;i++)
|
||||||
|
if (inet_ntop(AF_INET6,iplist+i,str,256))
|
||||||
|
printf("%s\n",str);
|
||||||
|
printf("\n");
|
||||||
|
*/
|
||||||
|
while (pos < ipct)
|
||||||
|
{
|
||||||
|
struct in6_addr mask, ip_start, ip;
|
||||||
|
uint32_t ip_ct_best = 0, zct_best = 0;
|
||||||
|
|
||||||
|
pos_end = pos + 1;
|
||||||
|
// find smallest network with maximum ip coverage with no less than ip6_subnet_threshold addresses
|
||||||
|
for (zct = params.zct_max; zct >= params.zct_min; zct--)
|
||||||
|
{
|
||||||
|
mask_from_bitcount6(zct, &mask);
|
||||||
|
ip6_and(iplist + pos, &mask, &ip_start);
|
||||||
|
for (p = pos + 1, ip_ct = 1; p < ipct; p++, ip_ct++)
|
||||||
|
{
|
||||||
|
ip6_and(iplist + p, &mask, &ip);
|
||||||
|
if (memcmp(&ip_start, &ip, sizeof(ip)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ip_ct == 1) break;
|
||||||
|
if (ip_ct >= params.v6_threshold)
|
||||||
|
{
|
||||||
|
// network found. but is there smaller network with the same ip_ct ? dont do carpet bombing if possible, use smaller subnets
|
||||||
|
if (!ip_ct_best || ip_ct == ip_ct_best)
|
||||||
|
{
|
||||||
|
ip_ct_best = ip_ct;
|
||||||
|
zct_best = zct;
|
||||||
|
pos_end = p;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!zct_best) ip_start = iplist[pos], pos_end = pos + 1; // network not found, use single ip
|
||||||
|
inet_ntop(AF_INET6, &ip_start, str, sizeof(str));
|
||||||
|
printf(zct_best ? "%s/%u\n" : "%s\n", str, 128 - zct_best);
|
||||||
|
|
||||||
|
pos = pos_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(iplist);
|
||||||
|
}
|
||||||
|
else // ipv4
|
||||||
|
{
|
||||||
|
uint32_t u1,u2,u3,u4, u11,u22,u33,u44, ip;
|
||||||
|
uint32_t *iplist = NULL, *iplist_new;
|
||||||
|
uint32_t i, subnet_ct, end_ip;
|
||||||
|
|
||||||
|
while (fgets(str, sizeof(str), stdin))
|
||||||
|
{
|
||||||
|
if ((i = sscanf(str, "%u.%u.%u.%u-%u.%u.%u.%u", &u1, &u2, &u3, &u4, &u11, &u22, &u33, &u44)) >= 8 &&
|
||||||
|
!(u1 & 0xFFFFFF00) && !(u2 & 0xFFFFFF00) && !(u3 & 0xFFFFFF00) && !(u4 & 0xFFFFFF00) &&
|
||||||
|
!(u11 & 0xFFFFFF00) && !(u22 & 0xFFFFFF00) && !(u33 & 0xFFFFFF00) && !(u44 & 0xFFFFFF00))
|
||||||
|
{
|
||||||
|
printf("%u.%u.%u.%u-%u.%u.%u.%u\n", u1, u2, u3, u4, u11, u22, u33, u44);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if ((i = sscanf(str, "%u.%u.%u.%u/%u", &u1, &u2, &u3, &u4, &zct)) >= 4 &&
|
||||||
|
!(u1 & 0xFFFFFF00) && !(u2 & 0xFFFFFF00) && !(u3 & 0xFFFFFF00) && !(u4 & 0xFFFFFF00))
|
||||||
|
{
|
||||||
|
if (i == 5 && zct != 32)
|
||||||
|
{
|
||||||
|
// we have subnet x.x.x.x/y
|
||||||
|
// output it as is if valid, ignore otherwise
|
||||||
|
if (zct < 32)
|
||||||
|
printf("%u.%u.%u.%u/%u\n", u1, u2, u3, u4, zct);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ip = u1 << 24 | u2 << 16 | u3 << 8 | u4;
|
||||||
|
if (ipct >= iplist_size)
|
||||||
|
{
|
||||||
|
iplist_size += ALLOC_STEP;
|
||||||
|
iplist_new = (uint32_t*)(iplist ? realloc(iplist, sizeof(*iplist)*iplist_size) : malloc(sizeof(*iplist)*iplist_size));
|
||||||
|
if (!iplist_new)
|
||||||
|
{
|
||||||
|
free(iplist);
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
iplist = iplist_new;
|
||||||
|
}
|
||||||
|
iplist[ipct++] = ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gnu_quicksort(iplist, ipct, sizeof(*iplist), ucmp, NULL);
|
||||||
|
ipct = unique(iplist, ipct);
|
||||||
|
|
||||||
|
while (pos < ipct)
|
||||||
|
{
|
||||||
|
uint32_t mask, ip_start, ip_end, subnet_ct;
|
||||||
|
uint32_t ip_ct_best = 0, zct_best = 0;
|
||||||
|
|
||||||
|
// find smallest network with maximum ip coverage with no less than mul/div percent addresses
|
||||||
|
for (zct = params.zct_max; zct >= params.zct_min; zct--)
|
||||||
|
{
|
||||||
|
mask = mask_from_bitcount(zct);
|
||||||
|
ip_start = iplist[pos] & mask;
|
||||||
|
subnet_ct = ~mask + 1;
|
||||||
|
if (iplist[pos] > (ip_start + subnet_ct*(params.pctdiv - params.pctmult) / params.pctdiv))
|
||||||
|
continue; // ip is higher than (1-PCT). definitely coverage is not enough. skip searching
|
||||||
|
ip_end = ip_start | ~mask;
|
||||||
|
for (p=pos+1, ip_ct=1; p < ipct && iplist[p] <= ip_end; p++) ip_ct++; // count ips within subnet range
|
||||||
|
if (ip_ct == 1) break;
|
||||||
|
if (ip_ct >= (subnet_ct*params.pctmult / params.pctdiv))
|
||||||
|
{
|
||||||
|
// network found. but is there smaller network with the same ip_ct ? dont do carpet bombing if possible, use smaller subnets
|
||||||
|
if (!ip_ct_best || ip_ct == ip_ct_best)
|
||||||
|
{
|
||||||
|
ip_ct_best = ip_ct;
|
||||||
|
zct_best = zct;
|
||||||
|
pos_end = p;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!zct_best) ip_start = iplist[pos], pos_end = pos + 1; // network not found, use single ip
|
||||||
|
|
||||||
|
u1 = ip_start >> 24;
|
||||||
|
u2 = (ip_start >> 16) & 0xFF;
|
||||||
|
u3 = (ip_start >> 8) & 0xFF;
|
||||||
|
u4 = ip_start & 0xFF;
|
||||||
|
printf(zct_best ? "%u.%u.%u.%u/%u\n" : "%u.%u.%u.%u\n", u1, u2, u3, u4, 32 - zct_best);
|
||||||
|
|
||||||
|
pos = pos_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(iplist);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
250
ip2net/qsort.c
Normal file
250
ip2net/qsort.c
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Written by Douglas C. Schmidt (schmidt@ics.uci.edu).
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with the GNU C Library; if not, see
|
||||||
|
<http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
/* If you consider tuning this algorithm, you should consult first:
|
||||||
|
Engineering a sort function; Jon Bentley and M. Douglas McIlroy;
|
||||||
|
Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */
|
||||||
|
|
||||||
|
//#include <alloca.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
//#include <string.h>
|
||||||
|
#include "qsort.h"
|
||||||
|
|
||||||
|
/* Byte-wise swap two items of size SIZE. */
|
||||||
|
#define SWAP(a, b, size) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
size_t __size = (size); \
|
||||||
|
char *__a = (a), *__b = (b); \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
char __tmp = *__a; \
|
||||||
|
*__a++ = *__b; \
|
||||||
|
*__b++ = __tmp; \
|
||||||
|
} while (--__size > 0); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Discontinue quicksort algorithm when partition gets below this size.
|
||||||
|
This particular magic number was chosen to work best on a Sun 4/260. */
|
||||||
|
#define MAX_THRESH 4
|
||||||
|
|
||||||
|
/* Stack node declarations used to store unfulfilled partition obligations. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *lo;
|
||||||
|
char *hi;
|
||||||
|
} stack_node;
|
||||||
|
|
||||||
|
/* The next 4 #defines implement a very fast in-line stack abstraction. */
|
||||||
|
/* The stack needs log (total_elements) entries (we could even subtract
|
||||||
|
log(MAX_THRESH)). Since total_elements has type size_t, we get as
|
||||||
|
upper bound for log (total_elements):
|
||||||
|
bits per byte (CHAR_BIT) * sizeof(size_t). */
|
||||||
|
#define STACK_SIZE (CHAR_BIT * sizeof(size_t))
|
||||||
|
#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
|
||||||
|
#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi)))
|
||||||
|
#define STACK_NOT_EMPTY (stack < top)
|
||||||
|
|
||||||
|
|
||||||
|
/* Order size using quicksort. This implementation incorporates
|
||||||
|
four optimizations discussed in Sedgewick:
|
||||||
|
|
||||||
|
1. Non-recursive, using an explicit stack of pointer that store the
|
||||||
|
next array partition to sort. To save time, this maximum amount
|
||||||
|
of space required to store an array of SIZE_MAX is allocated on the
|
||||||
|
stack. Assuming a 32-bit (64 bit) integer for size_t, this needs
|
||||||
|
only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes).
|
||||||
|
Pretty cheap, actually.
|
||||||
|
|
||||||
|
2. Chose the pivot element using a median-of-three decision tree.
|
||||||
|
This reduces the probability of selecting a bad pivot value and
|
||||||
|
eliminates certain extraneous comparisons.
|
||||||
|
|
||||||
|
3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
|
||||||
|
insertion sort to order the MAX_THRESH items within each partition.
|
||||||
|
This is a big win, since insertion sort is faster for small, mostly
|
||||||
|
sorted array segments.
|
||||||
|
|
||||||
|
4. The larger of the two sub-partitions is always pushed onto the
|
||||||
|
stack first, with the algorithm then concentrating on the
|
||||||
|
smaller partition. This *guarantees* no more than log (total_elems)
|
||||||
|
stack size is needed (actually O(1) in this case)! */
|
||||||
|
|
||||||
|
void
|
||||||
|
gnu_quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||||
|
__gnu_compar_d_fn_t cmp, void *arg)
|
||||||
|
{
|
||||||
|
char *base_ptr = (char *) pbase;
|
||||||
|
|
||||||
|
const size_t max_thresh = MAX_THRESH * size;
|
||||||
|
|
||||||
|
if (total_elems == 0)
|
||||||
|
/* Avoid lossage with unsigned arithmetic below. */
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (total_elems > MAX_THRESH)
|
||||||
|
{
|
||||||
|
char *lo = base_ptr;
|
||||||
|
char *hi = &lo[size * (total_elems - 1)];
|
||||||
|
stack_node stack[STACK_SIZE];
|
||||||
|
stack_node *top = stack;
|
||||||
|
|
||||||
|
PUSH (NULL, NULL);
|
||||||
|
|
||||||
|
while (STACK_NOT_EMPTY)
|
||||||
|
{
|
||||||
|
char *left_ptr;
|
||||||
|
char *right_ptr;
|
||||||
|
|
||||||
|
/* Select median value from among LO, MID, and HI. Rearrange
|
||||||
|
LO and HI so the three values are sorted. This lowers the
|
||||||
|
probability of picking a pathological pivot value and
|
||||||
|
skips a comparison for both the LEFT_PTR and RIGHT_PTR in
|
||||||
|
the while loops. */
|
||||||
|
|
||||||
|
char *mid = lo + size * ((hi - lo) / size >> 1);
|
||||||
|
|
||||||
|
if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
|
||||||
|
SWAP (mid, lo, size);
|
||||||
|
if ((*cmp) ((void *) hi, (void *) mid, arg) < 0)
|
||||||
|
SWAP (mid, hi, size);
|
||||||
|
else
|
||||||
|
goto jump_over;
|
||||||
|
if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
|
||||||
|
SWAP (mid, lo, size);
|
||||||
|
jump_over:;
|
||||||
|
|
||||||
|
left_ptr = lo + size;
|
||||||
|
right_ptr = hi - size;
|
||||||
|
|
||||||
|
/* Here's the famous ``collapse the walls'' section of quicksort.
|
||||||
|
Gotta like those tight inner loops! They are the main reason
|
||||||
|
that this algorithm runs much faster than others. */
|
||||||
|
do
|
||||||
|
{
|
||||||
|
while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
|
||||||
|
left_ptr += size;
|
||||||
|
|
||||||
|
while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
|
||||||
|
right_ptr -= size;
|
||||||
|
|
||||||
|
if (left_ptr < right_ptr)
|
||||||
|
{
|
||||||
|
SWAP (left_ptr, right_ptr, size);
|
||||||
|
if (mid == left_ptr)
|
||||||
|
mid = right_ptr;
|
||||||
|
else if (mid == right_ptr)
|
||||||
|
mid = left_ptr;
|
||||||
|
left_ptr += size;
|
||||||
|
right_ptr -= size;
|
||||||
|
}
|
||||||
|
else if (left_ptr == right_ptr)
|
||||||
|
{
|
||||||
|
left_ptr += size;
|
||||||
|
right_ptr -= size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (left_ptr <= right_ptr);
|
||||||
|
|
||||||
|
/* Set up pointers for next iteration. First determine whether
|
||||||
|
left and right partitions are below the threshold size. If so,
|
||||||
|
ignore one or both. Otherwise, push the larger partition's
|
||||||
|
bounds on the stack and continue sorting the smaller one. */
|
||||||
|
|
||||||
|
if ((size_t) (right_ptr - lo) <= max_thresh)
|
||||||
|
{
|
||||||
|
if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||||
|
/* Ignore both small partitions. */
|
||||||
|
POP (lo, hi);
|
||||||
|
else
|
||||||
|
/* Ignore small left partition. */
|
||||||
|
lo = left_ptr;
|
||||||
|
}
|
||||||
|
else if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||||
|
/* Ignore small right partition. */
|
||||||
|
hi = right_ptr;
|
||||||
|
else if ((right_ptr - lo) > (hi - left_ptr))
|
||||||
|
{
|
||||||
|
/* Push larger left partition indices. */
|
||||||
|
PUSH (lo, right_ptr);
|
||||||
|
lo = left_ptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Push larger right partition indices. */
|
||||||
|
PUSH (left_ptr, hi);
|
||||||
|
hi = right_ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Once the BASE_PTR array is partially sorted by quicksort the rest
|
||||||
|
is completely sorted using insertion sort, since this is efficient
|
||||||
|
for partitions below MAX_THRESH size. BASE_PTR points to the beginning
|
||||||
|
of the array to sort, and END_PTR points at the very last element in
|
||||||
|
the array (*not* one beyond it!). */
|
||||||
|
|
||||||
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||||
|
|
||||||
|
{
|
||||||
|
char *const end_ptr = &base_ptr[size * (total_elems - 1)];
|
||||||
|
char *tmp_ptr = base_ptr;
|
||||||
|
char *thresh = min(end_ptr, base_ptr + max_thresh);
|
||||||
|
char *run_ptr;
|
||||||
|
|
||||||
|
/* Find smallest element in first threshold and place it at the
|
||||||
|
array's beginning. This is the smallest array element,
|
||||||
|
and the operation speeds up insertion sort's inner loop. */
|
||||||
|
|
||||||
|
for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
|
||||||
|
if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
|
||||||
|
tmp_ptr = run_ptr;
|
||||||
|
|
||||||
|
if (tmp_ptr != base_ptr)
|
||||||
|
SWAP (tmp_ptr, base_ptr, size);
|
||||||
|
|
||||||
|
/* Insertion sort, running from left-hand-side up to right-hand-side. */
|
||||||
|
|
||||||
|
run_ptr = base_ptr + size;
|
||||||
|
while ((run_ptr += size) <= end_ptr)
|
||||||
|
{
|
||||||
|
tmp_ptr = run_ptr - size;
|
||||||
|
while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
|
||||||
|
tmp_ptr -= size;
|
||||||
|
|
||||||
|
tmp_ptr += size;
|
||||||
|
if (tmp_ptr != run_ptr)
|
||||||
|
{
|
||||||
|
char *trav;
|
||||||
|
|
||||||
|
trav = run_ptr + size;
|
||||||
|
while (--trav >= run_ptr)
|
||||||
|
{
|
||||||
|
char c = *trav;
|
||||||
|
char *hi, *lo;
|
||||||
|
|
||||||
|
for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
|
||||||
|
*hi = *lo;
|
||||||
|
*hi = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
ip2net/qsort.h
Normal file
6
ip2net/qsort.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// GNU qsort is 2x faster than musl
|
||||||
|
|
||||||
|
typedef int (*__gnu_compar_d_fn_t) (const void *, const void *, void *);
|
||||||
|
void gnu_quicksort (void *const pbase, size_t total_elems, size_t size, __gnu_compar_d_fn_t cmp, void *arg);
|
19
ipset/antifilter.helper
Normal file
19
ipset/antifilter.helper
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
get_antifilter()
|
||||||
|
{
|
||||||
|
# $1 - list url
|
||||||
|
# $2 - target file
|
||||||
|
local ZIPLISTTMP="$TMPDIR/zapret-ip.txt"
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" != "1" ] && {
|
||||||
|
curl --fail --max-time 150 --connect-timeout 20 --max-filesize 41943040 -k -L "$1" | cut_local >"$ZIPLISTTMP" &&
|
||||||
|
{
|
||||||
|
dlsize=$(wc -c "$ZIPLISTTMP" | cut -f 1 -d ' ')
|
||||||
|
if test $dlsize -lt 204800; then
|
||||||
|
echo list file is too small. can be bad.
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
cat "$ZIPLISTTMP" | zz "$2"
|
||||||
|
rm -f "$ZIPLISTTMP" "$2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
ipset/clear_lists.sh
Executable file
8
ipset/clear_lists.sh
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
. "$EXEDIR/def.sh"
|
||||||
|
|
||||||
|
rm -f "$ZIPLIST"* "$ZIPLIST6"* "$ZIPLIST_USER" "$ZIPLIST_USER6" "$ZIPLIST_IPBAN"* "$ZIPLIST_IPBAN6"* "$ZIPLIST_USER_IPBAN" "$ZIPLIST_USER_IPBAN6" "$ZIPLIST_EXCLUDE" "$ZIPLIST_EXCLUDE6" "$ZHOSTLIST"*
|
145
ipset/create_ipset.sh
Executable file
145
ipset/create_ipset.sh
Executable file
@ -0,0 +1,145 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# create ipset from resolved ip's
|
||||||
|
# $1=no-update - do not update ipset, only create if its absent
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
[ -z "$IPSET_OPT" ] && IPSET_OPT="hashsize 262144 maxelem 2097152"
|
||||||
|
[ -z "$IPSET_OPT_EXCLUDE" ] && IPSET_OPT_EXCLUDE="hashsize 1024 maxelem 65536"
|
||||||
|
|
||||||
|
IP2NET="$EXEDIR/../ip2net/ip2net"
|
||||||
|
|
||||||
|
. "$EXEDIR/def.sh"
|
||||||
|
IPSET_CMD="$TMPDIR/ipset_cmd.txt"
|
||||||
|
IPSET_SAVERAM_CHUNK_SIZE=20000
|
||||||
|
IPSET_SAVERAM_MIN_FILESIZE=131072
|
||||||
|
|
||||||
|
|
||||||
|
while [ -n "$1" ]; do
|
||||||
|
[ "$1" = "no-update" ] && NO_UPDATE=1
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
file_extract_lines()
|
||||||
|
{
|
||||||
|
# $1 - filename
|
||||||
|
# $2 - from line (starting with 0)
|
||||||
|
# $3 - line count
|
||||||
|
# awk "{ err=1 } NR < $(($2+1)) { next } { print; err=0 } NR == $(($2+$3)) { exit err } END {exit err}" "$1"
|
||||||
|
awk "NR < $(($2+1)) { next } { print } NR == $(($2+$3)) { exit }" "$1"
|
||||||
|
}
|
||||||
|
ipset_restore_chunked()
|
||||||
|
{
|
||||||
|
# $1 - filename
|
||||||
|
# $2 - chunk size
|
||||||
|
local pos lines
|
||||||
|
[ -f "$1" ] || return
|
||||||
|
lines=$(wc -l <"$1")
|
||||||
|
pos=$lines
|
||||||
|
while [ "$pos" -gt "0" ]; do
|
||||||
|
pos=$((pos-$2))
|
||||||
|
[ "$pos" -lt "0" ] && pos=0
|
||||||
|
file_extract_lines "$1" $pos $2 | ipset -! restore
|
||||||
|
sed -i "$(($pos+1)),$ d" "$1"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sortu()
|
||||||
|
{
|
||||||
|
sort -u
|
||||||
|
}
|
||||||
|
ip2net4()
|
||||||
|
{
|
||||||
|
"$IP2NET" -4 $IP2NET_OPT4
|
||||||
|
}
|
||||||
|
ip2net6()
|
||||||
|
{
|
||||||
|
"$IP2NET" -6 $IP2NET_OPT6
|
||||||
|
}
|
||||||
|
ipset_get_script()
|
||||||
|
{
|
||||||
|
# $1 - filename
|
||||||
|
# $2 - ipset name
|
||||||
|
# $3 - "6" = ipv6
|
||||||
|
local filter=sortu
|
||||||
|
[ -x "$IP2NET" ] && filter=ip2net$3
|
||||||
|
zzcat "$1" | $filter | sed -nre "s/^.+$/add $2 &/p"
|
||||||
|
}
|
||||||
|
|
||||||
|
ipset_restore()
|
||||||
|
{
|
||||||
|
# $1 - filename
|
||||||
|
# $2 - ipset name
|
||||||
|
# $3 - "6" = ipv6
|
||||||
|
zzexist "$1" || return
|
||||||
|
local fsize=$(zzsize "$1")
|
||||||
|
local svram=0
|
||||||
|
# do not saveram small files. file can also be gzipped
|
||||||
|
[ "$SAVERAM" = "1" ] && [ "$fsize" -ge "$IPSET_SAVERAM_MIN_FILESIZE" ] && svram=1
|
||||||
|
|
||||||
|
local T="Adding to ipset $2 ($IPSTYPE"
|
||||||
|
[ -x "$IP2NET" ] && T="$T, ip2net"
|
||||||
|
[ "$svram" = "1" ] && T="$T, saveram"
|
||||||
|
T="$T) : $f"
|
||||||
|
echo $T
|
||||||
|
|
||||||
|
if [ "$svram" = "1" ]; then
|
||||||
|
ipset_get_script "$1" "$2" "$3" >"$IPSET_CMD"
|
||||||
|
ipset_restore_chunked "$IPSET_CMD" $IPSET_SAVERAM_CHUNK_SIZE
|
||||||
|
rm -f "$IPSET_CMD"
|
||||||
|
else
|
||||||
|
ipset_get_script "$1" "$2" "$3" | ipset -! restore
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
create_ipset()
|
||||||
|
{
|
||||||
|
local IPSTYPE
|
||||||
|
if [ -x "$IP2NET" ]; then
|
||||||
|
IPSTYPE=hash:net
|
||||||
|
else
|
||||||
|
IPSTYPE=$3
|
||||||
|
fi
|
||||||
|
if [ "$1" -eq "6" ]; then
|
||||||
|
FAMILY=inet6
|
||||||
|
else
|
||||||
|
FAMILY=inet
|
||||||
|
fi
|
||||||
|
ipset create $2 $IPSTYPE $4 family $FAMILY 2>/dev/null || {
|
||||||
|
[ "$NO_UPDATE" = "1" ] && return
|
||||||
|
}
|
||||||
|
ipset flush $2
|
||||||
|
for f in "$5" "$6" ; do
|
||||||
|
ipset_restore "$f" "$2" $1
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
oom_adjust_high
|
||||||
|
|
||||||
|
# ipset seem to buffer the whole script to memory
|
||||||
|
# on low RAM system this can cause oom errors
|
||||||
|
# in SAVERAM mode we feed script lines in portions starting from the end, while truncating source file to free /tmp space
|
||||||
|
# only /tmp is considered tmpfs. other locations mean tmpdir was redirected to a disk
|
||||||
|
SAVERAM=0
|
||||||
|
[ "$TMPDIR" = "/tmp" ] && {
|
||||||
|
RAMSIZE=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
||||||
|
[ "$RAMSIZE" -lt "110000" ] && SAVERAM=1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" != "1" ] && {
|
||||||
|
create_ipset 4 $ZIPSET hash:ip "$IPSET_OPT" "$ZIPLIST" "$ZIPLIST_USER"
|
||||||
|
create_ipset 4 $ZIPSET_IPBAN hash:ip "$IPSET_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN"
|
||||||
|
create_ipset 4 $ZIPSET_EXCLUDE hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE"
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV6" != "1" ] && {
|
||||||
|
create_ipset 6 $ZIPSET6 hash:ip "$IPSET_OPT" "$ZIPLIST6" "$ZIPLIST_USER6"
|
||||||
|
create_ipset 6 $ZIPSET_IPBAN6 hash:ip "$IPSET_OPT" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
|
||||||
|
create_ipset 6 $ZIPSET_EXCLUDE6 hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6"
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
103
ipset/def.sh
Normal file
103
ipset/def.sh
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
. "$EXEDIR/../config"
|
||||||
|
|
||||||
|
[ -z "$TMPDIR" ] && TMPDIR=/tmp
|
||||||
|
ZIPSET=zapret
|
||||||
|
ZIPSET6=zapret6
|
||||||
|
ZIPSET_EXCLUDE=nozapret
|
||||||
|
ZIPSET_EXCLUDE6=nozapret6
|
||||||
|
ZIPLIST="$EXEDIR/zapret-ip.txt"
|
||||||
|
ZIPLIST6="$EXEDIR/zapret-ip6.txt"
|
||||||
|
ZIPLIST_EXCLUDE="$EXEDIR/zapret-ip-exclude.txt"
|
||||||
|
ZIPLIST_EXCLUDE6="$EXEDIR/zapret-ip-exclude6.txt"
|
||||||
|
ZIPLIST_USER="$EXEDIR/zapret-ip-user.txt"
|
||||||
|
ZIPLIST_USER6="$EXEDIR/zapret-ip-user6.txt"
|
||||||
|
ZUSERLIST="$EXEDIR/zapret-hosts-user.txt"
|
||||||
|
ZHOSTLIST="$EXEDIR/zapret-hosts.txt"
|
||||||
|
|
||||||
|
ZIPSET_IPBAN=ipban
|
||||||
|
ZIPSET_IPBAN6=ipban6
|
||||||
|
ZIPLIST_IPBAN="$EXEDIR/zapret-ip-ipban.txt"
|
||||||
|
ZIPLIST_IPBAN6="$EXEDIR/zapret-ip-ipban6.txt"
|
||||||
|
ZIPLIST_USER_IPBAN="$EXEDIR/zapret-ip-user-ipban.txt"
|
||||||
|
ZIPLIST_USER_IPBAN6="$EXEDIR/zapret-ip-user-ipban6.txt"
|
||||||
|
ZUSERLIST_IPBAN="$EXEDIR/zapret-hosts-user-ipban.txt"
|
||||||
|
ZUSERLIST_EXCLUDE="$EXEDIR/zapret-hosts-user-exclude.txt"
|
||||||
|
|
||||||
|
MDIG="$EXEDIR/../mdig/mdig"
|
||||||
|
[ -z "$MDIG_THREADS" ] && MDIG_THREADS=30
|
||||||
|
|
||||||
|
zzexist()
|
||||||
|
{
|
||||||
|
[ -f "$1.gz" ] || [ -f "$1" ]
|
||||||
|
}
|
||||||
|
zzcat()
|
||||||
|
{
|
||||||
|
if [ -f "$1.gz" ]; then
|
||||||
|
gunzip -c "$1.gz"
|
||||||
|
else
|
||||||
|
cat "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
zz()
|
||||||
|
{
|
||||||
|
gzip -c >"$1.gz"
|
||||||
|
}
|
||||||
|
zzsize()
|
||||||
|
{
|
||||||
|
local f="$1"
|
||||||
|
[ -f "$1.gz" ] && f="$1.gz"
|
||||||
|
wc -c <"$f"
|
||||||
|
}
|
||||||
|
|
||||||
|
digger()
|
||||||
|
{
|
||||||
|
# $1 - hostlist
|
||||||
|
# $2 - family (4|6)
|
||||||
|
>&2 echo digging $(wc -l <"$1") ipv$2 domains : "$1"
|
||||||
|
|
||||||
|
if [ -x "$MDIG" ]; then
|
||||||
|
zzcat "$1" | "$MDIG" --family=$2 --threads=$MDIG_THREADS --stats=1000
|
||||||
|
else
|
||||||
|
local A=A
|
||||||
|
[ "$2" = "6" ] && A=AAAA
|
||||||
|
zzcat "$1" | dig $A +short +time=8 +tries=2 -f - | grep -E '^[^;].*[^\.]$'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cut_local()
|
||||||
|
{
|
||||||
|
grep -vE '^192\.168\.|^127\.|^10\.'
|
||||||
|
}
|
||||||
|
cut_local6()
|
||||||
|
{
|
||||||
|
grep -vE '^::|fc..:|fd..:'
|
||||||
|
}
|
||||||
|
|
||||||
|
oom_adjust_high()
|
||||||
|
{
|
||||||
|
echo setting high oom kill priority
|
||||||
|
echo -n 100 >/proc/$$/oom_score_adj
|
||||||
|
}
|
||||||
|
|
||||||
|
getexclude()
|
||||||
|
{
|
||||||
|
oom_adjust_high
|
||||||
|
|
||||||
|
[ -f "$ZUSERLIST_EXCLUDE" ] && {
|
||||||
|
[ "$DISABLE_IPV4" != "1" ] && digger "$ZUSERLIST_EXCLUDE" 4 | sort -u > "$ZIPLIST_EXCLUDE"
|
||||||
|
[ "$DISABLE_IPV6" != "1" ] && digger "$ZUSERLIST_EXCLUDE" 6 | sort -u > "$ZIPLIST_EXCLUDE6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getuser()
|
||||||
|
{
|
||||||
|
getexclude
|
||||||
|
[ -f "$ZUSERLIST" ] && {
|
||||||
|
[ "$DISABLE_IPV4" != "1" ] && digger "$ZUSERLIST" 4 | cut_local | sort -u > "$ZIPLIST_USER"
|
||||||
|
[ "$DISABLE_IPV6" != "1" ] && digger "$ZUSERLIST" 6 | cut_local6 | sort -u > "$ZIPLIST_USER6"
|
||||||
|
}
|
||||||
|
[ -f "$ZUSERLIST_IPBAN" ] && {
|
||||||
|
[ "$DISABLE_IPV4" != "1" ] && digger "$ZUSERLIST_IPBAN" 4 | cut_local | sort -u > "$ZIPLIST_USER_IPBAN"
|
||||||
|
[ "$DISABLE_IPV6" != "1" ] && digger "$ZUSERLIST_IPBAN" 6 | cut_local6 | sort -u > "$ZIPLIST_USER_IPBAN6"
|
||||||
|
}
|
||||||
|
}
|
14
ipset/get_antifilter_ip.sh
Executable file
14
ipset/get_antifilter_ip.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
. "$EXEDIR/def.sh"
|
||||||
|
|
||||||
|
getuser
|
||||||
|
|
||||||
|
. "$EXEDIR/antifilter.helper"
|
||||||
|
|
||||||
|
get_antifilter https://antifilter.network/download/ip.lst "$ZIPLIST"
|
||||||
|
|
||||||
|
"$EXEDIR/create_ipset.sh"
|
14
ipset/get_antifilter_ipsmart.sh
Executable file
14
ipset/get_antifilter_ipsmart.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
. "$EXEDIR/def.sh"
|
||||||
|
|
||||||
|
getuser
|
||||||
|
|
||||||
|
. "$EXEDIR/antifilter.helper"
|
||||||
|
|
||||||
|
get_antifilter https://antifilter.network/download/ipsmart.lst "$ZIPLIST"
|
||||||
|
|
||||||
|
"$EXEDIR/create_ipset.sh"
|
14
ipset/get_antifilter_ipsum.sh
Executable file
14
ipset/get_antifilter_ipsum.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
. "$EXEDIR/def.sh"
|
||||||
|
|
||||||
|
getuser
|
||||||
|
|
||||||
|
. "$EXEDIR/antifilter.helper"
|
||||||
|
|
||||||
|
get_antifilter https://antifilter.network/download/ipsum.lst "$ZIPLIST"
|
||||||
|
|
||||||
|
"$EXEDIR/create_ipset.sh"
|
10
ipset/get_config.sh
Executable file
10
ipset/get_config.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# run script specified in config
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
. "$EXEDIR/../config"
|
||||||
|
|
||||||
|
[ -z "$GETLIST" ] && GETLIST=get_exclude.sh
|
||||||
|
[ -x "$EXEDIR/$GETLIST" ] && exec "$EXEDIR/$GETLIST"
|
11
ipset/get_exclude.sh
Executable file
11
ipset/get_exclude.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# resolve user host list
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
. "$EXEDIR/def.sh"
|
||||||
|
|
||||||
|
getexclude
|
||||||
|
|
||||||
|
"$EXEDIR/create_ipset.sh"
|
59
ipset/get_reestr_combined.sh
Executable file
59
ipset/get_reestr_combined.sh
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
. "$EXEDIR/def.sh"
|
||||||
|
|
||||||
|
ZREESTR="$TMPDIR/reestr.txt"
|
||||||
|
#ZURL_REESTR=https://reestr.rublacklist.net/api/current
|
||||||
|
ZURL_REESTR=https://raw.githubusercontent.com/zapret-info/z-i/master/dump.csv
|
||||||
|
|
||||||
|
getuser
|
||||||
|
|
||||||
|
dig_reestr()
|
||||||
|
{
|
||||||
|
# $1 - grep ipmask
|
||||||
|
# $2 - iplist
|
||||||
|
# $3 - ipban list
|
||||||
|
|
||||||
|
local DOMMASK='^.*;[^ ;:/]+\.[^ ;:/]+;'
|
||||||
|
local TMP="$TMPDIR/tmp.txt"
|
||||||
|
# find entries with https or without domain name - they should be banned by IP
|
||||||
|
# 2971-18 is TELEGRAM. lots of proxy IPs banned, list grows very large
|
||||||
|
(grep -avE "$DOMMASK" "$ZREESTR" ; grep -a "https://" "$ZREESTR") |
|
||||||
|
grep -av "2971-18" |
|
||||||
|
grep -oE "$1" | cut_local | sort -u >$TMP
|
||||||
|
|
||||||
|
cat "$TMP" | zz "$3"
|
||||||
|
|
||||||
|
# other IPs go to regular zapret list
|
||||||
|
grep -av "2971-18" "$ZREESTR" | grep -oE "$1" | cut_local | grep -xvFf "$TMP" | sort -u | zz "$2"
|
||||||
|
|
||||||
|
rm -f "$TMP"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
curl -k --fail --max-time 150 --connect-timeout 5 --retry 3 --max-filesize 251658240 "$ZURL_REESTR" -o "$ZREESTR" ||
|
||||||
|
{
|
||||||
|
echo reestr list download failed
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
dlsize=$(wc -c "$ZREESTR" | cut -f 1 -d ' ')
|
||||||
|
if test $dlsize -lt 1048576; then
|
||||||
|
echo reestr ip list is too small. can be bad.
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
#sed -i 's/\\n/\r\n/g' $ZREESTR
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" != "1" ] && {
|
||||||
|
dig_reestr '([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]+)?' "$ZIPLIST" "$ZIPLIST_IPBAN"
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV6" != "1" ] && {
|
||||||
|
dig_reestr '([0-9,a-f,A-F]{1,4}:){7}[0-9,a-f,A-F]{1,4}(/[0-9]+)?' "$ZIPLIST6" "$ZIPLIST_IPBAN6"
|
||||||
|
}
|
||||||
|
|
||||||
|
rm -f "$ZREESTR"
|
||||||
|
|
||||||
|
"$EXEDIR/create_ipset.sh"
|
33
ipset/get_reestr_hostlist.sh
Executable file
33
ipset/get_reestr_hostlist.sh
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
. "$EXEDIR/def.sh"
|
||||||
|
|
||||||
|
# useful in case ipban set is used in custom scripts
|
||||||
|
getuser
|
||||||
|
"$EXEDIR/create_ipset.sh"
|
||||||
|
|
||||||
|
ZREESTR="$TMPDIR/zapret.txt"
|
||||||
|
#ZURL=https://reestr.rublacklist.net/api/current
|
||||||
|
ZURL=https://raw.githubusercontent.com/zapret-info/z-i/master/dump.csv
|
||||||
|
|
||||||
|
curl -k --fail --max-time 150 --connect-timeout 5 --retry 3 --max-filesize 251658240 "$ZURL" >"$ZREESTR" ||
|
||||||
|
{
|
||||||
|
echo reestr list download failed
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
dlsize=$(wc -c "$ZREESTR" | cut -f 1 -d ' ')
|
||||||
|
if test $dlsize -lt 204800; then
|
||||||
|
echo list file is too small. can be bad.
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
(cut -s -f2 -d';' "$ZREESTR" | grep -a . | sed -re 's/^\*\.(.+)$/\1/' | awk '{ print tolower($0) }' ; cat "$ZUSERLIST" ) | sort -u | zz "$ZHOSTLIST"
|
||||||
|
rm -f "$ZREESTR"
|
||||||
|
|
||||||
|
# force daemons to reload hostlist if they are running
|
||||||
|
killall -HUP tpws 2>/dev/null
|
||||||
|
killall -HUP nfqws 2>/dev/null
|
||||||
|
|
||||||
|
exit 0
|
47
ipset/get_reestr_ip.sh
Executable file
47
ipset/get_reestr_ip.sh
Executable file
@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
. "$EXEDIR/def.sh"
|
||||||
|
|
||||||
|
ZREESTR="$TMPDIR/reestr.txt"
|
||||||
|
#ZURL_REESTR=https://reestr.rublacklist.net/api/current
|
||||||
|
ZURL_REESTR=https://raw.githubusercontent.com/zapret-info/z-i/master/dump.csv
|
||||||
|
|
||||||
|
getuser
|
||||||
|
|
||||||
|
dig_reestr()
|
||||||
|
{
|
||||||
|
# $1 - grep ipmask
|
||||||
|
# $2 - iplist
|
||||||
|
|
||||||
|
# 2971-18 is TELEGRAM. lots of proxy IPs banned, list grows very large
|
||||||
|
grep -av "2971-18" "$ZREESTR" | grep -oE "$1" | cut_local | sort -u | zz "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# assume all https banned by ip
|
||||||
|
curl -k --fail --max-time 150 --connect-timeout 5 --retry 3 --max-filesize 251658240 "$ZURL_REESTR" -o "$ZREESTR" ||
|
||||||
|
{
|
||||||
|
echo reestr list download failed
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
dlsize=$(wc -c "$ZREESTR" | cut -f 1 -d ' ')
|
||||||
|
if test $dlsize -lt 1048576; then
|
||||||
|
echo reestr ip list is too small. can be bad.
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
#sed -i 's/\\n/\r\n/g' $ZREESTR
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" != "1" ] && {
|
||||||
|
dig_reestr '([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]+)?' "$ZIPLIST"
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV6" != "1" ] && {
|
||||||
|
dig_reestr '([0-9,a-f,A-F]{1,4}:){7}[0-9,a-f,A-F]{1,4}(/[0-9]+)?' "$ZIPLIST6"
|
||||||
|
}
|
||||||
|
|
||||||
|
rm -f "$ZREESTR"
|
||||||
|
|
||||||
|
"$EXEDIR/create_ipset.sh"
|
54
ipset/get_reestr_resolve.sh
Executable file
54
ipset/get_reestr_resolve.sh
Executable file
@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
. "$EXEDIR/def.sh"
|
||||||
|
|
||||||
|
ZREESTR="$TMPDIR/zapret.txt"
|
||||||
|
ZDIG="$TMPDIR/zapret-dig.txt"
|
||||||
|
ZIPLISTTMP="$TMPDIR/zapret-ip.txt"
|
||||||
|
#ZURL=https://reestr.rublacklist.net/api/current
|
||||||
|
ZURL=https://raw.githubusercontent.com/zapret-info/z-i/master/dump.csv
|
||||||
|
|
||||||
|
getuser
|
||||||
|
|
||||||
|
# both disabled
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] && [ "$DISABLE_IPV6" = "1" ] && exit 0
|
||||||
|
|
||||||
|
curl -k --fail --max-time 150 --connect-timeout 5 --retry 3 --max-filesize 251658240 "$ZURL" >"$ZREESTR" ||
|
||||||
|
{
|
||||||
|
echo reestr list download failed
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
dlsize=$(wc -c "$ZREESTR" | cut -f 1 -d ' ')
|
||||||
|
if test $dlsize -lt 204800; then
|
||||||
|
echo list file is too small. can be bad.
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
echo preparing dig list ..
|
||||||
|
#sed -i 's/\\n/\r\n/g' $ZREESTR
|
||||||
|
#sed -nre 's/^[^;]*;([^;|\\]{4,250})\;.*$/\1/p' $ZREESTR | sort | uniq >$ZDIG
|
||||||
|
cut -f2 -d ';' "$ZREESTR" | grep -avE '^$|\*|:' >"$ZDIG"
|
||||||
|
rm -f "$ZREESTR"
|
||||||
|
|
||||||
|
echo digging started. this can take long ...
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" != "1" ] && {
|
||||||
|
digger "$ZDIG" 4 | cut_local >"$ZIPLISTTMP" || {
|
||||||
|
rm -f "$ZDIG"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
sort -u "$ZIPLISTTMP" | zz "$ZIPLIST"
|
||||||
|
rm -f "$ZIPLISTTMP"
|
||||||
|
}
|
||||||
|
[ "$DISABLE_IPV6" != "1" ] && {
|
||||||
|
digger "$ZDIG" 6 | cut_local6 >"$ZIPLISTTMP" || {
|
||||||
|
rm -f "$ZDIG"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
sort -u "$ZIPLISTTMP" | zz "$ZIPLIST6"
|
||||||
|
rm -f "$ZIPLISTTMP"
|
||||||
|
}
|
||||||
|
rm -f "$ZDIG"
|
||||||
|
"$EXEDIR/create_ipset.sh"
|
11
ipset/get_user.sh
Executable file
11
ipset/get_user.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# resolve user host list
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
. "$EXEDIR/def.sh"
|
||||||
|
|
||||||
|
getuser
|
||||||
|
|
||||||
|
"$EXEDIR/create_ipset.sh"
|
6
ipset/zapret-hosts-user-exclude.txt
Normal file
6
ipset/zapret-hosts-user-exclude.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
10.0.0.0/8
|
||||||
|
172.16.0.0/12
|
||||||
|
192.168.0.0/16
|
||||||
|
169.254.0.0/16
|
||||||
|
fc00::/7
|
||||||
|
fe80::/10
|
2
ipset/zapret-hosts-user-ipban.txt
Normal file
2
ipset/zapret-hosts-user-ipban.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
kinozal.tv
|
||||||
|
rutracker.org
|
3
ipset/zapret-hosts-user.txt
Normal file
3
ipset/zapret-hosts-user.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
st.kinozal.tv
|
||||||
|
s.kinozal.tv
|
||||||
|
putinhuylo.com
|
12
mdig/Makefile
Normal file
12
mdig/Makefile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
CC ?= gcc
|
||||||
|
CFLAGS += -std=c99 -s -O3
|
||||||
|
LIBS = -lpthread
|
||||||
|
SRC_FILES = *.c
|
||||||
|
|
||||||
|
all: mdig
|
||||||
|
|
||||||
|
mdig: $(SRC_FILES)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f mdig *.o
|
337
mdig/mdig.c
Normal file
337
mdig/mdig.c
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
// multi thread dns resolver
|
||||||
|
// domain list <stdin
|
||||||
|
// ip list >stdout
|
||||||
|
// errors, verbose >stderr
|
||||||
|
// transparent for valid ip or ip/subnet of allowed address family
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#define RESOLVER_EAGAIN_ATTEMPTS 2
|
||||||
|
|
||||||
|
static void trimstr(char *s)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
for (p = s + strlen(s) - 1; p >= s && (*p == '\n' || *p == '\r'); p--) *p = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* eai_str(int r)
|
||||||
|
{
|
||||||
|
switch (r)
|
||||||
|
{
|
||||||
|
case EAI_NONAME:
|
||||||
|
return "EAI_NONAME";
|
||||||
|
case EAI_AGAIN:
|
||||||
|
return "EAI_AGAIN";
|
||||||
|
case EAI_ADDRFAMILY:
|
||||||
|
return "EAI_ADDRFAMILY";
|
||||||
|
case EAI_BADFLAGS:
|
||||||
|
return "EAI_BADFLAGS";
|
||||||
|
case EAI_FAIL:
|
||||||
|
return "EAI_FAIL";
|
||||||
|
case EAI_MEMORY:
|
||||||
|
return "EAI_MEMORY";
|
||||||
|
case EAI_FAMILY:
|
||||||
|
return "EAI_FAMILY";
|
||||||
|
case EAI_NODATA:
|
||||||
|
return "EAI_NODATA";
|
||||||
|
case EAI_SERVICE:
|
||||||
|
return "EAI_SERVICE";
|
||||||
|
case EAI_SOCKTYPE:
|
||||||
|
return "EAI_SOCKTYPE";
|
||||||
|
case EAI_SYSTEM:
|
||||||
|
return "EAI_SYSTEM";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FAMILY4 1
|
||||||
|
#define FAMILY6 2
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
char verbose;
|
||||||
|
char family;
|
||||||
|
int threads;
|
||||||
|
pthread_mutex_t flock;
|
||||||
|
pthread_mutex_t slock; // stats lock
|
||||||
|
int stats_every,stats_ct,stats_ct_ok; // stats
|
||||||
|
} glob;
|
||||||
|
|
||||||
|
// get next domain. return 0 if failure
|
||||||
|
static char interlocked_get_dom(char *dom, size_t size)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
pthread_mutex_lock(&glob.flock);
|
||||||
|
s = fgets(dom, size, stdin);
|
||||||
|
pthread_mutex_unlock(&glob.flock);
|
||||||
|
if (!s) return 0;
|
||||||
|
trimstr(s);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
static void interlocked_fprintf(FILE *stream, const char * format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
pthread_mutex_lock(&glob.flock);
|
||||||
|
vfprintf(stream, format, args);
|
||||||
|
pthread_mutex_unlock(&glob.flock);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ELOG(format, ...) interlocked_fprintf(stderr, "[%d] " format "\n", tid, ##__VA_ARGS__)
|
||||||
|
#define VLOG(format, ...) {if (glob.verbose) ELOG(format, ##__VA_ARGS__);}
|
||||||
|
|
||||||
|
static void print_addrinfo(struct addrinfo *ai)
|
||||||
|
{
|
||||||
|
char str[64];
|
||||||
|
while (ai)
|
||||||
|
{
|
||||||
|
switch (ai->ai_family)
|
||||||
|
{
|
||||||
|
case AF_INET:
|
||||||
|
if (inet_ntop(ai->ai_family, &((struct sockaddr_in*)ai->ai_addr)->sin_addr, str, sizeof(str)))
|
||||||
|
interlocked_fprintf(stdout, "%s\n", str);
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
if (inet_ntop(ai->ai_family, &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr, str, sizeof(str)))
|
||||||
|
interlocked_fprintf(stdout, "%s\n", str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ai = ai->ai_next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stat_print(int ct, int ct_ok)
|
||||||
|
{
|
||||||
|
if (glob.stats_every > 0)
|
||||||
|
interlocked_fprintf(stderr, "mdig stats : domains=%d success=%d error=%d\n", ct, ct_ok, ct-ct_ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stat_plus(char is_ok)
|
||||||
|
{
|
||||||
|
int ct,ct_ok;
|
||||||
|
if (glob.stats_every > 0)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&glob.slock);
|
||||||
|
ct = ++glob.stats_ct;
|
||||||
|
ct_ok = glob.stats_ct_ok+=!!is_ok;
|
||||||
|
pthread_mutex_unlock(&glob.slock);
|
||||||
|
|
||||||
|
if (!(ct % glob.stats_every)) stat_print(ct,ct_ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t GetAddrFamily(const char *saddr)
|
||||||
|
{
|
||||||
|
struct in_addr a4;
|
||||||
|
struct in6_addr a6;
|
||||||
|
|
||||||
|
if (inet_pton(AF_INET, saddr, &a4))
|
||||||
|
return AF_INET;
|
||||||
|
else if (inet_pton(AF_INET6, saddr, &a6))
|
||||||
|
return AF_INET6;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *t_resolver(void *arg)
|
||||||
|
{
|
||||||
|
int tid = (int)(size_t)arg;
|
||||||
|
int i,r;
|
||||||
|
char dom[256],is_ok;
|
||||||
|
struct addrinfo hints;
|
||||||
|
struct addrinfo *result;
|
||||||
|
|
||||||
|
VLOG("started");
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
|
hints.ai_family = (glob.family == FAMILY4) ? AF_INET : (glob.family == FAMILY6) ? AF_INET6 : AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
|
||||||
|
while (interlocked_get_dom(dom, sizeof(dom)))
|
||||||
|
{
|
||||||
|
if (*dom)
|
||||||
|
{
|
||||||
|
uint16_t family;
|
||||||
|
char *s_mask,s_ip[sizeof(dom)];
|
||||||
|
|
||||||
|
is_ok=0;
|
||||||
|
|
||||||
|
strncpy(s_ip,dom,sizeof(s_ip));
|
||||||
|
s_mask=strchr(s_ip,'/');
|
||||||
|
if (s_mask) *s_mask++=0;
|
||||||
|
family=GetAddrFamily(s_ip);
|
||||||
|
if (family)
|
||||||
|
{
|
||||||
|
if (family==AF_INET && (glob.family & FAMILY4) || family==AF_INET6 && (glob.family & FAMILY6))
|
||||||
|
{
|
||||||
|
unsigned int mask;
|
||||||
|
bool mask_needed=false;
|
||||||
|
if (s_mask)
|
||||||
|
{
|
||||||
|
if (sscanf(s_mask,"%u",&mask))
|
||||||
|
{
|
||||||
|
switch(family)
|
||||||
|
{
|
||||||
|
case AF_INET: is_ok=mask<=32; mask_needed=mask<32; break;
|
||||||
|
case AF_INET6: is_ok=mask<=128; mask_needed=mask<128; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
is_ok = 1;
|
||||||
|
if (is_ok)
|
||||||
|
interlocked_fprintf(stdout, mask_needed ? "%s/%u\n" : "%s\n", s_ip, mask);
|
||||||
|
else
|
||||||
|
VLOG("bad ip/subnet %s", dom);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
VLOG("wrong address family %s", s_ip);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VLOG("resolving %s", dom);
|
||||||
|
for (i = 0; i < RESOLVER_EAGAIN_ATTEMPTS; i++)
|
||||||
|
{
|
||||||
|
if (r = getaddrinfo(dom, NULL, &hints, &result))
|
||||||
|
{
|
||||||
|
VLOG("failed to resolve %s : result %d (%s)", dom, r, eai_str(r));
|
||||||
|
if (r == EAI_AGAIN) continue; // temporary failure. should retry
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print_addrinfo(result);
|
||||||
|
freeaddrinfo(result);
|
||||||
|
is_ok=1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stat_plus(is_ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VLOG("ended");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run_threads()
|
||||||
|
{
|
||||||
|
int i, thread;
|
||||||
|
pthread_t *t;
|
||||||
|
|
||||||
|
glob.stats_ct=glob.stats_ct_ok=0;
|
||||||
|
if (pthread_mutex_init(&glob.flock, NULL) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "mutex init failed\n");
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
if (pthread_mutex_init(&glob.slock, NULL) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "mutex init failed\n");
|
||||||
|
pthread_mutex_destroy(&glob.flock);
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
t = (pthread_t*)malloc(sizeof(pthread_t)*glob.threads);
|
||||||
|
if (!t)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
pthread_mutex_destroy(&glob.slock);
|
||||||
|
pthread_mutex_destroy(&glob.flock);
|
||||||
|
return 11;
|
||||||
|
}
|
||||||
|
for (thread = 0; thread < glob.threads; thread++)
|
||||||
|
{
|
||||||
|
if (pthread_create(t + thread, NULL, t_resolver, (void*)(size_t)thread))
|
||||||
|
{
|
||||||
|
interlocked_fprintf(stderr, "failed to create thread #%d\n", thread);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < thread; i++)
|
||||||
|
{
|
||||||
|
pthread_join(t[i], NULL);
|
||||||
|
}
|
||||||
|
free(t);
|
||||||
|
stat_print(glob.stats_ct,glob.stats_ct_ok);
|
||||||
|
pthread_mutex_destroy(&glob.slock);
|
||||||
|
pthread_mutex_destroy(&glob.flock);
|
||||||
|
return thread ? 0 : 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exithelp()
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
" --threads=<threads_number>\n"
|
||||||
|
" --family=<4|6|46>\t; ipv4, ipv6, ipv4+ipv6\n"
|
||||||
|
" --verbose\t\t; print query progress to stderr\n"
|
||||||
|
" --stats=N\t\t; print resolve stats to stderr every N domains\n"
|
||||||
|
);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int ret, v, option_index = 0;
|
||||||
|
|
||||||
|
static const struct option long_options[] = {
|
||||||
|
{"threads",required_argument,0,0}, // optidx=0
|
||||||
|
{"family",required_argument,0,0}, // optidx=1
|
||||||
|
{"verbose",no_argument,0,0}, // optidx=2
|
||||||
|
{"stats",required_argument,0,0}, // optidx=3
|
||||||
|
{"help",no_argument,0,0}, // optidx=4
|
||||||
|
{NULL,0,NULL,0}
|
||||||
|
};
|
||||||
|
|
||||||
|
memset(&glob, 0, sizeof(glob));
|
||||||
|
glob.family = FAMILY4;
|
||||||
|
glob.threads = 1;
|
||||||
|
while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1)
|
||||||
|
{
|
||||||
|
if (v) exithelp();
|
||||||
|
switch (option_index)
|
||||||
|
{
|
||||||
|
case 0: /* threads */
|
||||||
|
glob.threads = optarg ? atoi(optarg) : 0;
|
||||||
|
if (glob.threads <= 0 || glob.threads > 100)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "thread number must be within 1..100\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1: /* family */
|
||||||
|
if (!strcmp(optarg, "4"))
|
||||||
|
glob.family = FAMILY4;
|
||||||
|
else if (!strcmp(optarg, "6"))
|
||||||
|
glob.family = FAMILY6;
|
||||||
|
else if (!strcmp(optarg, "46"))
|
||||||
|
glob.family = FAMILY4 | FAMILY6;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ip family must be 4,6 or 46\n");
|
||||||
|
return 1;;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: /* verbose */
|
||||||
|
glob.verbose = '\1';
|
||||||
|
break;
|
||||||
|
glob.threads = optarg ? atoi(optarg) : 0;
|
||||||
|
case 3: /* stats */
|
||||||
|
glob.stats_every = optarg ? atoi(optarg) : 0;
|
||||||
|
break;
|
||||||
|
case 4: /* help */
|
||||||
|
exithelp();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return run_threads();
|
||||||
|
}
|
12
nfq/Makefile
Normal file
12
nfq/Makefile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
CC ?= gcc
|
||||||
|
CFLAGS += -std=c99 -s -O3
|
||||||
|
LIBS = -lnetfilter_queue -lnfnetlink -lcap -lz
|
||||||
|
SRC_FILES = *.c
|
||||||
|
|
||||||
|
all: nfqws
|
||||||
|
|
||||||
|
nfqws: $(SRC_FILES)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f nfqws *.o
|
312
nfq/darkmagic.c
Normal file
312
nfq/darkmagic.c
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
#include "darkmagic.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
uint16_t tcp_checksum(const void *buff, int len, in_addr_t src_addr, in_addr_t dest_addr)
|
||||||
|
{
|
||||||
|
const uint16_t *buf=buff;
|
||||||
|
uint16_t *ip_src=(uint16_t *)&src_addr, *ip_dst=(uint16_t *)&dest_addr;
|
||||||
|
uint32_t sum;
|
||||||
|
int length=len;
|
||||||
|
|
||||||
|
// Calculate the sum
|
||||||
|
sum = 0;
|
||||||
|
while (len > 1)
|
||||||
|
{
|
||||||
|
sum += *buf++;
|
||||||
|
if (sum & 0x80000000)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
len -= 2;
|
||||||
|
}
|
||||||
|
if ( len & 1 )
|
||||||
|
{
|
||||||
|
// Add the padding if the packet lenght is odd
|
||||||
|
uint16_t v=0;
|
||||||
|
*(uint8_t *)&v = *((uint8_t *)buf);
|
||||||
|
sum += v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the pseudo-header
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *ip_src;
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *ip_dst;
|
||||||
|
sum += htons(IPPROTO_TCP);
|
||||||
|
sum += htons(length);
|
||||||
|
|
||||||
|
// Add the carries
|
||||||
|
while (sum >> 16)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
|
||||||
|
// Return the one's complement of sum
|
||||||
|
return (uint16_t)(~sum);
|
||||||
|
}
|
||||||
|
void tcp_fix_checksum(struct tcphdr *tcp,int len, in_addr_t src_addr, in_addr_t dest_addr)
|
||||||
|
{
|
||||||
|
tcp->check = 0;
|
||||||
|
tcp->check = tcp_checksum(tcp,len,src_addr,dest_addr);
|
||||||
|
}
|
||||||
|
uint16_t tcp6_checksum(const void *buff, int len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr)
|
||||||
|
{
|
||||||
|
const uint16_t *buf=buff;
|
||||||
|
const uint16_t *ip_src=(uint16_t *)src_addr, *ip_dst=(uint16_t *)dest_addr;
|
||||||
|
uint32_t sum;
|
||||||
|
int length=len;
|
||||||
|
|
||||||
|
// Calculate the sum
|
||||||
|
sum = 0;
|
||||||
|
while (len > 1)
|
||||||
|
{
|
||||||
|
sum += *buf++;
|
||||||
|
if (sum & 0x80000000)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
len -= 2;
|
||||||
|
}
|
||||||
|
if ( len & 1 )
|
||||||
|
{
|
||||||
|
// Add the padding if the packet lenght is odd
|
||||||
|
uint16_t v=0;
|
||||||
|
*(uint8_t *)&v = *((uint8_t *)buf);
|
||||||
|
sum += v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the pseudo-header
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *ip_src;
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *ip_dst;
|
||||||
|
sum += htons(IPPROTO_TCP);
|
||||||
|
sum += htons(length);
|
||||||
|
|
||||||
|
// Add the carries
|
||||||
|
while (sum >> 16)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
|
||||||
|
// Return the one's complement of sum
|
||||||
|
return (uint16_t)(~sum);
|
||||||
|
}
|
||||||
|
void tcp6_fix_checksum(struct tcphdr *tcp,int len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr)
|
||||||
|
{
|
||||||
|
tcp->check = 0;
|
||||||
|
tcp->check = tcp6_checksum(tcp,len,src_addr,dest_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void fill_tcphdr(struct tcphdr *tcp, uint8_t tcp_flags, uint32_t seq, uint32_t ack_seq, enum tcp_fooling_mode fooling, uint16_t nsport, uint16_t ndport)
|
||||||
|
{
|
||||||
|
char *tcpopt = (char*)(tcp+1);
|
||||||
|
memset(tcp,0,sizeof(*tcp));
|
||||||
|
tcp->source = nsport;
|
||||||
|
tcp->dest = ndport;
|
||||||
|
tcp->seq = seq;
|
||||||
|
tcp->ack_seq = ack_seq;
|
||||||
|
tcp->doff = 5;
|
||||||
|
*((uint8_t*)tcp+13)= tcp_flags;
|
||||||
|
tcp->window = htons(65535);
|
||||||
|
if (fooling==TCP_FOOL_MD5SIG)
|
||||||
|
{
|
||||||
|
tcp->doff += 5; // +20 bytes
|
||||||
|
tcpopt[0] = 19; // kind
|
||||||
|
tcpopt[1] = 18; // len
|
||||||
|
*(uint32_t*)(tcpopt+2)=random();
|
||||||
|
*(uint32_t*)(tcpopt+6)=random();
|
||||||
|
*(uint32_t*)(tcpopt+10)=random();
|
||||||
|
*(uint32_t*)(tcpopt+14)=random();
|
||||||
|
tcpopt[18] = 0; // end
|
||||||
|
tcpopt[19] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rawsend_sock=-1;
|
||||||
|
void rawsend_cleanup()
|
||||||
|
{
|
||||||
|
if (rawsend_sock!=-1)
|
||||||
|
{
|
||||||
|
close(rawsend_sock);
|
||||||
|
rawsend_sock=-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void rawsend_socket(int family,uint32_t fwmark)
|
||||||
|
{
|
||||||
|
if (rawsend_sock==-1)
|
||||||
|
{
|
||||||
|
int yes=1,pri=6;
|
||||||
|
rawsend_sock = socket(family, SOCK_RAW, IPPROTO_RAW);
|
||||||
|
if (rawsend_sock==-1)
|
||||||
|
perror("rawsend: socket()");
|
||||||
|
else if (setsockopt(rawsend_sock, SOL_SOCKET, SO_MARK, &fwmark, sizeof(fwmark)) == -1)
|
||||||
|
{
|
||||||
|
perror("rawsend: setsockopt(SO_MARK)");
|
||||||
|
rawsend_cleanup();
|
||||||
|
}
|
||||||
|
else if (setsockopt(rawsend_sock, SOL_SOCKET, SO_PRIORITY, &pri, sizeof(pri)) == -1)
|
||||||
|
{
|
||||||
|
perror("rawsend: setsockopt(SO_PRIORITY)");
|
||||||
|
rawsend_cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool rawsend(struct sockaddr* dst,uint32_t fwmark,const void *data,size_t len)
|
||||||
|
{
|
||||||
|
rawsend_socket(dst->sa_family,fwmark);
|
||||||
|
if (rawsend_sock==-1) return false;
|
||||||
|
|
||||||
|
int salen = dst->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
|
||||||
|
struct sockaddr_storage dst2;
|
||||||
|
memcpy(&dst2,dst,salen);
|
||||||
|
if (dst->sa_family==AF_INET6)
|
||||||
|
((struct sockaddr_in6 *)&dst2)->sin6_port = 0; // or will be EINVAL
|
||||||
|
|
||||||
|
int bytes = sendto(rawsend_sock, data, len, 0, (struct sockaddr*)&dst2, salen);
|
||||||
|
if (bytes==-1)
|
||||||
|
{
|
||||||
|
perror("rawsend: sendto");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool prepare_tcp_segment4(
|
||||||
|
const struct sockaddr_in *src, const struct sockaddr_in *dst,
|
||||||
|
uint8_t tcp_flags,
|
||||||
|
uint32_t seq, uint32_t ack_seq,
|
||||||
|
uint8_t ttl,
|
||||||
|
enum tcp_fooling_mode fooling,
|
||||||
|
const void *data, uint16_t len,
|
||||||
|
char *buf, size_t *buflen)
|
||||||
|
{
|
||||||
|
uint16_t tcpoptlen = 0;
|
||||||
|
if (fooling==TCP_FOOL_MD5SIG) tcpoptlen=20;
|
||||||
|
uint16_t pktlen = sizeof(struct iphdr) + sizeof(struct tcphdr) + tcpoptlen + len;
|
||||||
|
if (pktlen>*buflen)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"prepare_tcp_segment : packet len cannot exceed %zu\n",*buflen);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iphdr *ip = (struct iphdr*) buf;
|
||||||
|
struct tcphdr *tcp = (struct tcphdr*) (ip+1);
|
||||||
|
|
||||||
|
ip->frag_off = 0;
|
||||||
|
ip->version = 4;
|
||||||
|
ip->ihl = 5;
|
||||||
|
ip->tot_len = htons(pktlen);
|
||||||
|
ip->id = 0;
|
||||||
|
ip->ttl = ttl;
|
||||||
|
ip->protocol = IPPROTO_TCP;
|
||||||
|
ip->saddr = src->sin_addr.s_addr;
|
||||||
|
ip->daddr = dst->sin_addr.s_addr;
|
||||||
|
|
||||||
|
fill_tcphdr(tcp,tcp_flags,seq,ack_seq,fooling,src->sin_port,dst->sin_port);
|
||||||
|
|
||||||
|
memcpy((char*)tcp+sizeof(struct tcphdr)+tcpoptlen,data,len);
|
||||||
|
tcp_fix_checksum(tcp,sizeof(struct tcphdr)+tcpoptlen+len,ip->saddr,ip->daddr);
|
||||||
|
if (fooling==TCP_FOOL_BADSUM) tcp->check^=0xBEAF;
|
||||||
|
|
||||||
|
*buflen = pktlen;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool prepare_tcp_segment6(
|
||||||
|
const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst,
|
||||||
|
uint8_t tcp_flags,
|
||||||
|
uint32_t seq, uint32_t ack_seq,
|
||||||
|
uint8_t ttl,
|
||||||
|
enum tcp_fooling_mode fooling,
|
||||||
|
const void *data, uint16_t len,
|
||||||
|
char *buf, size_t *buflen)
|
||||||
|
{
|
||||||
|
uint16_t tcpoptlen = 0;
|
||||||
|
if (fooling==TCP_FOOL_MD5SIG) tcpoptlen=20;
|
||||||
|
uint16_t payloadlen = sizeof(struct tcphdr) + tcpoptlen + len;
|
||||||
|
uint16_t pktlen = sizeof(struct ip6_hdr) + payloadlen;
|
||||||
|
if (pktlen>*buflen)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"prepare_tcp_segment : packet len cannot exceed %zu\n",*buflen);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ip6_hdr *ip6 = (struct ip6_hdr*) buf;
|
||||||
|
struct tcphdr *tcp = (struct tcphdr*) (ip6+1);
|
||||||
|
|
||||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_flow = htonl(0x60000000);
|
||||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(payloadlen);
|
||||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_TCP;
|
||||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = ttl;
|
||||||
|
ip6->ip6_src = src->sin6_addr;
|
||||||
|
ip6->ip6_dst = dst->sin6_addr;
|
||||||
|
|
||||||
|
fill_tcphdr(tcp,tcp_flags,seq,ack_seq,fooling,src->sin6_port,dst->sin6_port);
|
||||||
|
|
||||||
|
memcpy((char*)tcp+sizeof(struct tcphdr)+tcpoptlen,data,len);
|
||||||
|
tcp6_fix_checksum(tcp,sizeof(struct tcphdr)+tcpoptlen+len,&ip6->ip6_src,&ip6->ip6_dst);
|
||||||
|
if (fooling==TCP_FOOL_BADSUM) tcp->check^=0xBEAF;
|
||||||
|
|
||||||
|
*buflen = pktlen;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool prepare_tcp_segment(
|
||||||
|
const struct sockaddr *src, const struct sockaddr *dst,
|
||||||
|
uint8_t tcp_flags,
|
||||||
|
uint32_t seq, uint32_t ack_seq,
|
||||||
|
uint8_t ttl,
|
||||||
|
enum tcp_fooling_mode fooling,
|
||||||
|
const void *data, uint16_t len,
|
||||||
|
char *buf, size_t *buflen)
|
||||||
|
{
|
||||||
|
return (src->sa_family==AF_INET && dst->sa_family==AF_INET) ?
|
||||||
|
prepare_tcp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,tcp_flags,seq,ack_seq,ttl,fooling,data,len,buf,buflen) :
|
||||||
|
(src->sa_family==AF_INET6 && dst->sa_family==AF_INET6) ?
|
||||||
|
prepare_tcp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,tcp_flags,seq,ack_seq,ttl,fooling,data,len,buf,buflen) :
|
||||||
|
false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void extract_endpoints(const struct iphdr *iphdr,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst)
|
||||||
|
{
|
||||||
|
if (iphdr)
|
||||||
|
{
|
||||||
|
struct sockaddr_in *si = (struct sockaddr_in*)dst;
|
||||||
|
si->sin_family = AF_INET;
|
||||||
|
si->sin_port = tcphdr->dest;
|
||||||
|
si->sin_addr.s_addr = iphdr->daddr;
|
||||||
|
|
||||||
|
si = (struct sockaddr_in*)src;
|
||||||
|
si->sin_family = AF_INET;
|
||||||
|
si->sin_port = tcphdr->source;
|
||||||
|
si->sin_addr.s_addr = iphdr->saddr;
|
||||||
|
}
|
||||||
|
else if (ip6hdr)
|
||||||
|
{
|
||||||
|
struct sockaddr_in6 *si = (struct sockaddr_in6*)dst;
|
||||||
|
si->sin6_family = AF_INET6;
|
||||||
|
si->sin6_port = tcphdr->dest;
|
||||||
|
si->sin6_addr = ip6hdr->ip6_dst;
|
||||||
|
si->sin6_flowinfo = 0;
|
||||||
|
si->sin6_scope_id = 0;
|
||||||
|
|
||||||
|
si = (struct sockaddr_in6*)src;
|
||||||
|
si->sin6_family = AF_INET6;
|
||||||
|
si->sin6_port = tcphdr->source;
|
||||||
|
si->sin6_addr = ip6hdr->ip6_src;
|
||||||
|
si->sin6_flowinfo = 0;
|
||||||
|
si->sin6_scope_id = 0;
|
||||||
|
}
|
||||||
|
}
|
51
nfq/darkmagic.h
Normal file
51
nfq/darkmagic.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/ip6.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
uint16_t tcp_checksum(const void *buff, int len, in_addr_t src_addr, in_addr_t dest_addr);
|
||||||
|
void tcp_fix_checksum(struct tcphdr *tcp,int len, in_addr_t src_addr, in_addr_t dest_addr);
|
||||||
|
uint16_t tcp6_checksum(const void *buff, int len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr);
|
||||||
|
void tcp6_fix_checksum(struct tcphdr *tcp,int len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr);
|
||||||
|
|
||||||
|
enum tcp_fooling_mode {
|
||||||
|
TCP_FOOL_NONE=0,
|
||||||
|
TCP_FOOL_MD5SIG=1,
|
||||||
|
TCP_FOOL_BADSUM=2
|
||||||
|
};
|
||||||
|
bool prepare_tcp_segment4(
|
||||||
|
const struct sockaddr_in *src, const struct sockaddr_in *dst,
|
||||||
|
uint8_t tcp_flags,
|
||||||
|
uint32_t seq, uint32_t ack_seq,
|
||||||
|
uint8_t ttl,
|
||||||
|
enum tcp_fooling_mode fooling,
|
||||||
|
const void *data, uint16_t len,
|
||||||
|
char *buf, size_t *buflen);
|
||||||
|
bool prepare_tcp_segment6(
|
||||||
|
const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst,
|
||||||
|
uint8_t tcp_flags,
|
||||||
|
uint32_t seq, uint32_t ack_seq,
|
||||||
|
uint8_t ttl,
|
||||||
|
enum tcp_fooling_mode fooling,
|
||||||
|
const void *data, uint16_t len,
|
||||||
|
char *buf, size_t *buflen);
|
||||||
|
bool prepare_tcp_segment(
|
||||||
|
const struct sockaddr *src, const struct sockaddr *dst,
|
||||||
|
uint8_t tcp_flags,
|
||||||
|
uint32_t seq, uint32_t ack_seq,
|
||||||
|
uint8_t ttl,
|
||||||
|
enum tcp_fooling_mode fooling,
|
||||||
|
const void *data, uint16_t len,
|
||||||
|
char *buf, size_t *buflen);
|
||||||
|
|
||||||
|
void extract_endpoints(const struct iphdr *iphdr,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst);
|
||||||
|
|
||||||
|
// auto creates internal socket and uses it for subsequent calls
|
||||||
|
bool rawsend(struct sockaddr* dst,uint32_t fwmark,const void *data,size_t len);
|
||||||
|
// cleans up socket autocreated by rawsend
|
||||||
|
void rawsend_cleanup();
|
82
nfq/gzip.c
Normal file
82
nfq/gzip.c
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "gzip.h"
|
||||||
|
|
||||||
|
#define ZCHUNK 16384
|
||||||
|
#define BUFMIN 128
|
||||||
|
#define BUFCHUNK (1024*128)
|
||||||
|
|
||||||
|
int z_readfile(FILE *F,char **buf,size_t *size)
|
||||||
|
{
|
||||||
|
z_stream zs;
|
||||||
|
int r;
|
||||||
|
unsigned char in[ZCHUNK];
|
||||||
|
size_t bufsize;
|
||||||
|
void *newbuf;
|
||||||
|
|
||||||
|
memset(&zs,0,sizeof(zs));
|
||||||
|
|
||||||
|
*buf = NULL;
|
||||||
|
bufsize=*size=0;
|
||||||
|
|
||||||
|
r=inflateInit2(&zs,47);
|
||||||
|
if (r != Z_OK) return r;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
zs.avail_in = fread(in, 1, sizeof(in), F);
|
||||||
|
if (ferror(F))
|
||||||
|
{
|
||||||
|
r = Z_ERRNO;
|
||||||
|
goto zerr;
|
||||||
|
}
|
||||||
|
if (!zs.avail_in) break;
|
||||||
|
zs.next_in = in;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if ((bufsize-*size)<BUFMIN)
|
||||||
|
{
|
||||||
|
bufsize += BUFCHUNK;
|
||||||
|
newbuf = *buf ? realloc(*buf,bufsize) : malloc(bufsize);
|
||||||
|
if (!newbuf)
|
||||||
|
{
|
||||||
|
r = Z_MEM_ERROR;
|
||||||
|
goto zerr;
|
||||||
|
}
|
||||||
|
*buf = newbuf;
|
||||||
|
}
|
||||||
|
zs.avail_out = bufsize - *size;
|
||||||
|
zs.next_out = (unsigned char*)(*buf + *size);
|
||||||
|
r = inflate(&zs, Z_NO_FLUSH);
|
||||||
|
if (r!=Z_OK && r!=Z_STREAM_END) goto zerr;
|
||||||
|
*size = bufsize - zs.avail_out;
|
||||||
|
} while (r==Z_OK && zs.avail_in);
|
||||||
|
} while (r==Z_OK);
|
||||||
|
|
||||||
|
if (*size<bufsize)
|
||||||
|
{
|
||||||
|
// free extra space
|
||||||
|
if (newbuf = realloc(*buf,*size)) *buf=newbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
inflateEnd(&zs);
|
||||||
|
return Z_OK;
|
||||||
|
|
||||||
|
zerr:
|
||||||
|
inflateEnd(&zs);
|
||||||
|
if (*buf)
|
||||||
|
{
|
||||||
|
free(*buf);
|
||||||
|
*buf = NULL;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_gzip(FILE* F)
|
||||||
|
{
|
||||||
|
unsigned char magic[2];
|
||||||
|
bool b = !fseek(F,0,SEEK_SET) && fread(magic, 1, 2, F)==2 && magic[0]==0x1F && magic[1]==0x8B;
|
||||||
|
fseek(F,0,SEEK_SET);
|
||||||
|
return b;
|
||||||
|
}
|
8
nfq/gzip.h
Normal file
8
nfq/gzip.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
int z_readfile(FILE *F,char **buf,size_t *size);
|
||||||
|
bool is_gzip(FILE* F);
|
112
nfq/hostlist.c
Normal file
112
nfq/hostlist.c
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "hostlist.h"
|
||||||
|
#include "gzip.h"
|
||||||
|
|
||||||
|
|
||||||
|
static bool addpool(strpool **hostlist, char **s, char *end)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
// advance until eol lowering all chars
|
||||||
|
for (p = *s; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p);
|
||||||
|
if (!StrPoolAddStrLen(hostlist, *s, p-*s))
|
||||||
|
{
|
||||||
|
StrPoolDestroy(hostlist);
|
||||||
|
*hostlist = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// advance to the next line
|
||||||
|
for (; p<end && (!*p || *p=='\r' || *p=='\n') ; p++);
|
||||||
|
*s = p;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool LoadHostList(strpool **hostlist, char *filename)
|
||||||
|
{
|
||||||
|
char *p, *e, s[256], *zbuf;
|
||||||
|
size_t zsize;
|
||||||
|
int ct = 0;
|
||||||
|
FILE *F;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (*hostlist)
|
||||||
|
{
|
||||||
|
StrPoolDestroy(hostlist);
|
||||||
|
*hostlist = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(F = fopen(filename, "rb")))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Could not open %s\n", filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_gzip(F))
|
||||||
|
{
|
||||||
|
r = z_readfile(F,&zbuf,&zsize);
|
||||||
|
fclose(F);
|
||||||
|
if (r==Z_OK)
|
||||||
|
{
|
||||||
|
printf("zlib compression detected. uncompressed size : %zu\n", zsize);
|
||||||
|
|
||||||
|
p = zbuf;
|
||||||
|
e = zbuf + zsize;
|
||||||
|
while(p<e)
|
||||||
|
{
|
||||||
|
if (!addpool(hostlist,&p,e))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Not enough memory to store host list : %s\n", filename);
|
||||||
|
free(zbuf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ct++;
|
||||||
|
}
|
||||||
|
free(zbuf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "zlib decompression failed : result %d\n",r);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("loading plain text list\n");
|
||||||
|
|
||||||
|
while (fgets(s, 256, F))
|
||||||
|
{
|
||||||
|
p = s;
|
||||||
|
if (!addpool(hostlist,&p,p+strlen(p)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Not enough memory to store host list : %s\n", filename);
|
||||||
|
fclose(F);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ct++;
|
||||||
|
}
|
||||||
|
fclose(F);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Loaded %d hosts from %s\n", ct, filename);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SearchHostList(strpool *hostlist, const char *host, bool debug)
|
||||||
|
{
|
||||||
|
if (hostlist)
|
||||||
|
{
|
||||||
|
const char *p = host;
|
||||||
|
bool bInHostList;
|
||||||
|
while (p)
|
||||||
|
{
|
||||||
|
bInHostList = StrPoolCheckStr(hostlist, p);
|
||||||
|
if (debug) printf("Hostlist check for %s : %s\n", p, bInHostList ? "positive" : "negative");
|
||||||
|
if (bInHostList) return true;
|
||||||
|
p = strchr(p, '.');
|
||||||
|
if (p) p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
7
nfq/hostlist.h
Normal file
7
nfq/hostlist.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "strpool.h"
|
||||||
|
|
||||||
|
bool LoadHostList(strpool **hostlist, char *filename);
|
||||||
|
bool SearchHostList(strpool *hostlist, const char *host,bool debug);
|
1041
nfq/nfqws.c
Normal file
1041
nfq/nfqws.c
Normal file
File diff suppressed because it is too large
Load Diff
128
nfq/sec.c
Normal file
128
nfq/sec.c
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "sec.h"
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
bool setpcap(cap_value_t *caps, int ncaps)
|
||||||
|
{
|
||||||
|
cap_t capabilities;
|
||||||
|
|
||||||
|
if (!(capabilities = cap_init()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ncaps && (cap_set_flag(capabilities, CAP_PERMITTED, ncaps, caps, CAP_SET) ||
|
||||||
|
cap_set_flag(capabilities, CAP_EFFECTIVE, ncaps, caps, CAP_SET)))
|
||||||
|
{
|
||||||
|
cap_free(capabilities);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cap_set_proc(capabilities))
|
||||||
|
{
|
||||||
|
cap_free(capabilities);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cap_free(capabilities);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int getmaxcap()
|
||||||
|
{
|
||||||
|
int maxcap = CAP_LAST_CAP;
|
||||||
|
FILE *F = fopen("/proc/sys/kernel/cap_last_cap", "r");
|
||||||
|
if (F)
|
||||||
|
{
|
||||||
|
int n = fscanf(F, "%d", &maxcap);
|
||||||
|
fclose(F);
|
||||||
|
}
|
||||||
|
return maxcap;
|
||||||
|
|
||||||
|
}
|
||||||
|
bool dropcaps()
|
||||||
|
{
|
||||||
|
// must have CAP_SETPCAP at the end. its required to clear bounding set
|
||||||
|
cap_value_t cap_values[] = { CAP_NET_ADMIN,CAP_NET_RAW,CAP_SETPCAP };
|
||||||
|
int capct = sizeof(cap_values) / sizeof(*cap_values);
|
||||||
|
int maxcap = getmaxcap();
|
||||||
|
|
||||||
|
if (setpcap(cap_values, capct))
|
||||||
|
{
|
||||||
|
for (int cap = 0; cap <= maxcap; cap++)
|
||||||
|
{
|
||||||
|
if (cap_drop_bound(cap))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "could not drop cap %d\n", cap);
|
||||||
|
perror("cap_drop_bound");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now without CAP_SETPCAP
|
||||||
|
if (!setpcap(cap_values, capct - 1))
|
||||||
|
{
|
||||||
|
perror("setpcap");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool droproot(uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
if (uid || gid)
|
||||||
|
{
|
||||||
|
if (prctl(PR_SET_KEEPCAPS, 1L))
|
||||||
|
{
|
||||||
|
perror("prctl(PR_SET_KEEPCAPS): ");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (setgid(gid))
|
||||||
|
{
|
||||||
|
perror("setgid: ");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (setuid(uid))
|
||||||
|
{
|
||||||
|
perror("setuid: ");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dropcaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
void daemonize()
|
||||||
|
{
|
||||||
|
int pid;
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (pid == -1)
|
||||||
|
{
|
||||||
|
perror("fork: ");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
else if (pid != 0)
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
if (setsid() == -1)
|
||||||
|
exit(2);
|
||||||
|
if (chdir("/") == -1)
|
||||||
|
exit(2);
|
||||||
|
close(STDIN_FILENO);
|
||||||
|
close(STDOUT_FILENO);
|
||||||
|
close(STDERR_FILENO);
|
||||||
|
/* redirect fd's 0,1,2 to /dev/null */
|
||||||
|
open("/dev/null", O_RDWR);
|
||||||
|
int fd;
|
||||||
|
/* stdin */
|
||||||
|
fd = dup(0);
|
||||||
|
/* stdout */
|
||||||
|
fd = dup(0);
|
||||||
|
/* stderror */
|
||||||
|
}
|
||||||
|
|
||||||
|
bool writepid(const char *filename)
|
||||||
|
{
|
||||||
|
FILE *F;
|
||||||
|
if (!(F = fopen(filename, "w")))
|
||||||
|
return false;
|
||||||
|
fprintf(F, "%d", getpid());
|
||||||
|
fclose(F);
|
||||||
|
return true;
|
||||||
|
}
|
12
nfq/sec.h
Normal file
12
nfq/sec.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/capability.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
bool setpcap(cap_value_t *caps, int ncaps);
|
||||||
|
int getmaxcap();
|
||||||
|
bool dropcaps();
|
||||||
|
bool droproot(uid_t uid, gid_t gid);
|
||||||
|
void daemonize();
|
||||||
|
bool writepid(const char *filename);
|
76
nfq/strpool.c
Normal file
76
nfq/strpool.c
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
#include "strpool.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#undef uthash_nonfatal_oom
|
||||||
|
#define uthash_nonfatal_oom(elt) ut_oom_recover(elt)
|
||||||
|
|
||||||
|
static bool oom=false;
|
||||||
|
static void ut_oom_recover(strpool *elem)
|
||||||
|
{
|
||||||
|
oom=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for zero terminated strings
|
||||||
|
bool StrPoolAddStr(strpool **pp,const char *s)
|
||||||
|
{
|
||||||
|
strpool *elem;
|
||||||
|
if (!(elem = (strpool*)malloc(sizeof(strpool))))
|
||||||
|
return false;
|
||||||
|
if (!(elem->str = strdup(s)))
|
||||||
|
{
|
||||||
|
free(elem);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
oom = false;
|
||||||
|
HASH_ADD_KEYPTR( hh, *pp, elem->str, strlen(elem->str), elem );
|
||||||
|
if (oom)
|
||||||
|
{
|
||||||
|
free(elem->str);
|
||||||
|
free(elem);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// for not zero terminated strings
|
||||||
|
bool StrPoolAddStrLen(strpool **pp,const char *s,size_t slen)
|
||||||
|
{
|
||||||
|
strpool *elem;
|
||||||
|
if (!(elem = (strpool*)malloc(sizeof(strpool))))
|
||||||
|
return false;
|
||||||
|
if (!(elem->str = malloc(slen+1)))
|
||||||
|
{
|
||||||
|
free(elem);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(elem->str,s,slen);
|
||||||
|
elem->str[slen]=0;
|
||||||
|
oom = false;
|
||||||
|
HASH_ADD_KEYPTR( hh, *pp, elem->str, strlen(elem->str), elem );
|
||||||
|
if (oom)
|
||||||
|
{
|
||||||
|
free(elem->str);
|
||||||
|
free(elem);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StrPoolCheckStr(strpool *p,const char *s)
|
||||||
|
{
|
||||||
|
strpool *elem;
|
||||||
|
HASH_FIND_STR( p, s, elem);
|
||||||
|
return elem!=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrPoolDestroy(strpool **p)
|
||||||
|
{
|
||||||
|
strpool *elem,*tmp;
|
||||||
|
HASH_ITER(hh, *p, elem, tmp) {
|
||||||
|
free(elem->str);
|
||||||
|
HASH_DEL(*p, elem);
|
||||||
|
free(elem);
|
||||||
|
}
|
||||||
|
*p = NULL;
|
||||||
|
}
|
19
nfq/strpool.h
Normal file
19
nfq/strpool.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
//#define HASH_BLOOM 20
|
||||||
|
#define HASH_NONFATAL_OOM 1
|
||||||
|
#define HASH_FUNCTION HASH_BER
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
typedef struct strpool {
|
||||||
|
char *str; /* key */
|
||||||
|
UT_hash_handle hh; /* makes this structure hashable */
|
||||||
|
} strpool;
|
||||||
|
|
||||||
|
void StrPoolDestroy(strpool **p);
|
||||||
|
bool StrPoolAddStr(strpool **pp,const char *s);
|
||||||
|
bool StrPoolAddStrLen(strpool **pp,const char *s,size_t slen);
|
||||||
|
bool StrPoolCheckStr(strpool *p,const char *s);
|
1217
nfq/uthash.h
Normal file
1217
nfq/uthash.h
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user