mirror of
https://github.com/bol-van/zapret.git
synced 2025-04-17 04:22:59 +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