mirror of
https://github.com/bol-van/zapret.git
synced 2024-11-29 21:40:52 +03:00
history purge
This commit is contained in:
commit
3703918a4b
48
Makefile
Normal file
48
Makefile
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
DIRS := nfq tpws ip2net mdig
|
||||||
|
DIRS_MAC := tpws ip2net mdig
|
||||||
|
TGT := binaries/my
|
||||||
|
|
||||||
|
all: clean
|
||||||
|
@mkdir -p "$(TGT)"; \
|
||||||
|
for dir in $(DIRS); do \
|
||||||
|
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
|
||||||
|
$(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
|
||||||
|
|
||||||
|
bsd: clean
|
||||||
|
@mkdir -p "$(TGT)"; \
|
||||||
|
for dir in $(DIRS); do \
|
||||||
|
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
|
||||||
|
$(MAKE) -C "$$dir" bsd || 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
|
||||||
|
|
||||||
|
mac: clean
|
||||||
|
@mkdir -p "$(TGT)"; \
|
||||||
|
for dir in $(DIRS_MAC); do \
|
||||||
|
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
|
||||||
|
$(MAKE) -C "$$dir" mac || 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 -rf "$(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/arm/ip2net
Executable file
BIN
binaries/arm/ip2net
Executable file
Binary file not shown.
BIN
binaries/arm/mdig
Executable file
BIN
binaries/arm/mdig
Executable file
Binary file not shown.
BIN
binaries/arm/nfqws
Executable file
BIN
binaries/arm/nfqws
Executable file
Binary file not shown.
BIN
binaries/arm/tpws
Executable file
BIN
binaries/arm/tpws
Executable file
Binary file not shown.
BIN
binaries/mac64/ip2net
Executable file
BIN
binaries/mac64/ip2net
Executable file
Binary file not shown.
BIN
binaries/mac64/mdig
Executable file
BIN
binaries/mac64/mdig
Executable file
Binary file not shown.
BIN
binaries/mac64/tpws
Executable file
BIN
binaries/mac64/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.
BIN
binaries/x86_64/tpws_wsl.tgz
Normal file
BIN
binaries/x86_64/tpws_wsl.tgz
Normal file
Binary file not shown.
69
config
Normal file
69
config
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# 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"
|
||||||
|
|
||||||
|
# ipset/*.sh can compress large lists
|
||||||
|
GZIP_LISTS=1
|
||||||
|
# command to reload ip/host lists after update
|
||||||
|
# comment or leave empty for auto backend selection : ipset or ipfw if present
|
||||||
|
# on BSD systems with PF no auto reloading happens. you must provide your own command
|
||||||
|
# set to "-" to disable reload
|
||||||
|
#LISTS_RELOAD="pfctl -f /etc/pf.conf"
|
||||||
|
|
||||||
|
# CHOOSE OPERATION MODE
|
||||||
|
# MODE : nfqws,tpws,filter,custom
|
||||||
|
# nfqws : use nfqws
|
||||||
|
# tpws : use tpws
|
||||||
|
# filter : no daemon, just create ipset or download hostlist
|
||||||
|
# custom : custom mode. should modify custom init script and add your own code
|
||||||
|
MODE=tpws
|
||||||
|
# apply fooling to http
|
||||||
|
MODE_HTTP=1
|
||||||
|
# for nfqws only. support http keep alives. enable only if DPI checks for http request in any outgoing packet
|
||||||
|
MODE_HTTP_KEEPALIVE=0
|
||||||
|
# apply fooling to https
|
||||||
|
MODE_HTTPS=1
|
||||||
|
# none,ipset,hostlist
|
||||||
|
MODE_FILTER=none
|
||||||
|
|
||||||
|
# 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="--hostspell=HOST --split-http-req=method --split-pos=3"
|
||||||
|
|
||||||
|
# openwrt only : donttouch,none,software,hardware
|
||||||
|
FLOWOFFLOAD=donttouch
|
||||||
|
|
||||||
|
# for routers based on desktop linux and macos. has no effect in openwrt.
|
||||||
|
# CHOOSE LAN and optinally 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
|
346
docs/bsd.eng.txt
Normal file
346
docs/bsd.eng.txt
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
Supported versions
|
||||||
|
------------------
|
||||||
|
|
||||||
|
FreeBSD 11.x+ , OpenBSD 6.x+, partially MacOS Sierra+
|
||||||
|
|
||||||
|
Older versions may work or not. pfSense is not supported.
|
||||||
|
|
||||||
|
BSD features
|
||||||
|
------------
|
||||||
|
|
||||||
|
BSD does not have NFQUEUE. Similar mechanism - divert sockets.
|
||||||
|
In BSD compiling the source from nfq directory result in dvtws binary instead of nfqws.
|
||||||
|
dvtws shares most of the code with nfqws and offers almost identical parameters.
|
||||||
|
|
||||||
|
FreeBSD has 2 firewalls : IPFilter (ipfw) and Packet Filter (PF). OpenBSD has only PF.
|
||||||
|
|
||||||
|
To compile sources in FreeBSD use 'make', in OpenBSD - use 'make bsd', in MacOS - use 'make mac'.
|
||||||
|
Compile all programs : make -C /opt/zapret
|
||||||
|
Compile all programs with PF support : make -C /opt/zapret CFLAGS=-DUSE_PF
|
||||||
|
In FreeBSD enable PF only if you use it. Its undesirable if you don't.
|
||||||
|
PF is enabled automatically in OpenBSD and MacOS.
|
||||||
|
|
||||||
|
Divert sockets are internal type sockets in the BSD kernel. They have no relation to network addresses
|
||||||
|
or network packet exchange. They are identified by a port number 1..65535. Its like queue number in NFQUEUE.
|
||||||
|
Traffic can be diverted to a divert socket using firewall rule.
|
||||||
|
If nobody listens on the specified divert port packets are dropped. Its similar to NFQUEUE without --queue-bypass.
|
||||||
|
|
||||||
|
ipset/*.sh scripts work with ipfw lookup tables if ipfw is present.
|
||||||
|
ipfw table is analog to linux ipset. Unlike ipsets ipfw tables share v4 an v6 addresses and subnets.
|
||||||
|
If ipfw is absent scripts check LISTS_RELOAD config variable.
|
||||||
|
If its present then scripts execute a command from LISTS_RELOAD.
|
||||||
|
If LISTS_RELOAD=- scripts do not load tables even if ipfw exists.
|
||||||
|
|
||||||
|
PF can load ip tables from a file. To use this feature with ipset/*.sh scripts disable gzip file creation
|
||||||
|
using "GZIP_LISTS=0" directive in the /opt/zapret/config file.
|
||||||
|
|
||||||
|
BSD kernel doesn't implement splice syscall. tpws uses regular recv/send operations with data copying to user space.
|
||||||
|
Its slower but not critical.
|
||||||
|
tpws uses nonblocking sockets with linux specific epoll feature.
|
||||||
|
In BSD systems epoll is emulated by epoll-shim library on top of kqueue.
|
||||||
|
|
||||||
|
dvtws uses some programming HACKs, assumptions and knowledge of discovered bugs and limitations.
|
||||||
|
BSD systems have many limitations, version specific features and bugs in low level networking, especially for ipv6.
|
||||||
|
Many years have passed but BSD code still has 15-20 year artificial limiters in the code.
|
||||||
|
dvtws uses additinal divert socket(s) for layer 3 packet injection if raw sockets do not allow it.
|
||||||
|
It works for the moment but who knows. Such a usage is not very documented.
|
||||||
|
|
||||||
|
mdig and ip2net are fully compatible with BSD.
|
||||||
|
|
||||||
|
FreeBSD
|
||||||
|
-------
|
||||||
|
|
||||||
|
Divert sockets require special kernel module 'ipdivert'.
|
||||||
|
Write the following to config files :
|
||||||
|
/boot/loader.conf (create if absent) :
|
||||||
|
-----------
|
||||||
|
ipdivert_load="YES"
|
||||||
|
net.inet.ip.fw.default_to_accept=1
|
||||||
|
-----------
|
||||||
|
/etc/rc.conf :
|
||||||
|
-----------
|
||||||
|
firewall_enable="YES"
|
||||||
|
firewall_script="/etc/rc.firewall.my"
|
||||||
|
-----------
|
||||||
|
/etc/rc.firewall.my :
|
||||||
|
-----------
|
||||||
|
ipfw -q -f flush
|
||||||
|
-----------
|
||||||
|
Later you will add ipfw commands to /etc/rc.firewall.my to be reapplied after reboot.
|
||||||
|
You can also run zapret daemons from there. Start them with "--daemon" options, for example :
|
||||||
|
-----------
|
||||||
|
pkill ^dvtws$
|
||||||
|
/opt/zapret/nfq/dvtws --port=989 --daemon --dpi-desync=split2
|
||||||
|
-----------
|
||||||
|
To restart firewall and daemons run : /etc/rc.d/ipfw restart
|
||||||
|
|
||||||
|
|
||||||
|
Assume LAN='em1', WAN="em0".
|
||||||
|
|
||||||
|
tpws transparent mode quick start.
|
||||||
|
|
||||||
|
For all traffic:
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from me to any 80,443 proto ip4 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from me to any 80,443 proto ip6 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1
|
||||||
|
/opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1
|
||||||
|
|
||||||
|
Process only table zapret with the exception of table nozapret :
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 allow tcp from me to table\(nozapret\) 80,443
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from me to table\(zapret\) 80,443 proto ip4 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from me to table\(zapret\) 80,443 proto ip6 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 allow tcp from any to table\(nozapret\) 80,443 recv em1
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1
|
||||||
|
/opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1
|
||||||
|
|
||||||
|
Tables zapret, nozapret, ipban are created by ipset/*.sh scripts the same way as in Linux.
|
||||||
|
Its a good idea to update tables periodically :
|
||||||
|
crontab -e
|
||||||
|
write the line : 0 12 */2 * * /opt/zapret/ipset/get_config.sh
|
||||||
|
|
||||||
|
When using ipfw tpws does not require special permissions for transparent mode.
|
||||||
|
However without root its not possible to bind to ports <1024 and change UID/GID. Without changing UID tpws
|
||||||
|
will run into recursive loop, and that's why its necessary to write ipfw rules with the right UID.
|
||||||
|
Redirecting to ports >=1024 is dangerous. If tpws is not running any unprivileged process can
|
||||||
|
listen to that port and intercept traffic.
|
||||||
|
|
||||||
|
|
||||||
|
dvtws quick start.
|
||||||
|
|
||||||
|
For all traffic:
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted not sockarg xmit em0
|
||||||
|
/opt/zapret/nfq/dvtws --port=989 --dpi-desync=split2
|
||||||
|
|
||||||
|
Process only table zapret with the exception of table nozapret :
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 allow tcp from me to table\(nozapret\) 80,443
|
||||||
|
ipfw add 100 divert 989 tcp from any to table\(zapret\) 80,443 out not diverted not sockarg xmit em0
|
||||||
|
/opt/zapret/nfq/dvtws --port=989 --dpi-desync=split2
|
||||||
|
|
||||||
|
Reinjection loop avoidance.
|
||||||
|
FreeBSD artificially ignores sockarg for ipv6 in the kernel.
|
||||||
|
This limitation is coming from the ipv6 early age. Code is still in "testing" state. 10-20 years. Everybody forgot about it.
|
||||||
|
dvtws sends ipv6 forged frames using another divert socket (HACK). they can be filtered out using 'diverted'.
|
||||||
|
ipv4 frames are filtered using 'sockarg'.
|
||||||
|
|
||||||
|
PF in FreeBSD:
|
||||||
|
The setup is similar to OpenBSD, but there are important nuances.
|
||||||
|
1) Don't forget to build special PF-enabled version of tpws : make CFLAGS=-DUSE_PF
|
||||||
|
2) It's not possible to redirect to ::1. Need to redirect to the link-local address of the incoming interface.
|
||||||
|
Look for fe80:... address in ifconfig and use it for redirection target.
|
||||||
|
3) pf.conf syntax is a bit different from OpenBSD.
|
||||||
|
4) How to set maximum table size : sysctl net.pf.request_maxcount=2000000
|
||||||
|
5) The word 'divert-packet' is absent in the pfctl binary, divert-packet rules are not working.
|
||||||
|
'divert-to' is not the same thing. Looks like its not possible to use dvtws with PF in FreeBSD.
|
||||||
|
/etc/pf.conf
|
||||||
|
-----------
|
||||||
|
rdr pass on em1 inet6 proto tcp to port {80,443} -> fe80::31c:29ff:dee2:1c4d port 988
|
||||||
|
rdr pass on em1 inet proto tcp to port {80,443} -> 127.0.0.1 port 988
|
||||||
|
-----------
|
||||||
|
/opt/zapret/tpws/tpws --port=988 --bind-addr=127.0.0.1 --bind-iface6=em1 --bind-linklocal=force
|
||||||
|
|
||||||
|
Its not clear how to do rdr-to outgoing traffic. I could not make route-to scheme work.
|
||||||
|
|
||||||
|
|
||||||
|
OpenBSD
|
||||||
|
-------
|
||||||
|
|
||||||
|
In OpenBSD default tpws bind is ipv6 only. to bind to ipv4 specify --bind-addr=0.0.0.0
|
||||||
|
Use --bind-addr=0.0.0.0 --bind-addr=:: to achieve the same default bind as in others OSes.
|
||||||
|
|
||||||
|
tpws for forwarded traffic only :
|
||||||
|
|
||||||
|
/etc/pf.conf
|
||||||
|
------------
|
||||||
|
pass in quick on em1 inet proto tcp to port {80,443} rdr-to 127.0.0.1 port 988
|
||||||
|
pass in quick on em1 inet6 proto tcp to port {80,443} rdr-to ::1 port 988
|
||||||
|
------------
|
||||||
|
pfctl -f /etc/pf.conf
|
||||||
|
tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1
|
||||||
|
|
||||||
|
Its not clear how to do rdr-to outgoing traffic. I could not make route-to scheme work.
|
||||||
|
rdr-to support is done using /dev/pf, that's why transparent mode requires root.
|
||||||
|
|
||||||
|
dvtws for all traffic:
|
||||||
|
|
||||||
|
/etc/pf.conf
|
||||||
|
------------
|
||||||
|
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989
|
||||||
|
------------
|
||||||
|
pfctl -f /etc/pf.conf
|
||||||
|
./dvtws --port=989 --dpi-desync=split2
|
||||||
|
|
||||||
|
dwtws only for table zapret with the exception of table nozapret :
|
||||||
|
|
||||||
|
/etc/pf.conf
|
||||||
|
------------
|
||||||
|
table <zapret> file "/opt/zapret/ipset/zapret-ip.txt"
|
||||||
|
table <zapret-user> file "/opt/zapret/ipset/zapret-ip-user.txt"
|
||||||
|
table <nozapret> file "/opt/zapret/ipset/zapret-ip-exclude.txt"
|
||||||
|
pass out quick on em0 inet proto tcp to <nozapret> port {80,443}
|
||||||
|
pass out quick on em0 inet proto tcp to <zapret> port {80,443} divert-packet port 989
|
||||||
|
pass out quick on em0 inet proto tcp to <zapret-user> port {80,443} divert-packet port 989
|
||||||
|
table <zapret6> file "/opt/zapret/ipset/zapret-ip6.txt"
|
||||||
|
table <zapret6-user> file "/opt/zapret/ipset/zapret-ip-user6.txt"
|
||||||
|
table <nozapret6> file "/opt/zapret/ipset/zapret-ip-exclude6.txt"
|
||||||
|
pass out quick on em0 inet6 proto tcp to <nozapret6> port {80,443}
|
||||||
|
pass out quick on em0 inet6 proto tcp to <zapret6> port {80,443} divert-packet port 989
|
||||||
|
pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-packet port 989
|
||||||
|
------------
|
||||||
|
pfctl -f /etc/pf.conf
|
||||||
|
./dvtws --port=989 --dpi-desync=split2
|
||||||
|
|
||||||
|
|
||||||
|
dvtws in OpenBSD sends all fakes through a divert socket because raw sockets have critical artificial limitations.
|
||||||
|
Looks like pf automatically prevent reinsertion of diverted frames. Loop problem does not exist.
|
||||||
|
|
||||||
|
Sadly PF auto applies return rule to divert-packet.
|
||||||
|
Not only outgoing packets go through dvtws but also incoming.
|
||||||
|
This adds great unneeded overhead that will be the most noticable on http/https downloads.
|
||||||
|
I could not figure out how to disable this feature.
|
||||||
|
Thats why you are encouraged to use table filters with your personal blocked site lists.
|
||||||
|
|
||||||
|
OpenBSD forcibly recomputes tcp checksum after divert. Thats why most likely
|
||||||
|
dpi-desync-fooling=badsum will not work. dvtws will warn if you specify this parameter.
|
||||||
|
|
||||||
|
ipset scripts do not reload PF by default. To enable reload specify command in /opt/zapret/config :
|
||||||
|
LISTS_RELOAD="pfctl -f /etc/pf.conf"
|
||||||
|
Newer pfctl versions can reload tables only : pfctl -Tl -f /etc/pf.conf
|
||||||
|
But OpenBSD 6.8 pfctl is old enough and does not support that. Newer FreeBSD do.
|
||||||
|
Don't forget to disable gzip compression :
|
||||||
|
GZIP_LISTS=0
|
||||||
|
If some list files do not exist and have references in pf.conf it leads to error.
|
||||||
|
You need to exclude those tables from pf.conf and referencing them rules.
|
||||||
|
After configuration is done you can put ipset script :
|
||||||
|
crontab -e
|
||||||
|
write the line : 0 12 */2 * * /opt/zapret/ipset/get_config.sh
|
||||||
|
|
||||||
|
|
||||||
|
MacOS
|
||||||
|
-----
|
||||||
|
|
||||||
|
Initially, the kernel of this OS was based on BSD. That's why it is still BSD but a lot was modified by Apple.
|
||||||
|
As usual a mass commercial project priorities differ from their free counterparts.
|
||||||
|
Apple guys do what they want.
|
||||||
|
What everyone have updated long ago they keep old like a mammoth. But who cares ?
|
||||||
|
|
||||||
|
MacOS used to have ipfw but it was removed later and replaced by PF.
|
||||||
|
It looks like divert sockets are internally replaced with raw. Its possible to request a divert socket
|
||||||
|
but it behaves exactly as raw socket with all its BSD inherited + apple specific bugs and feature.
|
||||||
|
The fact is that divert-packet in /etc/pf.conf does not work. pfctl binary does not contain the word 'divert'.
|
||||||
|
dvtws does compile but is useless.
|
||||||
|
|
||||||
|
After some efforts tpws works. Apple has removed some important stuff from their newer SDKs (DIOCNATLOOK) making
|
||||||
|
them undocumented and unsupported. With important definitions copied from an older SDK it was possible to make
|
||||||
|
transparent mode working again. But this is not guaranteed to work in the future versions.
|
||||||
|
Another MacOS unique feature is root requirement while polling /dev/pf.
|
||||||
|
By default tpws drops root. Its necessary to specify --user=root to stay with root.
|
||||||
|
In other aspects PF behaves very similar to FreeBSD and shares the same pf.conf syntax.
|
||||||
|
|
||||||
|
In MacOS redirection works both for passthrough and outgoing traffic. Outgoing redirection requires route-to rule.
|
||||||
|
Because tpws is forced to run as root to avoid loop its necessary to exempt root from the redirection.
|
||||||
|
That's why DPI bypass will not work for local requests from root.
|
||||||
|
|
||||||
|
If you do ipv6 routing you have to get rid of "secured" ipv6 address assignment.
|
||||||
|
"secured" addresses are designed to be permanent and not related to the MAC address.
|
||||||
|
And they really are. Except for link-locals.
|
||||||
|
If you just reboot the system link-locals will not change. But next day they will change. Not necessary to wait so long.
|
||||||
|
Just change the system time to tomorrow and reboot. Link-locals will change. (at least they change in vmware guest)
|
||||||
|
Looks like its a kernel bug. Link locals should not change. Its useless and can be harmful. Cant use LL as a gateway.
|
||||||
|
The easiest solution is to disable "secured" addresses.
|
||||||
|
Outgoing connections prefer randomly generated temporary addressesas like in other systems.
|
||||||
|
Put the string "net.inet6.send.opmode=0" to /etc/sysctl.conf. If not present - create it.
|
||||||
|
Then reboot the system.
|
||||||
|
If you dont like this solution you can assign an additional static ipv6 address from fd00::/8 range with /128 prefix
|
||||||
|
to your LAN interface and use it as the gateway address.
|
||||||
|
|
||||||
|
tpws transparent mode only for outgoing connections. en0 - WAN.
|
||||||
|
|
||||||
|
/etc/pf.conf
|
||||||
|
------------
|
||||||
|
rdr pass on lo0 inet proto tcp from !127.0.0.0/8 to any port {80,443} -> 127.0.0.1 port 988
|
||||||
|
rdr pass on lo0 inet6 proto tcp from !::1 to any port {80,443} -> fe80::1 port 988
|
||||||
|
pass out route-to (lo0 127.0.0.1) inet proto tcp from any to any port {80,443} user { >root }
|
||||||
|
pass out route-to (lo0 fe80::1) inet6 proto tcp from any to any port {80,443} user { >root }
|
||||||
|
------------
|
||||||
|
pfctl -ef /etc/pf.conf
|
||||||
|
/opt/zapret/tpws/tpws --user=root --port=988 --bind-addr=127.0.0.1 --bind-iface6=en0 --bind-linklocal=force
|
||||||
|
|
||||||
|
|
||||||
|
tpws transparent mode for both passthrough and outgoing connections. en0 - WAN, en1 - WAN.
|
||||||
|
|
||||||
|
ifconfig en1 | grep fe80
|
||||||
|
inet6 fe80::bbbb:bbbb:bbbb:bbbb%en1 prefixlen 64 scopeid 0x8
|
||||||
|
/etc/pf.conf
|
||||||
|
------------
|
||||||
|
rdr pass on en1 inet proto tcp from any to any port {80,443} -> 127.0.0.1 port 988
|
||||||
|
rdr pass on en1 inet6 proto tcp from any to any port {80,443} -> fe80::bbbb:bbbb:bbbb:bbbb port 988
|
||||||
|
rdr pass on lo0 inet proto tcp from !127.0.0.0/8 to any port {80,443} -> 127.0.0.1 port 988
|
||||||
|
rdr pass on lo0 inet6 proto tcp from !::1 to any port {80,443} -> fe80::1 port 988
|
||||||
|
pass out route-to (lo0 127.0.0.1) inet proto tcp from any to any port {80,443} user { >root }
|
||||||
|
pass out route-to (lo0 fe80::1) inet6 proto tcp from any to any port {80,443} user { >root }
|
||||||
|
------------
|
||||||
|
pfctl -ef /etc/pf.conf
|
||||||
|
/opt/zapret/tpws/tpws --user=root --port=988 --bind-addr=127.0.0.1 --bind-iface6=en0 --bind-linklocal=force --bind-iface6=en1 --bind-linklocal=force
|
||||||
|
|
||||||
|
|
||||||
|
Build from source : make -C /opt/zapret mac
|
||||||
|
|
||||||
|
ipset/*.sh scripts work.
|
||||||
|
|
||||||
|
|
||||||
|
MacOS easy install
|
||||||
|
------------------
|
||||||
|
|
||||||
|
install_easy.sh supports MacOS
|
||||||
|
|
||||||
|
Shipped precompiled binaries are built for 64-bit MacOS with -mmacosx-version-min=10.8 option.
|
||||||
|
They should run on all supported MacOS versions.
|
||||||
|
If no - its easy to build your own. Running 'make' automatically installs developer tools.
|
||||||
|
|
||||||
|
!! Internet sharing is not supported !!
|
||||||
|
Routing is supported but only manually configured through PF.
|
||||||
|
If you enable internet sharing tpws stops functioning. When you disable internet sharing you may lose web site access.
|
||||||
|
To fix : pfctl -f /etc/pf.conf
|
||||||
|
If you need internet sharing use tpws socks mode.
|
||||||
|
|
||||||
|
launchd is used for autostart (/Library/LaunchDaemons/zapret.plist)
|
||||||
|
Control script : /opt/zapret/init.d/macos/zapret
|
||||||
|
The following commands fork with both tpws and firewall (if INIT_APPLY_FW=1 in config)
|
||||||
|
/opt/zapret/init.d/macos/zapret start
|
||||||
|
/opt/zapret/init.d/macos/zapret stop
|
||||||
|
/opt/zapret/init.d/macos/zapret restart
|
||||||
|
Work with tpws only :
|
||||||
|
/opt/zapret/init.d/macos/zapret start-daemons
|
||||||
|
/opt/zapret/init.d/macos/zapret stop-daemons
|
||||||
|
/opt/zapret/init.d/macos/zapret restart-daemons
|
||||||
|
Work with PF only :
|
||||||
|
/opt/zapret/init.d/macos/zapret start-fw
|
||||||
|
/opt/zapret/init.d/macos/zapret stop-fw
|
||||||
|
/opt/zapret/init.d/macos/zapret restart-fw
|
||||||
|
Reloading PF tables :
|
||||||
|
/opt/zapret/init.d/macos/zapret reload-fw-tables
|
||||||
|
|
||||||
|
Installer configures LISTS_RELOAD in the config so ipset/*.sh scripts automatically reload PF tables.
|
||||||
|
Installer creates cron job for ipset/get_config.sh, as in OpenWRT.
|
||||||
|
|
||||||
|
start-fw script automatically patches /etc/pf.conf inserting there "zapret" anchors.
|
||||||
|
Auto patching requires pf.conf with apple anchors preserved.
|
||||||
|
If your pf.conf is highly customized and patching fails you will see the warning. Do not ignore it.
|
||||||
|
In that case you need to manually insert "zapret" anchors to your pf.conf (keeping the right rule type ordering) :
|
||||||
|
rdr-anchor "zapret"
|
||||||
|
anchor "zapret"
|
||||||
|
unistall_easy.sh unpatches pf.conf
|
||||||
|
|
||||||
|
start-fw creates 3 anchor files in /etc/pf.anchors : zapret,zapret-v4,zapret-v6.
|
||||||
|
Last 2 are referenced by anchor "zapret".
|
||||||
|
Tables nozapret,nozapret6 belong to anchor "zapret".
|
||||||
|
Tables zapret,zapret-user belong to anchor "zapret-v4".
|
||||||
|
Tables zapret6,zapret6-user belong to anchor "zapret-v6".
|
||||||
|
If an ip version is disabled then corresponding anchor is empty and is not referenced from the anchor "zapret".
|
||||||
|
Tables are only created for existing list files in the ipset directory.
|
375
docs/bsd.txt
Normal file
375
docs/bsd.txt
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
Поддерживаемые версии
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
FreeBSD 11.x+ , OpenBSD 6.x+, частично MacOS Sierra+
|
||||||
|
|
||||||
|
На более старых может собираться, может не собираться, может работать или не работать.
|
||||||
|
На FreeBSD 10 собирается и работает dvtws. С tpws есть проблемы из-за слишком старой версии компилятора clang.
|
||||||
|
Вероятно, будет работать, если обновить компилятор.
|
||||||
|
На pfSense если и можно завести, то это не просто. Собранные на FreeBSD с той же версией ядра бинарики не работают.
|
||||||
|
Статические бинарики тоже. Модуль ipdivert отсутствует.
|
||||||
|
|
||||||
|
|
||||||
|
Особенности BSD систем
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
В BSD нет nfqueue. Похожий механизм - divert sockets.
|
||||||
|
Из каталога "nfq" под BSD собирается dvtws вместо nfqws.
|
||||||
|
Он разделяет с nfqws большую часть кода и почти совпадает по параметрам командной строки.
|
||||||
|
|
||||||
|
FreeBSD содержит 2 фаервола : IPFilter (ipfw) и Packet Filter (PF). OpenBSD содержит только PF.
|
||||||
|
|
||||||
|
Под FreeBSD tpws и dvtws собираются через "make", под OpenBSD - "make bsd", под MacOS - "make mac".
|
||||||
|
FreeBSD make распознает BSDmakefile , OpenBSD и MacOS - нет. Поэтому там используется отдельный target в Makefile.
|
||||||
|
Сборка всех исходников : make -C /opt/zapret
|
||||||
|
Сборка всех исходников с поддержкой PF : make -C /opt/zapret CFLAGS=-DUSE_PF
|
||||||
|
В FreeBSD поддержку PF нужно включать только, если вы его используете. Иначе это нежелательно !
|
||||||
|
В OpenBSD и MacOS PF при сборке включается автоматически.
|
||||||
|
|
||||||
|
divert сокет - внутренний тип сокета ядра BSD. Он не привязывается ни к какому сетевому адресу, не участвует
|
||||||
|
в обмене данными через сеть и идентифицируется по номеру порта 1..65535. Аналогия с номером очереди NFQUEUE.
|
||||||
|
На divert сокеты заворачивается трафик посредством правил ipfw или PF.
|
||||||
|
Если в фаерволе есть правило divert, но на divert порту никто не слушает, то пакеты дропаются.
|
||||||
|
Это поведение аналогично правилам NFQUEUE без параметра --queue-bypass.
|
||||||
|
На FreeBSD divert сокеты могут быть только ipv4, хотя на них принимаются и ipv4, и ipv6 фреймы.
|
||||||
|
На OpenBSD divert сокеты создаются отдельно для ipv4 и ipv6 и работают только с одной версией ip каждый.
|
||||||
|
На MacOS похоже, что divert сокеты из ядра вырезаны. См подробнее раздел про MacOS.
|
||||||
|
Отсылка в divert сокет работает аналогично отсылке через raw socket на linux. Передается полностью IP фрейм, начиная
|
||||||
|
с ip загловка . Эти особенности учитываются в dvtws.
|
||||||
|
|
||||||
|
Скрипты ipset/*.sh при наличии ipfw работают с ipfw lookup tables.
|
||||||
|
Это прямой аналог ipset. lookup tables не разделены на v4 и v6. Они могут содержать v4 и v6 адреса и подсети одновременно.
|
||||||
|
Если ipfw отсутствует, то действие зависит от переменной LISTS_RELOAD в config.
|
||||||
|
Если она задана, то выполняется команда из LISTS_RELOAD. В противном случае не делается ничего.
|
||||||
|
Если LISTS_RELOAD=-, то заполнение таблиц отключается даже при наличии ipfw.
|
||||||
|
|
||||||
|
PF может загружать ip таблицы из файла. Чтобы использовать эту возможность следует отключить сжатие gzip для листов
|
||||||
|
через параметр файла config "GZIP_LISTS=0".
|
||||||
|
|
||||||
|
BSD не содержит системного вызова splice. tpws работает через переброску данных в user mode в оба конца.
|
||||||
|
Это медленнее, но не критически.
|
||||||
|
Управление асинхронными сокетами в tpws основано на linux-specific механизме epoll.
|
||||||
|
В BSD для его эмуляции используется epoll-shim - прослойка для эмуляции epoll на базе kqueue.
|
||||||
|
|
||||||
|
Некоторые функции dvtws пришлось реализовывать через хаки.
|
||||||
|
В BSD много ограничений, особенностей и багов при работе с низкоуровневой сетью, в особенности в области ipv6.
|
||||||
|
Казалось бы столько лет прошло, а в коде все еще сидят ограничители 15-20 летней давности.
|
||||||
|
Прямая отсылка ipv6 фреймов с измененным source address и вовсе невозможна через raw sockets.
|
||||||
|
OpenBSD не дает отсылать через raw sockets tcp фреймы.
|
||||||
|
Там, где функции нельзя было реализовать напрямую, либо их реализация привела бы к залезанию в низкоуровневые дебри,
|
||||||
|
используются те же divert сокеты. Оказывается через них можно скармливать ядру любые пакеты, обходя ограничения
|
||||||
|
raw sockets. Не знаю насколько это легально, но пока это работает. Однако, имейте в виду. Что-то может сломаться.
|
||||||
|
|
||||||
|
mdig и ip2net полностью работоспособны в BSD. В них нет ничего системо-зависимого.
|
||||||
|
|
||||||
|
FreeBSD
|
||||||
|
-------
|
||||||
|
|
||||||
|
divert сокеты требуют специального модуля ядра ipdivert.
|
||||||
|
Поместите следующие строки в /boot/loader.conf (создать, если отсутствует) :
|
||||||
|
-----------
|
||||||
|
ipdivert_load="YES"
|
||||||
|
net.inet.ip.fw.default_to_accept=1
|
||||||
|
-----------
|
||||||
|
В /etc/rc.conf :
|
||||||
|
-----------
|
||||||
|
firewall_enable="YES"
|
||||||
|
firewall_script="/etc/rc.firewall.my"
|
||||||
|
-----------
|
||||||
|
/etc/rc.firewall.my :
|
||||||
|
-----------
|
||||||
|
ipfw -q -f flush
|
||||||
|
-----------
|
||||||
|
В /etc/rc.firewall.my можно дописывать правила ipfw, чтобы они восстанавливались после перезагрузки.
|
||||||
|
Оттуда же можно запускать и демоны zapret, добавив в параметры "--daemon". Например так :
|
||||||
|
-----------
|
||||||
|
pkill ^dvtws$
|
||||||
|
/opt/zapret/nfq/dvtws --port=989 --daemon --dpi-desync=split2
|
||||||
|
-----------
|
||||||
|
Для перезапуска фаервола и демонов достаточно будет сделать : /etc/rc.d/ipfw restart
|
||||||
|
|
||||||
|
|
||||||
|
Краткая инструкция по запуску tpws в прозрачном режиме.
|
||||||
|
Предполагается, что интерфейс LAN называется em1, WAN - em0.
|
||||||
|
|
||||||
|
Для всего трафика :
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from me to any 80,443 proto ip4 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from me to any 80,443 proto ip6 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1
|
||||||
|
/opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1
|
||||||
|
|
||||||
|
Для трафика только на таблицу zapret, за исключением таблицы nozapret :
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 allow tcp from me to table\(nozapret\) 80,443
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from me to table\(zapret\) 80,443 proto ip4 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from me to table\(zapret\) 80,443 proto ip6 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 allow tcp from any to table\(nozapret\) 80,443 recv em1
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1
|
||||||
|
/opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1
|
||||||
|
|
||||||
|
Таблицы zapret, nozapret, ipban создаются скриптами из ipset по аналогии с Linux.
|
||||||
|
Обновление скриптов можно забить в cron под root :
|
||||||
|
crontab -e
|
||||||
|
Создать строчку "0 12 */2 * * /opt/zapret/ipset/get_config.sh"
|
||||||
|
|
||||||
|
При использовании ipfw tpws не требует повышенных привилегий для реализации прозрачного режима.
|
||||||
|
Однако, без рута невозможен бинд на порты <1024 и смена UID/GID. Без смены UID будет рекурсия,
|
||||||
|
поэтому правила ipfw нужно создавать с учетом UID, под которым работает tpws.
|
||||||
|
Переадресация на порты >=1024 может создать угрозу перехвата трафика непривилегированным
|
||||||
|
процессом, если вдруг tpws не запущен.
|
||||||
|
|
||||||
|
|
||||||
|
Краткая инструкция по запуску dvtws.
|
||||||
|
|
||||||
|
Для всего трафика :
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted not sockarg xmit em0
|
||||||
|
/opt/zapret/nfq/dvtws --port=989 ---dpi-desync=split2
|
||||||
|
|
||||||
|
Для трафика только на таблицу zapret, за исключением таблицы nozapret :
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 allow tcp from me to table\(nozapret\) 80,443
|
||||||
|
ipfw add 100 divert 989 tcp from any to table\(zapret\) 80,443 out not diverted not sockarg xmit em0
|
||||||
|
/opt/zapret/nfq/dvtws --port=989 --dpi-desync=split2
|
||||||
|
|
||||||
|
Недопущение зацикливания - повторного вхождения фейк пакетов на обработку.
|
||||||
|
FreeBSD игнорирует sockarg в ipv6.
|
||||||
|
Это искусственное ограничение в коде ядра, которое тянется уже лет 10-20.
|
||||||
|
Кто-то в свое время посчитал код сырым, и до сих пор никто не удосужился поправить.
|
||||||
|
dvtws в FreeBSD отсылает ipv4 фреймы через raw socket. Такие пакеты не 'diverted'. Они отсекаются по 'sockarg'.
|
||||||
|
Для отсылки ipv6 фейков используется divert socket, потому что ipv6 raw сокеты в BSD не дают самому
|
||||||
|
формировать IP заголовок и подменять source address. Фейки в ipv6 'diverted'. Они отсекаются по 'diverted'.
|
||||||
|
В linux nfqws для недопущения зацикливания используется fwmark.
|
||||||
|
|
||||||
|
|
||||||
|
PF в FreeBSD:
|
||||||
|
Настройка аналогична OpenBSD, но есть важные нюансы.
|
||||||
|
1) Не забыть собрать специальную версию под PF : make CFLAGS=-DUSE_PF
|
||||||
|
2) Нельзя сделать ipv6 rdr на ::1. Нужно делать на link-local адрес входящего интерфейса.
|
||||||
|
Смотрите через ifconfig адрес fe80:... и добавляете в правило
|
||||||
|
3) Синтаксис pf.conf немного отличается. Более новая версия PF.
|
||||||
|
4) Лимит на количество элементов таблиц задается так : sysctl net.pf.request_maxcount=2000000
|
||||||
|
5) Слово 'divert-packet' отсутствует в бинарике pfctl, правила divert-packet выдают ошибку.
|
||||||
|
'divert-to' - это не то. Не похоже, что в FreeBSD можно завести dvtws через PF.
|
||||||
|
/etc/pf.conf
|
||||||
|
-----------
|
||||||
|
rdr pass on em1 inet6 proto tcp to port {80,443} -> fe80::31c:29ff:dee2:1c4d port 988
|
||||||
|
rdr pass on em1 inet proto tcp to port {80,443} -> 127.0.0.1 port 988
|
||||||
|
-----------
|
||||||
|
/opt/zapret/tpws/tpws --port=988 --bind-addr=127.0.0.1 --bind-iface6=em1 --bind-linklocal=force
|
||||||
|
|
||||||
|
В PF непонятно как делать rdr-to с той же системы, где работает proxy. Вариант с route-to у меня не заработал.
|
||||||
|
|
||||||
|
|
||||||
|
OpenBSD
|
||||||
|
-------
|
||||||
|
|
||||||
|
В tpws бинд по умолчанию только на ipv6. для бинда на ipv4 указать "--bind-addr=0.0.0.0"
|
||||||
|
Используйте --bind-addr=0.0.0.0 --bind-addr=:: для достижения того же результата, как в других ОС по умолчанию.
|
||||||
|
(лучше все же так не делать, а сажать на определенные внутренние адреса или интерфейсы)
|
||||||
|
|
||||||
|
tpws для проходящего трафика :
|
||||||
|
|
||||||
|
/etc/pf.conf
|
||||||
|
------------
|
||||||
|
pass in quick on em1 inet proto tcp to port {80,443} rdr-to 127.0.0.1 port 988
|
||||||
|
pass in quick on em1 inet6 proto tcp to port {80,443} rdr-to ::1 port 988
|
||||||
|
------------
|
||||||
|
pfctl -f /etc/pf.conf
|
||||||
|
tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1
|
||||||
|
|
||||||
|
В PF непонятно как делать rdr-to с той же системы, где работает proxy. Вариант с route-to у меня не заработал.
|
||||||
|
Поддержка rdr-to реализована через /dev/pf, поэтому прозрачный режим требует root.
|
||||||
|
|
||||||
|
dvtws для всего трафика :
|
||||||
|
|
||||||
|
/etc/pf.conf
|
||||||
|
------------
|
||||||
|
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989
|
||||||
|
------------
|
||||||
|
pfctl -f /etc/pf.conf
|
||||||
|
./dvtws --port=989 --dpi-desync=split2
|
||||||
|
|
||||||
|
dvtws для трафика только на таблицу zapret, за исключением таблицы nozapret :
|
||||||
|
|
||||||
|
/etc/pf.conf
|
||||||
|
------------
|
||||||
|
set limit table-entries 2000000
|
||||||
|
table <zapret> file "/opt/zapret/ipset/zapret-ip.txt"
|
||||||
|
table <zapret-user> file "/opt/zapret/ipset/zapret-ip-user.txt"
|
||||||
|
table <nozapret> file "/opt/zapret/ipset/zapret-ip-exclude.txt"
|
||||||
|
pass out quick on em0 inet proto tcp to <nozapret> port {80,443}
|
||||||
|
pass out quick on em0 inet proto tcp to <zapret> port {80,443} divert-packet port 989
|
||||||
|
pass out quick on em0 inet proto tcp to <zapret-user> port {80,443} divert-packet port 989
|
||||||
|
table <zapret6> file "/opt/zapret/ipset/zapret-ip6.txt"
|
||||||
|
table <zapret6-user> file "/opt/zapret/ipset/zapret-ip-user6.txt"
|
||||||
|
table <nozapret6> file "/opt/zapret/ipset/zapret-ip-exclude6.txt"
|
||||||
|
pass out quick on em0 inet6 proto tcp to <nozapret6> port {80,443}
|
||||||
|
pass out quick on em0 inet6 proto tcp to <zapret6> port {80,443} divert-packet port 989
|
||||||
|
pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-packet port 989
|
||||||
|
------------
|
||||||
|
pfctl -f /etc/pf.conf
|
||||||
|
./dvtws --port=989 --dpi-desync=split2
|
||||||
|
|
||||||
|
|
||||||
|
В OpenBSD dvtws все фейки отсылает через divert socket, поскольку эта возможность через raw sockets заблокирована.
|
||||||
|
Видимо pf автоматически предотвращает повторный заворот diverted фреймов, поэтому проблемы зацикливания нет.
|
||||||
|
|
||||||
|
К сожалению, в PF присутствует "удобная" функция, которая автоматически применяет к правилу divert-packet
|
||||||
|
обратный трафик. Через divert пойдет все соединение, а не только исходящие пакеты.
|
||||||
|
Это добавит огромный ненужный overhead по процессингу входящих пакетов в dvtws, который будет наиболее заметен
|
||||||
|
на скачивании по http/https. Мне не удалось понять как этого избежать.
|
||||||
|
Поэтому использование фильтр-таблиц крайне рекомендовано !
|
||||||
|
|
||||||
|
OpenBSD принудительно пересчитывает tcp checksum после divert, поэтому скорее всего
|
||||||
|
dpi-desync-fooling=badsum у вас не заработает. При использовании этого параметра
|
||||||
|
dvtws предупредит о возможной проблеме.
|
||||||
|
|
||||||
|
Скрипты из ipset не перезагружают таблицы в PF по умолчанию.
|
||||||
|
Чтобы они это делали, добавьте параметр в /opt/zapret/config :
|
||||||
|
LISTS_RELOAD="pfctl -f /etc/pf.conf"
|
||||||
|
Более новые версии pfctl понимают команду перезагрузить только таблицы : pfctl -Tl -f /etc/pf.conf
|
||||||
|
Но это не относится к OpenBSD 6.8. В новых FreeBSD есть.
|
||||||
|
Не забудьте выключить сжатие gzip :
|
||||||
|
GZIP_LISTS=0
|
||||||
|
Если в вашей конфигурации какого-то файла листа нет, то его необходимо исключить из правил PF.
|
||||||
|
Если вдруг листа нет, и он задан в pf.conf, будет ошибка перезагрузки фаервола.
|
||||||
|
После настройки обновление листов можно поместить в cron :
|
||||||
|
crontab -e
|
||||||
|
дописать строчку : 0 12 */2 * * /opt/zapret/ipset/get_config.sh
|
||||||
|
|
||||||
|
Если будете пользоваться скриптом ipset/get_combined.sh, установите GNU grep : pkg_add ggrep.
|
||||||
|
Родной древний как мамонт, безумно медленный с опцией -f.
|
||||||
|
|
||||||
|
|
||||||
|
MacOS
|
||||||
|
-----
|
||||||
|
|
||||||
|
Иначально ядро этой ОС "darwin" основывалось на BSD, потому в ней много похожего на другие версии BSD.
|
||||||
|
Однако, как и в других массовых коммерческих проектах, приоритеты смещаются в сторону от оригинала.
|
||||||
|
Яблочники что хотят, то и творят. Меняют, убирают, оставляют какие-то безумно старые версии API и утилит.
|
||||||
|
То, что уже давно везде обновили, может быть еще древним как мамонт в самой последней версии MacOS.
|
||||||
|
Но кого это волнует ?
|
||||||
|
|
||||||
|
Раньше был ipfw, потом его убрали, заменили на PF.
|
||||||
|
Есть сомнения, что divert сокеты в ядре остались. Попытка создать divert socket не выдает ошибок,
|
||||||
|
но полученный сокет ведет себя точно так же, как raw, со всеми его унаследованными косяками + еще яблочно специфическими.
|
||||||
|
В PF divert-packet не работает. Простой grep бинарика pfctl показывает, что там нет слова "divert",
|
||||||
|
а в других версиях BSD оно есть. dvtws собирается, но совершенно бесполезен.
|
||||||
|
|
||||||
|
tpws удалось адаптировать, он работоспособен. Получение адреса назначения для прозрачного прокси в PF (DIOCNATLOOK)
|
||||||
|
убрали из заголовков в новых SDK, сделав фактически недокументированным.
|
||||||
|
В tpws перенесены некоторые определения из более старых версий яблочных SDK. С ними удалось завести прозрачный режим.
|
||||||
|
Однако, что будет в следующих версиях угадать сложно. Гарантий нет.
|
||||||
|
Еще одной особенностью PF в MacOS является проверка на рута в момент обращения к /dev/pf, чего нет в остальных BSD.
|
||||||
|
tpws по умолчанию сбрасывает рутовые привилегии. Необходимо явно указать параметр --user=root.
|
||||||
|
В остальном PF себя ведет похоже на FreeBSD. Синтаксис pf.conf тот же.
|
||||||
|
|
||||||
|
На MacOS работает редирект как с проходящего трафика, так и с локальной системы через route-to.
|
||||||
|
Поскольку tpws вынужден работать под root, для исключения рекурсии приходится пускать исходящий от root трафик напрямую.
|
||||||
|
Отсюда имеем недостаток : обход DPI для рута работать не будет.
|
||||||
|
|
||||||
|
Если вы пользуетесь MaсOS в качестве ipv6 роутера, то нужно будет решить вопрос с регулярно изменяемым link-local адресом.
|
||||||
|
С некоторых версий MacOS использует по умолчанию постоянные "secured" ipv6 адреса вместо генерируемых на базе MAC адреса.
|
||||||
|
Все замечательно, но есть одна проблема. Постоянными остаются только global scope адреса.
|
||||||
|
Link locals периодически меняются. Смена завязана на системное время. Перезагрузки адрес не меняют,
|
||||||
|
Но если перевести время на день вперед и перезагрузиться - link local станет другим. (по крайней мере в vmware это так)
|
||||||
|
Информации по вопросу крайне мало, но тянет на баг. Не должен меняться link local. Скрывать link local не имеет смысла,
|
||||||
|
а динамический link local нельзя использовать в качестве адреса шлюза.
|
||||||
|
Проще всего отказаться от "secured" адресов.
|
||||||
|
Поместите строчку "net.inet6.send.opmode=0" в /etc/sysctl.conf. Затем перезагрузите систему.
|
||||||
|
Все равно для исходящих соединений будут использоваться temporary адреса, как и в других системах.
|
||||||
|
Или вам идея не по вкусу, можно прописать дополнительный статический ipv6 из диапазона fd00::/8 -
|
||||||
|
выберите любой с длиной префикса 128. Это можно сделать в системных настройках, создав дополнительный адаптер на базе
|
||||||
|
того же сетевого интерфейса, отключить в нем ipv4 и вписать статический ipv6. Он добавится к автоматически настраеваемым.
|
||||||
|
|
||||||
|
Настройка tpws на macos в прозрачном режиме только для исходящих запросов, где en0 - WAN :
|
||||||
|
|
||||||
|
/etc/pf.conf
|
||||||
|
------------
|
||||||
|
rdr pass on lo0 inet proto tcp from !127.0.0.0/8 to any port {80,443} -> 127.0.0.1 port 988
|
||||||
|
rdr pass on lo0 inet6 proto tcp from !::1 to any port {80,443} -> fe80::1 port 988
|
||||||
|
pass out route-to (lo0 127.0.0.1) inet proto tcp from any to any port {80,443} user { >root }
|
||||||
|
pass out route-to (lo0 fe80::1) inet6 proto tcp from any to any port {80,443} user { >root }
|
||||||
|
------------
|
||||||
|
pfctl -ef /etc/pf.conf
|
||||||
|
/opt/zapret/tpws/tpws --user=root --port=988 --bind-addr=127.0.0.1 --bind-iface6=en0 --bind-linklocal=force
|
||||||
|
|
||||||
|
|
||||||
|
Настройка tpws на macos роутере в прозрачном режиме, где en0 - WAN, en1 - LAN.
|
||||||
|
|
||||||
|
ifconfig en1 | grep fe80
|
||||||
|
inet6 fe80::bbbb:bbbb:bbbb:bbbb%en1 prefixlen 64 scopeid 0x8
|
||||||
|
/etc/pf.conf
|
||||||
|
------------
|
||||||
|
rdr pass on en1 inet proto tcp from any to any port {80,443} -> 127.0.0.1 port 988
|
||||||
|
rdr pass on en1 inet6 proto tcp from any to any port {80,443} -> fe80::bbbb:bbbb:bbbb:bbbb port 988
|
||||||
|
rdr pass on lo0 inet proto tcp from !127.0.0.0/8 to any port {80,443} -> 127.0.0.1 port 988
|
||||||
|
rdr pass on lo0 inet6 proto tcp from !::1 to any port {80,443} -> fe80::1 port 988
|
||||||
|
pass out route-to (lo0 127.0.0.1) inet proto tcp from any to any port {80,443} user { >root }
|
||||||
|
pass out route-to (lo0 fe80::1) inet6 proto tcp from any to any port {80,443} user { >root }
|
||||||
|
------------
|
||||||
|
pfctl -ef /etc/pf.conf
|
||||||
|
/opt/zapret/tpws/tpws --user=root --port=988 --bind-addr=127.0.0.1 --bind-iface6=en0 --bind-linklocal=force --bind-iface6=en1 --bind-linklocal=force
|
||||||
|
|
||||||
|
|
||||||
|
Сборка : make -C /opt/zapret mac
|
||||||
|
|
||||||
|
Скрипты получения листов ipset/*.sh работают.
|
||||||
|
Если будете пользоваться ipset/get_combined.sh, нужно установить gnu grep через brew.
|
||||||
|
Имеющийся очень старый и безумно медленный с оцией -f.
|
||||||
|
|
||||||
|
|
||||||
|
MacOS простая установка
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
В MacOS поддерживается install_easy.sh
|
||||||
|
|
||||||
|
В комплекте идут бинарики, собраные под 64-bit с опцией -mmacosx-version-min=10.8.
|
||||||
|
Они должны работать на всех поддерживаемых версиях macos.
|
||||||
|
Если вдруг не работают - можно собрать свои. Developer tools ставятся автоматом при запуске make.
|
||||||
|
|
||||||
|
!! Internet sharing средствами системы НЕ ПОДДЕРЖИВАЕТСЯ !!
|
||||||
|
Поддерживается только роутер, настроенный своими силами через PF.
|
||||||
|
Если вы вдруг включили шаринг, а потом выключили, то доступ к сайтам может пропасть совсем.
|
||||||
|
Лечение : pfctl -f /etc/pf.conf
|
||||||
|
Если вам нужен шаринг интернета, лучше отказаться от прозрачного режима и использовать socks.
|
||||||
|
|
||||||
|
Для автостарта используется launchd (/Library/LaunchDaemons/zapret.plist)
|
||||||
|
Управляющий скрипт : /opt/zapret/init.d/macos/zapret
|
||||||
|
Следующие команды работают с tpws и фаерволом одновременно (если INIT_APPLY_FW=1 в config)
|
||||||
|
/opt/zapret/init.d/macos/zapret start
|
||||||
|
/opt/zapret/init.d/macos/zapret stop
|
||||||
|
/opt/zapret/init.d/macos/zapret restart
|
||||||
|
Работа только с tpws :
|
||||||
|
/opt/zapret/init.d/macos/zapret start-daemons
|
||||||
|
/opt/zapret/init.d/macos/zapret stop-daemons
|
||||||
|
/opt/zapret/init.d/macos/zapret restart-daemons
|
||||||
|
Работа только с PF :
|
||||||
|
/opt/zapret/init.d/macos/zapret start-fw
|
||||||
|
/opt/zapret/init.d/macos/zapret stop-fw
|
||||||
|
/opt/zapret/init.d/macos/zapret restart-fw
|
||||||
|
Перезагрузка всех IP таблиц из файлов :
|
||||||
|
/opt/zapret/init.d/macos/zapret reload-fw-tables
|
||||||
|
|
||||||
|
Инсталятор настраивает LISTS_RELOAD в config, так что скрипты ipset/*.sh автоматически перезагружают IP таблицы в PF.
|
||||||
|
Автоматически создается cron job на ipset/get_config.sh, по аналогии с openwrt.
|
||||||
|
|
||||||
|
При start-fw скрипт автоматически модицифирует /etc/pf.conf, вставляя туда anchors "zapret".
|
||||||
|
Модификация расчитана на pf.conf, в котором сохранены дефолтные anchors от apple.
|
||||||
|
Если у вас измененный pf.conf и модификация не удалась, об этом будет предупреждение. Не игнорируйте его.
|
||||||
|
В этом случае вам нужно вставить в свой pf.conf (в соответствии с порядком типов правил) :
|
||||||
|
rdr-anchor "zapret"
|
||||||
|
anchor "zapret"
|
||||||
|
При деинсталяции через uninstall_easy.sh модификации pf.conf убираются.
|
||||||
|
|
||||||
|
start-fw создает 3 файла anchors в /etc/pf.anchors : zapret,zapret-v4,zapret-v6.
|
||||||
|
Последние 2 подключаются из anchor "zapret".
|
||||||
|
Таблицы nozapret,nozapret6 принадлежат anchor "zapret".
|
||||||
|
Таблицы zapret,zapret-user - в anchor "zapret-v4".
|
||||||
|
Таблицы zapret6,zapret6-user - в anchor "zapret-v6".
|
||||||
|
Если какая-то версия протокола отключена - соответствующий anchor пустой и не упоминается в anchor "zapret".
|
||||||
|
Таблицы и правила создаются только на те листы, которые фактически есть в директории ipset.
|
89
docs/bsdfw.txt
Normal file
89
docs/bsdfw.txt
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
WAN=em0 LAN=em1
|
||||||
|
|
||||||
|
FreeBSD IPFW :
|
||||||
|
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from me to any 80,443 proto ip4 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from me to any 80,443 proto ip6 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1
|
||||||
|
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 allow tcp from me to table\(nozapret\) 80,443
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from me to table\(zapret\) 80,443 proto ip4 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from me to table\(zapret\) 80,443 proto ip6 xmit em0 not uid daemon
|
||||||
|
ipfw add 100 allow tcp from any to table\(nozapret\) 80,443 recv em1
|
||||||
|
ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1
|
||||||
|
ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1
|
||||||
|
|
||||||
|
/opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1
|
||||||
|
|
||||||
|
|
||||||
|
; Loop avoidance.
|
||||||
|
; FreeBSD artificially ignores sockarg for ipv6 in the kernel.
|
||||||
|
; This limitation is coming from the ipv6 early age. Code is still in "testing" state. 10-20 years. Everybody forgot about it.
|
||||||
|
; dvtws sends ipv6 forged frames using another divert socket (HACK). they can be filtered out using 'diverted'.
|
||||||
|
|
||||||
|
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted not sockarg xmit em0
|
||||||
|
|
||||||
|
ipfw delete 100
|
||||||
|
ipfw add 100 allow tcp from me to table\(nozapret\) 80,443
|
||||||
|
ipfw add 100 divert 989 tcp from any to table\(zapret\) 80,443 out not diverted not sockarg xmit em0
|
||||||
|
|
||||||
|
/opt/zapret/nfq/dvtws --port=989 --debug --dpi-desync=split
|
||||||
|
|
||||||
|
|
||||||
|
sample ipfw NAT setup :
|
||||||
|
|
||||||
|
WAN=em0
|
||||||
|
LAN=em1
|
||||||
|
ipfw -q flush
|
||||||
|
ipfw -q nat 1 config if $WAN unreg_only reset
|
||||||
|
ipfw -q add 10 allow ip from any to any via $LAN
|
||||||
|
ipfw -q add 20 allow ip from any to any via lo0
|
||||||
|
ipfw -q add 300 nat 1 ip4 from any to any in recv $WAN
|
||||||
|
ipfw -q add 301 check-state
|
||||||
|
ipfw -q add 350 skipto 390 tcp from any to any out xmit $WAN setup keep-state
|
||||||
|
ipfw -q add 350 skipto 390 udp from any to any out xmit $WAN keep-state
|
||||||
|
ipfw -q add 360 allow all from any to me in recv $WAN
|
||||||
|
ipfw -q add 390 nat 1 ip4 from any to any out xmit $WAN
|
||||||
|
ipfw -q add 10000 allow ip from any to any
|
||||||
|
|
||||||
|
Forwarding :
|
||||||
|
sysctl net.inet.ip.forwarding=1
|
||||||
|
sysctl net.inet6.ip6.forwarding=1
|
||||||
|
|
||||||
|
|
||||||
|
OpenBSD PF :
|
||||||
|
|
||||||
|
; dont know how to rdr-to from local system. doesn't seem to work. only works for routed traffic.
|
||||||
|
|
||||||
|
/etc/pf.conf
|
||||||
|
pass in quick on em1 inet proto tcp to port {80,443} rdr-to 127.0.0.1 port 988
|
||||||
|
pass in quick on em1 inet6 proto tcp to port {80,443} rdr-to ::1 port 988
|
||||||
|
pfctl -f /etc/pf.conf
|
||||||
|
/opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1
|
||||||
|
|
||||||
|
; dvtws works both for routed and local
|
||||||
|
|
||||||
|
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989
|
||||||
|
pfctl -f /etc/pf.conf
|
||||||
|
./dvtws --port=989 --dpi-desync=split2
|
||||||
|
|
||||||
|
; dvtws with table limitations : to zapret,zapret6 but not to nozapret,nozapret6
|
||||||
|
; reload tables : pfctl -f /etc/pf.conf
|
||||||
|
set limit table-entries 2000000
|
||||||
|
table <zapret> file "/opt/zapret/ipset/zapret-ip.txt"
|
||||||
|
table <zapret-user> file "/opt/zapret/ipset/zapret-ip-user.txt"
|
||||||
|
table <nozapret> file "/opt/zapret/ipset/zapret-ip-exclude.txt"
|
||||||
|
pass out quick on em0 inet proto tcp to <nozapret> port {80,443}
|
||||||
|
pass out quick on em0 inet proto tcp to <zapret> port {80,443} divert-packet port 989
|
||||||
|
pass out quick on em0 inet proto tcp to <zapret-user> port {80,443} divert-packet port 989
|
||||||
|
table <zapret6> file "/opt/zapret/ipset/zapret-ip6.txt"
|
||||||
|
table <zapret6-user> file "/opt/zapret/ipset/zapret-ip-user6.txt"
|
||||||
|
table <nozapret6> file "/opt/zapret/ipset/zapret-ip-exclude6.txt"
|
||||||
|
pass out quick on em0 inet6 proto tcp to <nozapret6> port {80,443}
|
||||||
|
pass out quick on em0 inet6 proto tcp to <zapret6> port {80,443} divert-packet port 989
|
||||||
|
pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-packet port 989
|
191
docs/changes.txt
Normal file
191
docs/changes.txt
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
v31
|
||||||
|
|
||||||
|
nfqws : DPI desync attack modes : disorder,disorder2,split,split2.
|
||||||
|
nfqws : DPI desync fooling mode : badseq. multiple modes supported
|
||||||
|
|
||||||
|
v32
|
||||||
|
|
||||||
|
tpws : multiple binds
|
||||||
|
init scripts : run only one instance of tpws in any case
|
||||||
|
|
||||||
|
v33
|
||||||
|
|
||||||
|
openwrt : flow offloading support
|
||||||
|
config : MODE refactoring
|
||||||
|
|
||||||
|
v34
|
||||||
|
|
||||||
|
nfqws : dpi-desync 2 mode combos
|
||||||
|
nfqws : dpi-desync without parameter no more supported. previously it meant "fake"
|
||||||
|
nfqws : custom fake http request and tls client hello
|
||||||
|
|
||||||
|
v35
|
||||||
|
|
||||||
|
limited FreeBSD and OpenBSD support
|
||||||
|
|
||||||
|
v36
|
||||||
|
|
||||||
|
full FreeBSD and OpenBSD support
|
||||||
|
|
||||||
|
v37
|
||||||
|
|
||||||
|
limited MacOS support
|
||||||
|
|
||||||
|
v38
|
||||||
|
|
||||||
|
MacOS easy install
|
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 988 -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 988
|
||||||
|
|
||||||
|
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 988
|
||||||
|
|
||||||
|
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.127:988
|
||||||
|
iptables -t nat -I OUTPUT -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to 127.0.0.127:988
|
||||||
|
|
||||||
|
|
||||||
|
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
|
643
docs/readme.eng.txt
Normal file
643
docs/readme.eng.txt
Normal file
@ -0,0 +1,643 @@
|
|||||||
|
What is it for
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Bypass the blocking of http/https web sites on DPI without the use of third-party servers.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
(EXPERIMENTAL) FreeBSD and OpenBSD are also supported.
|
||||||
|
(EXPERIMENTAL, PARTIAL) MacOS limited support.
|
||||||
|
see docs/bsd.eng.txt
|
||||||
|
|
||||||
|
How it works
|
||||||
|
------------
|
||||||
|
|
||||||
|
In the simplest case you are dealing with passive DPI. Passive DPI can read passthrough traffic,
|
||||||
|
inject its own packets, but cannot drop packets.
|
||||||
|
If the request is prohibited the passive DPI will inject its own RST packet and optionally http redirect packet.
|
||||||
|
If fake packets from DPI are only sent to client, you can use iptables commands to drop them if you can write
|
||||||
|
correct filter rules. This requires manual in-deep traffic analysis and tuning for specific ISP.
|
||||||
|
This is how we bypass the consequences of a ban trigger.
|
||||||
|
|
||||||
|
If the passive DPI sends an RST packet also to the server, there is nothing you can do about it.
|
||||||
|
Your task is to prevent ban trigger from firing up. Iptables alone will not work.
|
||||||
|
This project is aimed at preventing the ban rather than eliminating its consequences.
|
||||||
|
|
||||||
|
To do that send what DPI does not expect and what breaks its algorithm of recognizing requests and blocking them.
|
||||||
|
|
||||||
|
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."
|
||||||
|
|
||||||
|
There is also more advanced magic for bypassing DPI at the packet level.
|
||||||
|
|
||||||
|
|
||||||
|
How to put this into practice in the linux system
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
In short, the options can be classified according to the following scheme:
|
||||||
|
|
||||||
|
1) Passive DPI not sending RST to the server. ISP tuned iptables commands can help.
|
||||||
|
This option is out of the scope of the project. If you do not allow ban trigger to fire, then you won’t have to
|
||||||
|
deal with its consequences.
|
||||||
|
2) Modification of the TCP connection at the stream level. Implemented through a proxy or transparent proxy.
|
||||||
|
3) Modification of TCP connection at the packet level. Implemented through the NFQUEUE handler and raw sockets.
|
||||||
|
|
||||||
|
For options 2 and 3, tpws and nfqws programs are implemented, respectively.
|
||||||
|
You need to run them with the necessary parameters and redirect certain traffic with iptables.
|
||||||
|
|
||||||
|
To redirect a TCP connection to a transparent proxy, the following commands are used:
|
||||||
|
|
||||||
|
forwarded fraffic :
|
||||||
|
iptables -t nat -I PREROUTING -i <internal_interface> -p tcp --dport 80 -j DNAT --to 127.0.0.127:988
|
||||||
|
outgoing traffic :
|
||||||
|
iptables -t nat -I OUTPUT -o <external_interface> -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to 127.0.0.127:988
|
||||||
|
|
||||||
|
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.<internal_interface>.route_localnet=1
|
||||||
|
|
||||||
|
You can use "-j REDIRECT --to-port 988" instead of DNAT, but in this case the transparent 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 automated scripts will have to recognize it,
|
||||||
|
then dynamically enter it into the command. In any case, additional efforts are required.
|
||||||
|
Using route_localnet can also introduce some security risks. You make available from internal_interface everything
|
||||||
|
bound to 127.0.0.0/8. Services are usually bound to 127.0.0.1. Its possible to deny input to 127.0.0.1 from all interfaces except lo
|
||||||
|
or bind tpws to any other IP from 127.0.0.0/8 range, for example to 127.0.0.127, and allow incomings only to that IP :
|
||||||
|
|
||||||
|
iptables -A INPUT ! -i lo -d 127.0.0.127 -j ACCEPT
|
||||||
|
iptables -A INPUT ! -i lo -d 127.0.0.0/8 -j DROP
|
||||||
|
|
||||||
|
Owner filter is necessary to prevent recursive redirection of connections from tpws itself.
|
||||||
|
tpws must be started under OS user "tpws".
|
||||||
|
|
||||||
|
|
||||||
|
NFQUEUE redirection of the outgoing traffic and forwarded traffic going towards the external interface,
|
||||||
|
can be done with the following commands:
|
||||||
|
|
||||||
|
iptables -t mangle -I POSTROUTING -o <external_interface> -p tcp --dport 80 -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
In order not to touch the traffic to unblocked addresses, you can take a list of blocked hosts, resolve it
|
||||||
|
into IP addresses and put them to ipset 'zapret', then add a filter to the command:
|
||||||
|
|
||||||
|
iptables -t mangle -I POSTROUTING -o <external_interface> -p tcp --dport 80 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
Some DPIs catch only the first http request, ignoring subsequent requests in a keep-alive session.
|
||||||
|
Then we can reduce CPU load, refusing to process unnecessary packets.
|
||||||
|
|
||||||
|
iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4 -m mark ! --mark 0x40000000/0x40000000 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
Mark filter does not allow nfqws-generated packets to enter the queue again.
|
||||||
|
Its necessary to use this filter when also using "connbytes 2:4". Without it packet ordering can be changed breaking the whole idea.
|
||||||
|
|
||||||
|
|
||||||
|
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 :
|
||||||
|
|
||||||
|
ip6tables -t nat -I OUTPUT -o <external_interface> -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to [::1]:988
|
||||||
|
|
||||||
|
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.
|
||||||
|
For BSD systems there is dvtws. Its built from the same source and has almost the same parameters (see bsd.eng.txt).
|
||||||
|
nfqws takes the following parameters:
|
||||||
|
|
||||||
|
--debug=0|1 ; 1=print debug info
|
||||||
|
--qnum=<nfqueue_number>
|
||||||
|
--wsize=<window_size> ; set window size. 0 = do not modify (obsolete !)
|
||||||
|
--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
|
||||||
|
--domcase ; mix domain case after Host: like this : TeSt.cOm
|
||||||
|
--daemon ; daemonize
|
||||||
|
--pidfile=<filename> ; write pid to file
|
||||||
|
--user=<username> ; drop root privs
|
||||||
|
--uid=uid[:gid] ; drop root privs
|
||||||
|
--dpi-desync[=<mode>][,<mode2>] ; try to desync dpi state. modes : fake rst rstack disorder disorder2 split split2
|
||||||
|
--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|ts|badseq|badsum ; can take multiple comma separated values
|
||||||
|
--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-repeats=<N> ; send every desync packet N times
|
||||||
|
--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 split* and disorder* only) split TCP packet at specified position
|
||||||
|
--dpi-desync-any-protocol=0|1 ; 0(default)=desync only http and tls 1=desync any nonempty data packet
|
||||||
|
--dpi-desync-fake-http=<filename> ; file containing fake http request. replacement for built-in
|
||||||
|
--dpi-desync-fake-tls=<filename> ; file containing fake TLS ClientHello (for https). replacement for built-in
|
||||||
|
--hostlist=<filename> ; apply fooling only to the listed hosts (one host per line, subdomains auto apply)
|
||||||
|
|
||||||
|
The manipulation parameters can be combined in any way.
|
||||||
|
|
||||||
|
WARNING. --wsize parameter is now not used anymore in scripts. TCP split can be achieved using DPI desync attack.
|
||||||
|
|
||||||
|
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.
|
||||||
|
* badseq packets will be dropped by server, but DPI also can ignore them
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
--dpi-desync-fooling takes multiple comma separated values.
|
||||||
|
|
||||||
|
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.
|
||||||
|
When dpi-desync-retrans=1 its mandatory to use connbytes in iptables rule. Otherwise loop happens.
|
||||||
|
|
||||||
|
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.
|
||||||
|
Mode 'disorder2' disables sending of fake segments.
|
||||||
|
|
||||||
|
Split mode is very similar to disorder but without segment reordering :
|
||||||
|
1. fake 1st segment, data filled with zeroes
|
||||||
|
2. 1st segment
|
||||||
|
3. fake 1st segment, data filled with zeroes (2nd copy)
|
||||||
|
4. 2nd segment
|
||||||
|
Mode 'split2' disables sending of fake segments. It can be used as a faster alternative to --wsize.
|
||||||
|
|
||||||
|
In disorder2 and split2 modes no fake packets are sent, so ttl and fooling options are not required.
|
||||||
|
|
||||||
|
There are DPIs that analyze responses from the server, particularly the certificate from the ServerHello
|
||||||
|
that contain domain name(s). The ClientHello delivery confirmation is an ACK packet from the server
|
||||||
|
with ACK sequence number corresponding to the length of the ClientHello+1.
|
||||||
|
In the disorder variant, a selective acknowledgement (SACK) usually arrives first, then a full ACK.
|
||||||
|
If, instead of ACK or SACK, there is an RST packet with minimal delay, DPI cuts you off at the request stage.
|
||||||
|
If the RST is after a full ACK after a delay of about ping to the server, then probably DPI acts
|
||||||
|
on the server response. The DPI may be satisfied with good ClientHello and stop monitoring the TCP session
|
||||||
|
without checking ServerHello. Then you were lucky. 'fake' option could work.
|
||||||
|
If it does not stop monitoring and persistently checks the ServerHello, also performing reconstruction of TCP segments,
|
||||||
|
doing something about it is hardly possible without the help of the server.
|
||||||
|
The best solution is to enable TLS 1.3 support on the server. TLS 1.3 sends the server certificate in encrypted form.
|
||||||
|
This is recommendation to all admins of blocked sites. Enable TLS 1.3. You will give more opportunities to overcome DPI.
|
||||||
|
|
||||||
|
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 on the first packet :
|
||||||
|
|
||||||
|
iptables -t mangle -I POSTROUTING -o <external_interface> -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
|
||||||
|
|
||||||
|
This is good if DPI does not track all requests in http keep-alive session.
|
||||||
|
If it does, then pass all outgoing packets for http and only first data packet for https :
|
||||||
|
|
||||||
|
iptables -t mangle -I POSTROUTING -o <external_interface> -p tcp --dport 443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
iptables -t mangle -I POSTROUTING -o <external_interface> -p tcp --dport 80 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
mark is needed to keep away generated packets from NFQUEUE. nfqws sets fwmark when it sends generated packets.
|
||||||
|
nfqws can internally filter marked packets. but when connbytes filter is used without mark filter
|
||||||
|
packet ordering can be changed breaking the whole idea of desync attack.
|
||||||
|
|
||||||
|
DESYNC COMBOS
|
||||||
|
dpi-desync parameter can take 2 comma separated arguments.
|
||||||
|
1st phase mode can be fake,rst,rstack, 2nd phase mode - disorder,disorder2,split,split2.
|
||||||
|
Can be useful for ISPs with more than one DPI.
|
||||||
|
|
||||||
|
VIRTUAL MACHINES
|
||||||
|
Most of nfqws packet magic does not work from VMs powered by virtualbox and vmware when network is NATed.
|
||||||
|
Hypervisor forcibly changes ttl and does not forward fake packets.
|
||||||
|
Set up bridge networking.
|
||||||
|
|
||||||
|
|
||||||
|
tpws
|
||||||
|
-----
|
||||||
|
|
||||||
|
tpws is transparent proxy.
|
||||||
|
|
||||||
|
--debug=0|1|2 ; 0(default)=silent 1=verbose 2=debug
|
||||||
|
--bind-addr=<v4_addr>|<v6_addr>; for v6 link locals append %interface_name : fe80::1%br-lan
|
||||||
|
--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
|
||||||
|
--bind-wait-only ; wait for bind conditions satisfaction then exit. return code 0 if success.
|
||||||
|
--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, gzip lists supported)
|
||||||
|
--split-http-req=method|host ; split http request at specified logical position.
|
||||||
|
--split-pos=<numeric_offset> ; split at specified pos. split-http-req takes precedence over split-pos for http reqs.
|
||||||
|
--split-any-protocol ; split not only http and https
|
||||||
|
--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:
|
||||||
|
--domcase ; mix domain case after Host: like this : TeSt.cOm
|
||||||
|
--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.
|
||||||
|
|
||||||
|
split-http-req takes precedence over split-pos for http reqs.
|
||||||
|
split-pos works by default only on http and TLS ClientHello. use --split-any-protocol to act on any packet
|
||||||
|
|
||||||
|
tpws can bind to multiple interfaces and IP addresses (up to 32).
|
||||||
|
Port number is always the same.
|
||||||
|
Parameters --bind-iface* и --bind-addr create new bind.
|
||||||
|
Other parameters --bind-* are related to the last bind.
|
||||||
|
To bind to all ipv4 specify --bind-addr "0.0.0.0", all ipv6 - "::". --bind-addr="" - mean bind to all ipv4 and ipv6.
|
||||||
|
If no binds are specified default bind to all ipv4 and ipv6 addresses is created.
|
||||||
|
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.
|
||||||
|
With "no-update" parameter create_ipset.sh creates ipset but populate it only if it was actually created.
|
||||||
|
It's useful when multiple subsequent calls are possible to avoid wasting of cpu time redoing the same job.
|
||||||
|
Ipset loading is resource consuming. Its a good idea to call create_ipset without "no-update" parameter
|
||||||
|
only once a several days. Use it with "no-update" option in other cases.
|
||||||
|
|
||||||
|
ipset scripts automatically call ip2net utility.
|
||||||
|
ip2net helps to reduce ip list size by combining IPs to subnets. Also it cuts invalid IPs from the list.
|
||||||
|
Stored lists are already processed by ip2net. They are error free and ready for loading.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
FreeBSD. ipset/*.sh scripts also work in FreeBSD. Instead of ipset they create ipfw lookup tables with the same names as in Linux.
|
||||||
|
ipfw tables can store both ipv4 and ipv6 addresses and subnets. There's no 4 and 6 separation.
|
||||||
|
|
||||||
|
LISTS_RELOAD config parameter defines a custom lists reloading command.
|
||||||
|
Its useful on BSD systems with PF.
|
||||||
|
LISTS_RELOAD=- disables reloading ip list backend.
|
||||||
|
|
||||||
|
|
||||||
|
Domain name filtering
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
An alternative to ipset is to use tpws or nfqws with a list of domains. Only one list is supported.
|
||||||
|
|
||||||
|
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, daemons should run without filtering by ipset.
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
Main mode :
|
||||||
|
tpws - use tpws
|
||||||
|
tpws - use nfqws
|
||||||
|
filter - only fill ipset or load hostlist
|
||||||
|
custom - use custom script for running daemons and establishing firewall rules
|
||||||
|
|
||||||
|
MODE=tpws
|
||||||
|
|
||||||
|
Enable http fooling :
|
||||||
|
|
||||||
|
MODE_HTTP=1
|
||||||
|
|
||||||
|
Apply fooling to keep alive http sessions. Only applicable to nfqws. Tpws always fool keepalives.
|
||||||
|
Not enabling this can save CPU time.
|
||||||
|
|
||||||
|
MODE_HTTP_KEEPALIVE=0
|
||||||
|
|
||||||
|
Enable https fooling :
|
||||||
|
|
||||||
|
MODE_HTTPS=1
|
||||||
|
|
||||||
|
Host filtering mode :
|
||||||
|
none - apply fooling to all hosts
|
||||||
|
ipset - limit fooling to hosts from ipset zapret/zapret6
|
||||||
|
hostlist - limit fooling to hosts from hostlist
|
||||||
|
|
||||||
|
MODE_FILTER=none
|
||||||
|
|
||||||
|
Its possible to change manipulation options used by tpws :
|
||||||
|
|
||||||
|
TPWS_OPT="--hostspell=HOST --split-http-req=method --split-pos=3"
|
||||||
|
|
||||||
|
nfqws options for DPI desync attack:
|
||||||
|
|
||||||
|
DESYNC_MARK=0x40000000
|
||||||
|
NFQWS_OPT_DESYNC="--dpi-desync=fake --dpi-desync-ttl=0 --dpi-desync-fooling=badsum --dpi-desync-fwmark=$DESYNC_MARK"
|
||||||
|
|
||||||
|
flow offloading control (openwrt only)
|
||||||
|
donttouch : disable system flow offloading setting if selected mode is incompatible with it, dont touch it otherwise and dont configure selective flow offloading
|
||||||
|
none : always disable system flow offloading setting and dont configure selective flow offloading
|
||||||
|
software : always disable system flow offloading setting and configure selective software flow offloading
|
||||||
|
hardware : always disable system flow offloading setting and configure selective hardware flow offloading
|
||||||
|
|
||||||
|
FLOWOFFLOAD=donttouch
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
temp directory. Used by ipset/*.sh scripts for large lists processing.
|
||||||
|
/tmp by default. Can be reassigned if /tmp is tmpfs and RAM is low.
|
||||||
|
TMPDIR=/opt/zapret/tmp
|
||||||
|
|
||||||
|
ipset options :
|
||||||
|
|
||||||
|
IPSET_OPT="hashsize 262144 maxelem 2097152"
|
||||||
|
|
||||||
|
Kernel automatically increases hashsize if ipset is too large for the current hashsize.
|
||||||
|
This procedure requires internal reallocation and may require additional memory.
|
||||||
|
On low RAM systems it can cause errors.
|
||||||
|
Do not use too high hashsize. This way you waste your RAM. And dont use too low hashsize to avoid reallocs.
|
||||||
|
|
||||||
|
ip2net options. separate for ipv4 and ipv6.
|
||||||
|
IP2NET_OPT4="--prefix-length=22-30 --v4-threshold=3/4"
|
||||||
|
IP2NET_OPT6="--prefix-length=56-64 --v6-threshold=5"
|
||||||
|
|
||||||
|
Enable gzip compression for large lists. Used by ipset/*.sh scripts.
|
||||||
|
GZIP_LISTS=1
|
||||||
|
|
||||||
|
Command to reload ip/host lists after update.
|
||||||
|
Comment or leave empty for auto backend selection : ipset or ipfw if present.
|
||||||
|
On BSD systems with PF no auto reloading happens. You must provide your own command.
|
||||||
|
Newer FreeBSD versions support table only reloading : pfctl -Tl -f /etc/pf.conf
|
||||||
|
Set to "-" to disable reload.
|
||||||
|
LISTS_RELOAD="pfctl -f /etc/pf.conf"
|
||||||
|
|
||||||
|
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, OpenWRT and MacOS.
|
||||||
|
Run install_easy.sh and answer its questions.
|
||||||
|
|
||||||
|
Simple install to openwrt
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
install_easy.sh 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 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, ...
|
||||||
|
|
||||||
|
How to run tpws on root-less android.
|
||||||
|
You can't write to /system, /data, can't run from sd card.
|
||||||
|
Selinux prevents running executables in /data/local/tmp from apps.
|
||||||
|
Use adb and adb shell.
|
||||||
|
mkdir /data/local/tmp/zapret
|
||||||
|
adb push tpws /data/local/tmp/zapret
|
||||||
|
chmod 755 /data/local/tmp/zapret /data/local/tmp/zapret/tpws
|
||||||
|
chcon u:object_r:system_file:s0 /data/local/tmp/zapret/tpws
|
||||||
|
Now its possible to run /data/local/tmp/zapret/tpws from any app such as tasker.
|
||||||
|
|
||||||
|
|
||||||
|
FreeBSD, OpenBSD, MacOS
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
see docs/bsd.eng.txt
|
||||||
|
|
||||||
|
|
||||||
|
Windows (WSL)
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Using WSL (Windows subsystem for Linux) it's possible to run tpws in socks mode under rather new builds of
|
||||||
|
windows 10 and windows server.
|
||||||
|
Its not required to install any linux distributions as suggested in most articles.
|
||||||
|
tpws is static binary. It doesn't need a distribution.
|
||||||
|
|
||||||
|
Install WSL : dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all
|
||||||
|
Copy binaries/x86_64/tpws_wsl.tgz to the target system.
|
||||||
|
Run : wsl --import tpws "%USERPROFILE%\tpws" tpws_wsl.tgz
|
||||||
|
Run tpws : wsl --exec /tpws --uid=1 --no-resolve --socks --bind-addr=127.0.0.1 --port=1080 <fooling_options>
|
||||||
|
Configure socks as 127.0.0.1:1080 in a browser or another program.
|
||||||
|
|
||||||
|
Cleanup : wsl --unregister tpws
|
||||||
|
|
||||||
|
Tested in windows 10 build 19041 (20.04).
|
||||||
|
|
||||||
|
NOTICE. There is native windows solution GoodByeDPI. It works on packet level like nfqws.
|
||||||
|
|
||||||
|
|
||||||
|
Other devices
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Author's goal does not include easy supporting as much devices as possibles.
|
||||||
|
Please do not ask for easy supporting firmwares. It requires a lot of work and owning lots of devices. Its counterproductive.
|
||||||
|
As a devices owner its easier for you and should not be too hard if firmware is open.
|
||||||
|
Most closed stock firmwares are not designed for custom usage and sometimes actively prevent it.
|
||||||
|
In the latter case you have to hack into it and reverse engineer. Its not easy.
|
||||||
|
Binaries are universal. They can run on almost all firmwares.
|
||||||
|
You will need :
|
||||||
|
* root shell access. true sh shell, not microtik-like console
|
||||||
|
* startup hook
|
||||||
|
* r/w partition to store binaries and startup script with executable permission (+x)
|
||||||
|
* tpws can be run almost anywhere but nfqws require kernel support for NFQUEUE. Its missing in most firmwares.
|
||||||
|
* too old 2.6 kernels are unsupported and can cause errors
|
||||||
|
If binaries crash with segfault (rare but happens on some kernels) try to unpack upx like this : upx -d tpws.
|
||||||
|
First manually debug your scenario. Run iptables + daemon and check if its what you want.
|
||||||
|
Write your own script with iptables magic and run required daemon from there. Put it to startup.
|
||||||
|
Dont ask me how to do it. Its different for all firmwares and requires studying.
|
||||||
|
Find manual or reverse engineer yourself.
|
||||||
|
Check for race conditions. Firmware can clear or modify iptables after your startup script.
|
||||||
|
If this is the case then run another script in background and add some delay there.
|
||||||
|
|
||||||
|
|
||||||
|
Https blocking bypass
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
OTHERWISE 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)
|
1314
docs/readme.txt
Normal file
1314
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. В умелых руках творят чудеса.
|
9
files/fake/fake_http_req_example.bin
Normal file
9
files/fake/fake_http_req_example.bin
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
GET / HTTP/1.1
|
||||||
|
Host: www.iana.org
|
||||||
|
Connection: keep-alive
|
||||||
|
Upgrade-Insecure-Requests: 1
|
||||||
|
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4300.0 Safari/537.36
|
||||||
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
Accept-Language: en-US,en;q=0.9,ru;q=0.8
|
||||||
|
|
BIN
files/fake/fake_tls13_clienthello_example.bin
Normal file
BIN
files/fake/fake_tls13_clienthello_example.bin
Normal file
Binary file not shown.
35
files/huawei/E8372/run-zapret-hostlist
Executable file
35
files/huawei/E8372/run-zapret-hostlist
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
#!/system/bin/busybox sh
|
||||||
|
|
||||||
|
# download hostlist from http(s) (need curl, its absent by default),
|
||||||
|
# feed it to zapret. save flash write cycles
|
||||||
|
|
||||||
|
u="https://your.host.com/censorship/hoslist.txt"
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
d=/data/censorship
|
||||||
|
[ -d $d ] || mkdir $d
|
||||||
|
f=$d/hostlist.txt
|
||||||
|
t=/hostlist.txt
|
||||||
|
|
||||||
|
curl -k --fail --max-time 10 -o "$t" "$u" && {
|
||||||
|
if [ -s "$t" ]; then
|
||||||
|
m1=$(md5sum "$t" | cut -d ' ' -f 1)
|
||||||
|
m2=$(md5sum "$f" | cut -d ' ' -f 1)
|
||||||
|
echo $m1 $m2
|
||||||
|
if [ -z "$m2" ] || [ "$m1" != "$m2" ]; then
|
||||||
|
echo updating hostlist
|
||||||
|
cp -f "$t" "$f"
|
||||||
|
else
|
||||||
|
echo hostlist was not changed. keeping old copy
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo downloaded hostlist is empty. disabling zapret
|
||||||
|
rm "$f"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
rm -f "$t"
|
||||||
|
"$EXEDIR/unzapret"
|
||||||
|
[ -s "$f" ] && exec "$EXEDIR/zapret" "--hostlist=$f"
|
39
files/huawei/E8372/run-zapret-ip
Executable file
39
files/huawei/E8372/run-zapret-ip
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
#!/system/bin/busybox sh
|
||||||
|
|
||||||
|
# download hostlist from http(s) (need curl, its absent by default),
|
||||||
|
# resolve to ip list, feed to zapret-ip. save flash write cycles
|
||||||
|
|
||||||
|
u="https://your.host.com/censorship/hoslist.txt"
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
d=/data/censorship
|
||||||
|
[ -d $d ] || mkdir $d
|
||||||
|
f=$d/hostlist.txt
|
||||||
|
t=/hostlist.txt
|
||||||
|
i=/iplist.txt
|
||||||
|
|
||||||
|
curl -k --fail --max-time 10 -o "$t" "$u" && {
|
||||||
|
if [ -s "$t" ]; then
|
||||||
|
m1=$(md5sum "$t" | cut -d ' ' -f 1)
|
||||||
|
m2=$(md5sum "$f" | cut -d ' ' -f 1)
|
||||||
|
echo $m1 $m2
|
||||||
|
if [ -z "$m2" ] || [ "$m1" != "$m2" ]; then
|
||||||
|
echo updating hostlist
|
||||||
|
cp -f "$t" "$f"
|
||||||
|
else
|
||||||
|
echo hostlist was not changed. keeping old copy
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo downloaded hostlist is empty. disabling zapret
|
||||||
|
rm "$f"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
rm -f "$t"
|
||||||
|
"$EXEDIR/unzapret-ip"
|
||||||
|
[ -s "$f" ] && {
|
||||||
|
mdig --threads=10 --family=4 <"$f" >"$i"
|
||||||
|
[ -s "$i" ] && exec "$EXEDIR/zapret-ip" "$i"
|
||||||
|
}
|
BIN
files/huawei/E8372/unfuck_nfqueue.ko
Normal file
BIN
files/huawei/E8372/unfuck_nfqueue.ko
Normal file
Binary file not shown.
9
files/huawei/E8372/unzapret
Executable file
9
files/huawei/E8372/unzapret
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/system/bin/busybox sh
|
||||||
|
|
||||||
|
rule="PREROUTING -t nat -i br0 ! -d 192.168.0.0/16 -p tcp -m multiport --dports 80,443 -j REDIRECT --to-port 1"
|
||||||
|
iptables -C $rule 2>/dev/null && iptables -D $rule
|
||||||
|
killall tpws
|
||||||
|
|
||||||
|
rule="OUTPUT -t mangle -o wan0 -p tcp -m multiport --dports 80,443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass"
|
||||||
|
iptables -C $rule 2>/dev/null && iptables -D $rule
|
||||||
|
killall nfqws
|
11
files/huawei/E8372/unzapret-ip
Executable file
11
files/huawei/E8372/unzapret-ip
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/system/bin/busybox sh
|
||||||
|
|
||||||
|
rule="PREROUTING -t nat -i br0 -p tcp -m multiport --dports 80,443 -j tpws"
|
||||||
|
iptables -C $rule 2>/dev/null && iptables -D $rule
|
||||||
|
iptables -F tpws -t nat
|
||||||
|
iptables -X tpws -t nat
|
||||||
|
killall tpws
|
||||||
|
|
||||||
|
rule="OUTPUT -t mangle -o wan0 -p tcp -m multiport --dports 80,443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass"
|
||||||
|
iptables -C $rule 2>/dev/null && iptables -D $rule
|
||||||
|
killall nfqws
|
15
files/huawei/E8372/zapret
Executable file
15
files/huawei/E8372/zapret
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
#!/system/bin/busybox sh
|
||||||
|
|
||||||
|
# $1 - additional parameters for nfqws
|
||||||
|
|
||||||
|
insmod /online/modules/unfuck_nfqueue.ko 2>/dev/null
|
||||||
|
|
||||||
|
rule="PREROUTING -t nat -i br0 ! -d 192.168.0.0/16 -p tcp -m multiport --dports 80,443 -j REDIRECT --to-port 1"
|
||||||
|
iptables -C $rule 2>/dev/null || iptables -I $rule
|
||||||
|
|
||||||
|
tpws --uid 1:3003 --port=1 --daemon
|
||||||
|
|
||||||
|
rule="OUTPUT -t mangle -o wan0 -p tcp -m multiport --dports 80,443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass"
|
||||||
|
iptables -C $rule 2>/dev/null || iptables -I $rule
|
||||||
|
|
||||||
|
nfqws --uid 2 --qnum=200 --dpi-desync=disorder --dpi-desync-ttl=8 --dpi-desync-fooling=md5sig --daemon $1
|
34
files/huawei/E8372/zapret-ip
Executable file
34
files/huawei/E8372/zapret-ip
Executable file
@ -0,0 +1,34 @@
|
|||||||
|
#!/system/bin/busybox sh
|
||||||
|
|
||||||
|
# $1 - ip list file. create individual rules for tpws redirection. ipset is not available
|
||||||
|
|
||||||
|
[ -z "$1" ] && {
|
||||||
|
echo need iplist file as parameter
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
insmod /online/modules/unfuck_nfqueue.ko 2>/dev/null
|
||||||
|
|
||||||
|
tpws --maxconn=1024 --uid 1:3003 --port=1 --daemon
|
||||||
|
|
||||||
|
|
||||||
|
REDIR="-j REDIRECT --to-port 1"
|
||||||
|
|
||||||
|
iptables -F tpws -t nat
|
||||||
|
iptables -X tpws -t nat
|
||||||
|
iptables -N tpws -t nat
|
||||||
|
iptables -A tpws -t nat -d 192.168.0.0/16 -j RETURN
|
||||||
|
|
||||||
|
while read ip; do
|
||||||
|
echo redirecting $ip
|
||||||
|
iptables -A tpws -t nat -d $ip -p tcp $REDIR
|
||||||
|
done <"$1"
|
||||||
|
|
||||||
|
|
||||||
|
rule="PREROUTING -t nat -i br0 -p tcp -m multiport --dports 80,443 -j tpws"
|
||||||
|
iptables -C $rule 2>/dev/null || iptables -I $rule
|
||||||
|
|
||||||
|
nfqws --uid 2 --qnum=200 --dpi-desync=disorder --dpi-desync-ttl=8 --dpi-desync-fooling=md5sig --daemon
|
||||||
|
|
||||||
|
rule="OUTPUT -t mangle -o wan0 -p tcp -m multiport --dports 80,443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass"
|
||||||
|
iptables -C $rule 2>/dev/null || iptables -I $rule
|
401
init.d/macos/functions
Normal file
401
init.d/macos/functions
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
[ -n "$ZAPRET_BASE" ] || ZAPRET_BASE=/opt/zapret
|
||||||
|
|
||||||
|
IPSET_DIR=$ZAPRET_BASE/ipset
|
||||||
|
. "$IPSET_DIR/def.sh"
|
||||||
|
|
||||||
|
HOSTLIST="$ZHOSTLIST.gz"
|
||||||
|
[ -f "$HOSTLIST" ] || HOSTLIST="$ZHOSTLIST"
|
||||||
|
[ -f "$HOSTLIST" ] || HOSTLIST="$ZUSERLIST"
|
||||||
|
|
||||||
|
PIDDIR=/var/run
|
||||||
|
TPPORT=988
|
||||||
|
TPWS_WAIT="--bind-wait-ip=60"
|
||||||
|
TPWS="$ZAPRET_BASE/tpws/tpws"
|
||||||
|
|
||||||
|
PF_MAIN="/etc/pf.conf"
|
||||||
|
PF_ANCHOR_DIR=/etc/pf.anchors
|
||||||
|
PF_ANCHOR_ZAPRET="$PF_ANCHOR_DIR/zapret"
|
||||||
|
PF_ANCHOR_ZAPRET_V4="$PF_ANCHOR_DIR/zapret-v4"
|
||||||
|
PF_ANCHOR_ZAPRET_V6="$PF_ANCHOR_DIR/zapret-v6"
|
||||||
|
|
||||||
|
[ -n "$IFACE_WAN" ] && OWAN=" on $IFACE_WAN"
|
||||||
|
|
||||||
|
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" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
local ARGS="--daemon --pidfile=$PIDFILE $3"
|
||||||
|
[ -f "$PIDFILE" ] && pgrep -qF "$PIDFILE" && {
|
||||||
|
echo Already running $1: $2
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
echo "Starting daemon $1: $2 $ARGS"
|
||||||
|
"$2" $ARGS
|
||||||
|
}
|
||||||
|
stop_daemon()
|
||||||
|
{
|
||||||
|
# $1 - daemon number : 1,2,3,...
|
||||||
|
# $2 - daemon
|
||||||
|
# use $PIDDIR/$DAEMONBASE$1.pid as pidfile
|
||||||
|
|
||||||
|
local PID
|
||||||
|
local DAEMONBASE="$(basename $2)"
|
||||||
|
local PIDFILE="$PIDDIR/$DAEMONBASE$1.pid"
|
||||||
|
[ -f "$PIDFILE" ] && read PID <"$PIDFILE"
|
||||||
|
[ -n "$PID" ] && {
|
||||||
|
echo "Stopping daemon $1: $2 (PID=$PID)"
|
||||||
|
kill $PID
|
||||||
|
rm -f "$PIDFILE"
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
do_daemon()
|
||||||
|
{
|
||||||
|
# $1 - 1 - run, 0 - stop
|
||||||
|
on_off_function run_daemon stop_daemon "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
filter_apply_hostlist_target()
|
||||||
|
{
|
||||||
|
# $1 - var name of tpws or nfqws params
|
||||||
|
[ "$MODE_FILTER" = "hostlist" ] && eval $1="\"\$$1 --hostlist=$HOSTLIST\""
|
||||||
|
}
|
||||||
|
tpws_apply_binds()
|
||||||
|
{
|
||||||
|
local o
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || o="--bind-addr=127.0.0.1"
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
for i in lo0 $IFACE_LAN; do
|
||||||
|
o="$o --bind-iface6=$i --bind-linklocal=force $TPWS_WAIT"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
eval $1="\"\$$1 $o\""
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_interface_ll()
|
||||||
|
{
|
||||||
|
echo waiting for an ipv6 link local address on $1 ...
|
||||||
|
"$TPWS" --bind-wait-only --bind-iface6=$1 --bind-linklocal=force $TPWS_WAIT
|
||||||
|
}
|
||||||
|
wait_lan_ll()
|
||||||
|
{
|
||||||
|
[ "$DISABLE_IPV6" != "1" ] && [ -n "$IFACE_LAN" ] && {
|
||||||
|
wait_interface_ll $IFACE_LAN >&2 || {
|
||||||
|
echo "wait interface failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
get_ipv6_linklocal()
|
||||||
|
{
|
||||||
|
ifconfig $1 | sed -nEe 's/^.*inet6 (fe80:[a-f0-9:]+).*/\1/p'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pf_anchor_root_reload()
|
||||||
|
{
|
||||||
|
echo reloading PF root anchor
|
||||||
|
pfctl -qf "$PF_MAIN"
|
||||||
|
}
|
||||||
|
|
||||||
|
pf_anchor_root()
|
||||||
|
{
|
||||||
|
local patch
|
||||||
|
[ -f "$PF_MAIN" ] && {
|
||||||
|
grep -q '^rdr-anchor "zapret"$' "$PF_MAIN" || {
|
||||||
|
echo patching rdr-anchor in $PF_MAIN
|
||||||
|
patch=1
|
||||||
|
sed -i '' -e '/^rdr-anchor "com\.apple\/\*"$/i \
|
||||||
|
rdr-anchor "zapret"
|
||||||
|
' $PF_MAIN
|
||||||
|
}
|
||||||
|
grep -q '^anchor "zapret"$' "$PF_MAIN" || {
|
||||||
|
echo patching anchor in $PF_MAIN
|
||||||
|
patch=1
|
||||||
|
sed -i '' -e '/^anchor "com\.apple\/\*"$/i \
|
||||||
|
anchor "zapret"
|
||||||
|
' $PF_MAIN
|
||||||
|
}
|
||||||
|
grep -q "^set limit table-entries" "$PF_MAIN" || {
|
||||||
|
echo patching table-entries limit
|
||||||
|
patch=1
|
||||||
|
sed -i '' -e '/^scrub-anchor "com\.apple\/\*"$/i \
|
||||||
|
set limit table-entries 5000000
|
||||||
|
' $PF_MAIN
|
||||||
|
}
|
||||||
|
|
||||||
|
grep -q '^anchor "zapret"$' "$PF_MAIN" &&
|
||||||
|
grep -q '^rdr-anchor "zapret"$' "$PF_MAIN" &&
|
||||||
|
grep -q '^set limit table-entries' "$PF_MAIN" && {
|
||||||
|
if [ -n "$patch" ]; then
|
||||||
|
echo successfully patched $PF_MAIN
|
||||||
|
pf_anchor_root_reload
|
||||||
|
else
|
||||||
|
echo successfully checked zapret anchors in $PF_MAIN
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo ----------------------------------
|
||||||
|
echo Automatic $PF_MAIN patching failed. You must apply root anchors manually in your PF config.
|
||||||
|
echo rdr-anchor \"zapret\"
|
||||||
|
echo anchor \"zapret\"
|
||||||
|
echo ----------------------------------
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
pf_anchor_root_del()
|
||||||
|
{
|
||||||
|
sed -i '' -e '/^anchor "zapret"$/d' -e '/^rdr-anchor "zapret"$/d' -e '/^set limit table-entries/d' "$PF_MAIN"
|
||||||
|
}
|
||||||
|
|
||||||
|
pf_anchor_zapret()
|
||||||
|
{
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || {
|
||||||
|
if [ -f "$ZIPLIST_EXCLUDE" ]; then
|
||||||
|
echo "table <nozapret> persist file \"$ZIPLIST_EXCLUDE\""
|
||||||
|
else
|
||||||
|
echo "table <nozapret> persist"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || {
|
||||||
|
if [ -f "$ZIPLIST_EXCLUDE6" ]; then
|
||||||
|
echo "table <nozapret6> persist file \"$ZIPLIST_EXCLUDE6\""
|
||||||
|
else
|
||||||
|
echo "table <nozapret6> persist"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
echo
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || echo "rdr-anchor \"/zapret-v4\" inet to !<nozapret>"
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || echo "rdr-anchor \"/zapret-v6\" inet6 to !<nozapret6>"
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || echo "anchor \"/zapret-v4\" inet to !<nozapret>"
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || echo "anchor \"/zapret-v6\" inet6 to !<nozapret6>"
|
||||||
|
}
|
||||||
|
pf_anchor_zapret_tables()
|
||||||
|
{
|
||||||
|
# $1 - variable to receive applied table names
|
||||||
|
# $2/$3 $4/$5 ... table_name/table_file
|
||||||
|
local tblv=$1
|
||||||
|
local _tbl
|
||||||
|
|
||||||
|
shift
|
||||||
|
[ "$MODE_FILTER" = "ipset" ] &&
|
||||||
|
{
|
||||||
|
while [ -n "$1" ] && [ -n "$2" ] ; do
|
||||||
|
[ -f "$2" ] && {
|
||||||
|
echo "table <$1> file \"$2\""
|
||||||
|
_tbl="$_tbl<$1> "
|
||||||
|
}
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
}
|
||||||
|
[ -n "$_tbl" ] || _tbl="any"
|
||||||
|
|
||||||
|
eval $tblv="\"\$_tbl\""
|
||||||
|
}
|
||||||
|
pf_anchor_port_target()
|
||||||
|
{
|
||||||
|
if [ "$MODE_HTTP" = "1" ] && [ "$MODE_HTTPS" = "1" ]; then
|
||||||
|
echo "{80,443}"
|
||||||
|
elif [ "$MODE_HTTPS" = "1" ]; then
|
||||||
|
echo "443"
|
||||||
|
elif [ "$MODE_HTTP" = "1" ]; then
|
||||||
|
echo "80"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
pf_anchor_zapret_v4()
|
||||||
|
{
|
||||||
|
local tbl port
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || {
|
||||||
|
[ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ] && return
|
||||||
|
pf_anchor_zapret_tables tbl zapret-user "$ZIPLIST_USER" zapret "$ZIPLIST"
|
||||||
|
port=$(pf_anchor_port_target)
|
||||||
|
for t in $tbl; do
|
||||||
|
[ -n "$IFACE_LAN" ] && echo "rdr on $IFACE_LAN inet proto tcp from any to $t port $port -> 127.0.0.1 port $TPPORT"
|
||||||
|
done
|
||||||
|
echo "rdr on lo0 inet proto tcp from !127.0.0.0/8 to any port $port -> 127.0.0.1 port $TPPORT"
|
||||||
|
for t in $tbl; do
|
||||||
|
echo "pass out$OWAN route-to (lo0 127.0.0.1) inet proto tcp from !127.0.0.0/8 to $t port $port user { >root }"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pf_anchor_zapret_v6()
|
||||||
|
{
|
||||||
|
local tbl port LL_LAN
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
[ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ] && return
|
||||||
|
|
||||||
|
# LAN link local is only for router
|
||||||
|
[ -n "$IFACE_LAN" ] && LL_LAN=$(get_ipv6_linklocal $IFACE_LAN)
|
||||||
|
|
||||||
|
pf_anchor_zapret_tables tbl zapret6-user "$ZIPLIST_USER6" zapret6 "$ZIPLIST6"
|
||||||
|
port=$(pf_anchor_port_target)
|
||||||
|
for t in $tbl; do
|
||||||
|
[ -n "$LL_LAN" ] && echo "rdr on $IFACE_LAN inet6 proto tcp from any to $t port $port -> $LL_LAN port $TPPORT"
|
||||||
|
done
|
||||||
|
echo "rdr on lo0 inet6 proto tcp from !::1 to any port $port -> fe80::1 port $TPPORT"
|
||||||
|
for t in $tbl; do
|
||||||
|
echo "pass out$OWAN route-to (lo0 fe80::1) inet6 proto tcp from !::1 to $t port $port user { >root }"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pf_anchors_create()
|
||||||
|
{
|
||||||
|
wait_lan_ll
|
||||||
|
pf_anchor_zapret >"$PF_ANCHOR_ZAPRET"
|
||||||
|
pf_anchor_zapret_v4 >"$PF_ANCHOR_ZAPRET_V4"
|
||||||
|
pf_anchor_zapret_v6 >"$PF_ANCHOR_ZAPRET_V6"
|
||||||
|
}
|
||||||
|
pf_anchors_del()
|
||||||
|
{
|
||||||
|
rm -f "$PF_ANCHOR_ZAPRET" "$PF_ANCHOR_ZAPRET_V4" "$PF_ANCHOR_ZAPRET_V6"
|
||||||
|
}
|
||||||
|
pf_anchors_load()
|
||||||
|
{
|
||||||
|
echo loading zapret anchor from "$PF_ANCHOR_ZAPRET"
|
||||||
|
pfctl -qa zapret -f "$PF_ANCHOR_ZAPRET" || {
|
||||||
|
echo error loading zapret anchor
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if [ "$DISABLE_IPV4" = "1" ]; then
|
||||||
|
echo clearing zapret-v4 anchor
|
||||||
|
pfctl -qa zapret-v4 -F all 2>/dev/null
|
||||||
|
else
|
||||||
|
echo loading zapret-v4 anchor from "$PF_ANCHOR_ZAPRET_V4"
|
||||||
|
pfctl -qa zapret-v4 -f "$PF_ANCHOR_ZAPRET_V4" || {
|
||||||
|
echo error loading zapret-v4 anchor
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
if [ "$DISABLE_IPV6" = "1" ]; then
|
||||||
|
echo clearing zapret-v6 anchor
|
||||||
|
pfctl -qa zapret-v6 -F all 2>/dev/null
|
||||||
|
else
|
||||||
|
echo loading zapret-v6 anchor from "$PF_ANCHOR_ZAPRET_V6"
|
||||||
|
pfctl -qa zapret-v6 -f "$PF_ANCHOR_ZAPRET_V6" || {
|
||||||
|
echo error loading zapret-v6 anchor
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
echo successfully loaded PF anchors
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
pf_anchors_clear()
|
||||||
|
{
|
||||||
|
echo clearing zapret anchors
|
||||||
|
pfctl -qa zapret-v4 -F all 2>/dev/null
|
||||||
|
pfctl -qa zapret-v6 -F all 2>/dev/null
|
||||||
|
pfctl -qa zapret -F all 2>/dev/null
|
||||||
|
}
|
||||||
|
pf_enable()
|
||||||
|
{
|
||||||
|
echo enabling PF
|
||||||
|
pfctl -qe
|
||||||
|
}
|
||||||
|
pf_table_reload()
|
||||||
|
{
|
||||||
|
echo reloading zapret tables
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || pfctl -qTl -a zapret-v4 -f "$PF_ANCHOR_ZAPRET_V4"
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || pfctl -qTl -a zapret-v6 -f "$PF_ANCHOR_ZAPRET_V6"
|
||||||
|
pfctl -qTl -a zapret -f "$PF_ANCHOR_ZAPRET"
|
||||||
|
}
|
||||||
|
zapret_do_firewall()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
|
||||||
|
case "${MODE}" in
|
||||||
|
tpws)
|
||||||
|
if [ "$1" = "1" ] ; then
|
||||||
|
pf_anchor_root || return 1
|
||||||
|
pf_anchors_create
|
||||||
|
pf_anchors_load || return 1
|
||||||
|
pf_enable
|
||||||
|
else
|
||||||
|
pf_anchors_clear
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
filter)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "unsupported MODE=$MODE"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
zapret_apply_firewall()
|
||||||
|
{
|
||||||
|
zapret_do_firewall 1 "$@"
|
||||||
|
}
|
||||||
|
zapret_unapply_firewall()
|
||||||
|
{
|
||||||
|
zapret_do_firewall 0 "$@"
|
||||||
|
}
|
||||||
|
zapret_restart_firewall()
|
||||||
|
{
|
||||||
|
zapret_unapply_firewall "$@"
|
||||||
|
zapret_apply_firewall "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
zapret_do_daemons()
|
||||||
|
{
|
||||||
|
# $1 - 1 - run, 0 - stop
|
||||||
|
|
||||||
|
local opt
|
||||||
|
|
||||||
|
case "${MODE}" in
|
||||||
|
tpws)
|
||||||
|
[ "$1" = "1" ] && [ "$DISABLE_IPV4" = "1" ] && [ "$DISABLE_IPV6" = "1" ] && {
|
||||||
|
echo "both ipv4 and ipv6 are disabled. nothing to do"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
# MacOS requires root. kernel hardcoded requirement for /dev/pf ioctls
|
||||||
|
opt="--user=root --port=$TPPORT"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
tpws_apply_binds opt
|
||||||
|
opt="$opt $TPWS_OPT"
|
||||||
|
do_daemon $1 1 "$TPWS" "$opt"
|
||||||
|
;;
|
||||||
|
filter)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "unsupported MODE=$MODE"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
zapret_run_daemons()
|
||||||
|
{
|
||||||
|
zapret_do_daemons 1 "$@"
|
||||||
|
}
|
||||||
|
zapret_stop_daemons()
|
||||||
|
{
|
||||||
|
zapret_do_daemons 0 "$@"
|
||||||
|
}
|
||||||
|
zapret_restart_daemons()
|
||||||
|
{
|
||||||
|
zapret_stop_daemons "$@"
|
||||||
|
zapret_run_daemons "$@"
|
||||||
|
}
|
51
init.d/macos/zapret
Executable file
51
init.d/macos/zapret
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
EXEDIR="$(dirname "$0")"
|
||||||
|
ZAPRET_BASE="$EXEDIR/../.."
|
||||||
|
ZAPRET_BASE="$(cd "$ZAPRET_BASE"; pwd)"
|
||||||
|
|
||||||
|
. "$EXEDIR/functions"
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
zapret_run_daemons
|
||||||
|
[ "$INIT_APPLY_FW" != "1" ] || zapret_apply_firewall
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
[ "$INIT_APPLY_FW" != "1" ] || zapret_unapply_firewall
|
||||||
|
zapret_stop_daemons
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
"$0" stop
|
||||||
|
"$0" start
|
||||||
|
;;
|
||||||
|
|
||||||
|
start-fw)
|
||||||
|
zapret_apply_firewall
|
||||||
|
;;
|
||||||
|
stop-fw)
|
||||||
|
zapret_unapply_firewall
|
||||||
|
;;
|
||||||
|
restart-fw)
|
||||||
|
zapret_restart_firewall
|
||||||
|
;;
|
||||||
|
reload-fw-tables)
|
||||||
|
pf_table_reload
|
||||||
|
;;
|
||||||
|
|
||||||
|
start-daemons)
|
||||||
|
zapret_run_daemons
|
||||||
|
;;
|
||||||
|
stop-daemons)
|
||||||
|
zapret_stop_daemons
|
||||||
|
;;
|
||||||
|
restart-daemons)
|
||||||
|
zapret_restart_daemons
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
N="$SCRIPT/$NAME"
|
||||||
|
echo "Usage: $N {start|stop|start-fw|stop-fw|restart-fw|reload-fw-tables|start-daemons|stop-daemons|restart-daemons}" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
17
init.d/macos/zapret.plist
Normal file
17
init.d/macos/zapret.plist
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Label</key>
|
||||||
|
<string>zapret</string>
|
||||||
|
<key>LaunchOnlyOnce</key>
|
||||||
|
<false/>
|
||||||
|
<key>ProgramArguments</key>
|
||||||
|
<array>
|
||||||
|
<string>/opt/zapret/init.d/macos/zapret</string>
|
||||||
|
<string>start</string>
|
||||||
|
</array>
|
||||||
|
<key>RunAtLoad</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
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 restart
|
||||||
|
}
|
||||||
|
}
|
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
|
||||||
|
}
|
44
init.d/openwrt/custom-2nfqws
Normal file
44
init.d/openwrt/custom-2nfqws
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# this custom script demonstrates how to use 2 copies of nfqws
|
||||||
|
# it preserves config settings : MODE_HTTP, MODE_HTTP_KEEPALIVE, MODE_HTTPS, MODE_FILTER, NFQWS_OPT_DESYNC
|
||||||
|
# NFQWS_OPT_DESYNC - parameters for http
|
||||||
|
# NFQWS_OPT_DESYNC2 - parameters for https. you should add this variable to config file, its absent there
|
||||||
|
|
||||||
|
QNUM2=$(($QNUM+1))
|
||||||
|
|
||||||
|
zapret_custom_daemons()
|
||||||
|
{
|
||||||
|
local opt
|
||||||
|
|
||||||
|
[ "$MODE_HTTP" = "1" ] && {
|
||||||
|
opt="$NFQWS_OPT_BASE $NFQWS_OPT_DESYNC"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
run_daemon 1 $NFQWS "$opt"
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$MODE_HTTPS" = "1" ] && {
|
||||||
|
opt="$NFQWS_OPT_BASE $NFQWS_OPT_DESYNC2 --qnum=$QNUM2"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
run_daemon 2 $NFQWS "$opt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zapret_custom_firewall()
|
||||||
|
{
|
||||||
|
local f4 f6
|
||||||
|
local first_packet_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4"
|
||||||
|
local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
|
||||||
|
|
||||||
|
[ "$MODE_HTTP" = "1" ] && {
|
||||||
|
f4="--dport 80"
|
||||||
|
[ "$MODE_HTTP_KEEPALIVE" = "1" ] || f4="$f4 $first_packet_only"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post "$f4 $desync" "$f6 $desync" $QNUM
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$MODE_HTTPS" = "1" ] && {
|
||||||
|
f4="--dport 443 $first_packet_only"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post "$f4 $desync" "$f6 $desync" $QNUM2
|
||||||
|
}
|
||||||
|
}
|
39
init.d/openwrt/custom-tpws4http-nfqws4https
Normal file
39
init.d/openwrt/custom-tpws4http-nfqws4https
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# this custom script demonstrates how to apply tpws to http and nfqws to https
|
||||||
|
# it preserves config settings : MODE_HTTP, MODE_HTTPS, MODE_FILTER, TPWS_OPT, NFQWS_OPT_DESYNC
|
||||||
|
|
||||||
|
zapret_custom_daemons()
|
||||||
|
{
|
||||||
|
local opt
|
||||||
|
|
||||||
|
[ "$MODE_HTTP" = "1" ] && {
|
||||||
|
opt="$TPWS_OPT"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
run_tpws 1 "$opt"
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$MODE_HTTPS" = "1" ] && {
|
||||||
|
opt="$NFQWS_OPT_BASE $NFQWS_OPT_DESYNC"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
run_daemon 2 $NFQWS "$opt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zapret_custom_firewall()
|
||||||
|
{
|
||||||
|
local f4 f6
|
||||||
|
local first_packet_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4"
|
||||||
|
local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
|
||||||
|
|
||||||
|
[ "$MODE_HTTP" = "1" ] && {
|
||||||
|
f4="--dport 80"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_tpws "$f4" "$f6" $TPPORT
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$MODE_HTTPS" = "1" ] && {
|
||||||
|
f4="--dport 443 $first_packet_only"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post "$f4 $desync" "$f6 $desync" $QNUM
|
||||||
|
}
|
||||||
|
}
|
20
init.d/openwrt/custom.default
Normal file
20
init.d/openwrt/custom.default
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
|
||||||
|
}
|
11
init.d/openwrt/firewall.zapret
Normal file
11
init.d/openwrt/firewall.zapret
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
SCRIPT=$(readlink /etc/init.d/zapret)
|
||||||
|
if [ -n "$SCRIPT" ]; then
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
ZAPRET_BASE=$(readlink -f "$EXEDIR/../..")
|
||||||
|
else
|
||||||
|
ZAPRET_BASE=/opt/zapret
|
||||||
|
fi
|
||||||
|
|
||||||
|
. "$ZAPRET_BASE/init.d/openwrt/functions"
|
||||||
|
|
||||||
|
zapret_apply_firewall
|
425
init.d/openwrt/functions
Normal file
425
init.d/openwrt/functions
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
. /lib/functions/network.sh
|
||||||
|
|
||||||
|
[ -n "$ZAPRET_BASE" ] || ZAPRET_BASE=/opt/zapret
|
||||||
|
. "$ZAPRET_BASE/config"
|
||||||
|
|
||||||
|
QNUM=200
|
||||||
|
TPPORT=988
|
||||||
|
TPWS_USER=daemon
|
||||||
|
TPWS_LOCALHOST4=127.0.0.127
|
||||||
|
[ -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 "$@"
|
||||||
|
}
|
||||||
|
ipt_del()
|
||||||
|
{
|
||||||
|
iptables -C "$@" 2>/dev/null && iptables -D "$@"
|
||||||
|
}
|
||||||
|
ipt6()
|
||||||
|
{
|
||||||
|
ip6tables -C "$@" 2>/dev/null || ip6tables -I "$@"
|
||||||
|
}
|
||||||
|
ipt6_del()
|
||||||
|
{
|
||||||
|
ip6tables -C "$@" 2>/dev/null && ip6tables -D "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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 $TPWS_LOCALHOST4:$2
|
||||||
|
done
|
||||||
|
ipt prerouting_lan_rule -t nat -p tcp $1 $IPSET_EXCLUDE dst -j DNAT --to $TPWS_LOCALHOST4:$2
|
||||||
|
network_get_device DEVICE lan
|
||||||
|
[ -n "$DEVICE" ] && {
|
||||||
|
# allow localnet route only to special tpws IP
|
||||||
|
iptables -N input_lan_rule_zapret 2>/dev/null
|
||||||
|
ipt input_lan_rule_zapret -d 127.0.0.0/8 -j DROP
|
||||||
|
ipt input_lan_rule_zapret -d $TPWS_LOCALHOST4 -j RETURN
|
||||||
|
ipt input_lan_rule -j input_lan_rule_zapret
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
filter_apply_port_target()
|
||||||
|
{
|
||||||
|
# $1 - var name of iptables filter
|
||||||
|
local f
|
||||||
|
if [ "$MODE_HTTP" = "1" ] && [ "$MODE_HTTPS" = "1" ]; then
|
||||||
|
f="-m multiport --dports 80,443"
|
||||||
|
elif [ "$MODE_HTTPS" = "1" ]; then
|
||||||
|
f="--dport 443"
|
||||||
|
elif [ "$MODE_HTTP" = "1" ]; then
|
||||||
|
f="--dport 80"
|
||||||
|
else
|
||||||
|
echo WARNING !!! HTTP and HTTPS are both disabled
|
||||||
|
fi
|
||||||
|
eval $1="\"\$$1 $f\""
|
||||||
|
}
|
||||||
|
filter_apply_ipset_target()
|
||||||
|
{
|
||||||
|
# $1 - var name of ipv4 iptables filter
|
||||||
|
# $2 - var name of ipv6 iptables filter
|
||||||
|
if [ "$MODE_FILTER" = "ipset" ]; then
|
||||||
|
eval $1="\"\$$1 -m set --match-set zapret dst\""
|
||||||
|
eval $2="\"\$$2 -m set --match-set zapret6 dst\""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
create_ipset()
|
||||||
|
{
|
||||||
|
echo "Creating ipset"
|
||||||
|
"$IPSET_CR" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
is_flow_offload_avail()
|
||||||
|
{
|
||||||
|
# $1 = '' for ipv4, '6' for ipv6
|
||||||
|
grep -q FLOWOFFLOAD /proc/net/ip$1_tables_targets
|
||||||
|
}
|
||||||
|
list_nfqws_rules()
|
||||||
|
{
|
||||||
|
# $1 = '' for ipv4, '6' for ipv6
|
||||||
|
ip$1tables -S POSTROUTING -t mangle | grep "NFQUEUE --queue-num $QNUM --queue-bypass" | sed -re 's/^-A POSTROUTING (.*) -j NFQUEUE.*$/\1/' -e "s/-m mark ! --mark $DESYNC_MARK\/$DESYNC_MARK//"
|
||||||
|
}
|
||||||
|
reverse_nfqws_rule()
|
||||||
|
{
|
||||||
|
sed -e 's/-o /-i /' -e 's/--dport /--sport /' -e 's/--dports /--sports /' -e 's/ dst$/ src/' -e 's/ dst / src /'
|
||||||
|
}
|
||||||
|
apply_flow_offloading_enable_rule()
|
||||||
|
{
|
||||||
|
# $1 = '' for ipv4, '6' for ipv6
|
||||||
|
local i off='-j FLOWOFFLOAD'
|
||||||
|
[ "$FLOWOFFLOAD" = "hardware" ] && off="$off --hw"
|
||||||
|
i="forwarding_rule_zapret -m comment --comment zapret_traffic_offloading_enable -m conntrack --ctstate RELATED,ESTABLISHED $off"
|
||||||
|
echo enabling ipv${1:-4} flow offloading : $i
|
||||||
|
ip$1tables -A $i
|
||||||
|
}
|
||||||
|
apply_flow_offloading_exempt_rule()
|
||||||
|
{
|
||||||
|
# $1 = '' for ipv4, '6' for ipv6
|
||||||
|
local i v
|
||||||
|
v=$1
|
||||||
|
shift
|
||||||
|
i="forwarding_rule_zapret $@ -m comment --comment zapret_traffic_offloading_exemption -j RETURN"
|
||||||
|
echo applying ipv${v:-4} flow offloading exemption : $i
|
||||||
|
ip${v}tables -A $i
|
||||||
|
}
|
||||||
|
flow_offloading_exempt_v()
|
||||||
|
{
|
||||||
|
# $1 = '' for ipv4, '6' for ipv6
|
||||||
|
|
||||||
|
is_flow_offload_avail $1 || return 0
|
||||||
|
|
||||||
|
ipt$1_del forwarding_rule -j forwarding_rule_zapret
|
||||||
|
ip$1tables -F forwarding_rule_zapret 2>/dev/null
|
||||||
|
ip$1tables -X forwarding_rule_zapret 2>/dev/null
|
||||||
|
|
||||||
|
[ "$FLOWOFFLOAD" = 'software' -o "$FLOWOFFLOAD" = 'hardware' ] && {
|
||||||
|
ip$1tables -N forwarding_rule_zapret
|
||||||
|
|
||||||
|
list_nfqws_rules $1 |
|
||||||
|
while read rule; do
|
||||||
|
apply_flow_offloading_exempt_rule "$1" $rule
|
||||||
|
done
|
||||||
|
|
||||||
|
list_nfqws_rules $1 | grep -v "connbytes" | reverse_nfqws_rule |
|
||||||
|
while read rule; do
|
||||||
|
apply_flow_offloading_exempt_rule "$1" $rule
|
||||||
|
done
|
||||||
|
|
||||||
|
apply_flow_offloading_enable_rule $1
|
||||||
|
|
||||||
|
ipt$1 forwarding_rule -j forwarding_rule_zapret
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
flow_offloading_exempt()
|
||||||
|
{
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || flow_offloading_exempt_v
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || flow_offloading_exempt_v 6
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
zapret_apply_firewall()
|
||||||
|
{
|
||||||
|
local first_packet_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4"
|
||||||
|
local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
|
||||||
|
local f4 f6
|
||||||
|
|
||||||
|
# always create ipsets. ip_exclude ipset is required
|
||||||
|
create_ipset no-update
|
||||||
|
|
||||||
|
case "${MODE}" in
|
||||||
|
tpws)
|
||||||
|
if [ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ]; then
|
||||||
|
echo both http and https are disabled. not applying redirection.
|
||||||
|
else
|
||||||
|
filter_apply_port_target f4
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_tpws "$f4" "$f6" $TPPORT
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
nfqws)
|
||||||
|
if [ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ]; then
|
||||||
|
echo both http and https are disabled. not applying redirection.
|
||||||
|
else
|
||||||
|
if [ "$MODE_HTTP_KEEPALIVE" = "1" ]; then
|
||||||
|
if [ "$MODE_HTTP" = "1" ]; then
|
||||||
|
f4="--dport 80"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post "$f4 $desync" "$f6 $desync" $QNUM
|
||||||
|
fi
|
||||||
|
if [ "$MODE_HTTPS" = "1" ]; then
|
||||||
|
f4="--dport 443 $first_packet_only"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post "$f4 $desync" "$f6 $desync" $QNUM
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
filter_apply_port_target f4
|
||||||
|
f4="$f4 $first_packet_only"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post "$f4 $desync" "$f6 $desync" $QNUM
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
custom)
|
||||||
|
existf zapret_custom_firewall && zapret_custom_firewall
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
flow_offloading_exempt
|
||||||
|
}
|
100
init.d/openwrt/zapret
Executable file
100
init.d/openwrt/zapret
Executable file
@ -0,0 +1,100 @@
|
|||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
|
||||||
|
USE_PROCD=1
|
||||||
|
# after network
|
||||||
|
START=21
|
||||||
|
|
||||||
|
|
||||||
|
SCRIPT=$(readlink /etc/init.d/zapret)
|
||||||
|
if [ -n "$SCRIPT" ]; then
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
ZAPRET_BASE=$(readlink -f "$EXEDIR/../..")
|
||||||
|
else
|
||||||
|
ZAPRET_BASE=/opt/zapret
|
||||||
|
fi
|
||||||
|
. "$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=988
|
||||||
|
TPWS="$ZAPRET_BASE/tpws/tpws"
|
||||||
|
TPWS_LOCALHOST4=127.0.0.127
|
||||||
|
HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts.txt.gz"
|
||||||
|
[ -f "$HOSTLIST" ] || HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts.txt"
|
||||||
|
[ -f "$HOSTLIST" ] || HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts-user.txt"
|
||||||
|
TPWS_OPT_BASE="--user=$TPWS_USER --port=$TPPORT"
|
||||||
|
TPWS_OPT_BASE4="--bind-addr=$TPWS_LOCALHOST4"
|
||||||
|
TPWS_OPT_BASE6="--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="--bind-linklocal=prefer --bind-wait-ifup=30 --bind-wait-ip=30 --bind-wait-ip-linklocal=3"
|
||||||
|
|
||||||
|
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" ] && [ "$DISABLE_IPV6" = "1" ] && return 0
|
||||||
|
|
||||||
|
local OPT="$TPWS_OPT_BASE"
|
||||||
|
local DEVICE
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || OPT="$OPT $TPWS_OPT_BASE4"
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
OPT="$OPT $TPWS_OPT_BASE6"
|
||||||
|
network_get_device DEVICE lan
|
||||||
|
[ -n "$DEVICE" ] && OPT="$OPT --bind-iface6=$DEVICE $TPWS_OPT_BASE6_PRE"
|
||||||
|
}
|
||||||
|
run_daemon $1 $TPWS "$OPT $2"
|
||||||
|
}
|
||||||
|
stop_tpws()
|
||||||
|
{
|
||||||
|
stop_daemon $1 $TPWS
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
filter_apply_hostlist_target()
|
||||||
|
{
|
||||||
|
# $1 - var name of tpws or nfqws params
|
||||||
|
[ "$MODE_FILTER" = "hostlist" ] && eval $1="\"\$$1 --hostlist=$HOSTLIST\""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
local opt
|
||||||
|
|
||||||
|
case "${MODE}" in
|
||||||
|
tpws)
|
||||||
|
opt="$TPWS_OPT"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
run_tpws 1 "$opt"
|
||||||
|
;;
|
||||||
|
nfqws)
|
||||||
|
opt="$NFQWS_OPT_BASE $NFQWS_OPT_DESYNC"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
run_daemon 1 $NFQWS "$opt"
|
||||||
|
;;
|
||||||
|
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
|
||||||
|
}
|
44
init.d/sysv/custom-2nfqws
Normal file
44
init.d/sysv/custom-2nfqws
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# this custom script demonstrates how to use 2 copies of nfqws
|
||||||
|
# it preserves config settings : MODE_HTTP, MODE_HTTP_KEEPALIVE, MODE_HTTPS, MODE_FILTER, NFQWS_OPT_DESYNC
|
||||||
|
# NFQWS_OPT_DESYNC - parameters for http
|
||||||
|
# NFQWS_OPT_DESYNC2 - parameters for https. you should add this variable to config file, its absent there
|
||||||
|
|
||||||
|
QNUM2=$(($QNUM+1))
|
||||||
|
|
||||||
|
zapret_custom_daemons()
|
||||||
|
{
|
||||||
|
local opt
|
||||||
|
|
||||||
|
[ "$MODE_HTTP" = "1" ] && {
|
||||||
|
opt="$NFQWS_OPT_DESYNC"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
do_nfqws $1 1 "$opt"
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$MODE_HTTPS" = "1" ] && {
|
||||||
|
opt="$NFQWS_OPT_DESYNC2 --qnum=$QNUM2"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
do_nfqws $1 2 "$opt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zapret_custom_firewall()
|
||||||
|
{
|
||||||
|
local f4 f6
|
||||||
|
local first_packet_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4"
|
||||||
|
local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
|
||||||
|
|
||||||
|
[ "$MODE_HTTP" = "1" ] && {
|
||||||
|
f4="--dport 80"
|
||||||
|
[ "$MODE_HTTP_KEEPALIVE" = "1" ] || f4="$f4 $first_packet_only"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post $1 "$f4 $desync" "$f6 $desync" $QNUM
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$MODE_HTTPS" = "1" ] && {
|
||||||
|
f4="--dport 443 $first_packet_only"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post $1 "$f4 $desync" "$f6 $desync" $QNUM2
|
||||||
|
}
|
||||||
|
}
|
39
init.d/sysv/custom-tpws4http-nfqws4https
Normal file
39
init.d/sysv/custom-tpws4http-nfqws4https
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# this custom script demonstrates how to apply tpws to http and nfqws to https
|
||||||
|
# it preserves config settings : MODE_HTTP, MODE_HTTPS, MODE_FILTER, TPWS_OPT, NFQWS_OPT_DESYNC
|
||||||
|
|
||||||
|
zapret_custom_daemons()
|
||||||
|
{
|
||||||
|
local opt
|
||||||
|
|
||||||
|
[ "$MODE_HTTP" = "1" ] && {
|
||||||
|
opt="$TPWS_OPT"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
do_tpws $1 1 "$opt"
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$MODE_HTTPS" = "1" ] && {
|
||||||
|
opt="$NFQWS_OPT_DESYNC"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
do_nfqws $1 2 "$opt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zapret_custom_firewall()
|
||||||
|
{
|
||||||
|
local f4 f6
|
||||||
|
local first_packet_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4"
|
||||||
|
local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
|
||||||
|
|
||||||
|
[ "$MODE_HTTP" = "1" ] && {
|
||||||
|
f4="--dport 80"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_tpws $1 "$f4" "$f6" $TPPORT
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$MODE_HTTPS" = "1" ] && {
|
||||||
|
f4="--dport 443 $first_packet_only"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post $1 "$f4 $desync" "$f6 $desync" $QNUM
|
||||||
|
}
|
||||||
|
}
|
532
init.d/sysv/functions
Normal file
532
init.d/sysv/functions
Normal file
@ -0,0 +1,532 @@
|
|||||||
|
# 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=988
|
||||||
|
TPWS="$ZAPRET_BASE/tpws/tpws"
|
||||||
|
TPWS_LOCALHOST4=127.0.0.127
|
||||||
|
HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts.txt.gz"
|
||||||
|
[ -f "$HOSTLIST" ] || HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts.txt"
|
||||||
|
[ -f "$HOSTLIST" ] || HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts-user.txt"
|
||||||
|
|
||||||
|
TPWS_OPT_BASE="--user=$WS_USER --port=$TPPORT"
|
||||||
|
TPWS_OPT_BASE4="--bind-addr=$TPWS_LOCALHOST4"
|
||||||
|
TPWS_OPT_BASE6="--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="--bind-linklocal=prefer --bind-wait-ifup=30 --bind-wait-ip=30 --bind-wait-ip-linklocal=3"
|
||||||
|
|
||||||
|
[ -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=-
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare_tpws_fw4()
|
||||||
|
{
|
||||||
|
# otherwise linux kernel will treat 127.0.0.0/8 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.0/8
|
||||||
|
[ -n "$IFACE_LAN" ] && {
|
||||||
|
iptables -C INPUT -i $IFACE_LAN -j input_lan_rule_zapret 2>/dev/null || {
|
||||||
|
# allow localnet route only to special tpws IP
|
||||||
|
iptables -N input_lan_rule_zapret 2>/dev/null
|
||||||
|
iptables -F input_lan_rule_zapret
|
||||||
|
iptables -A input_lan_rule_zapret -d $TPWS_LOCALHOST4 -j RETURN
|
||||||
|
iptables -A input_lan_rule_zapret -d 127.0.0.0/8 -j DROP
|
||||||
|
iptables -I INPUT -i $IFACE_LAN -j input_lan_rule_zapret
|
||||||
|
}
|
||||||
|
sysctl -qw net.ipv4.conf.$IFACE_LAN.route_localnet=1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unprepare_tpws_fw4()
|
||||||
|
{
|
||||||
|
[ -n "$IFACE_LAN" ] && {
|
||||||
|
iptables -C INPUT -i $IFACE_LAN -j input_lan_rule_zapret 2>/dev/null && {
|
||||||
|
sysctl -qw net.ipv4.conf.$IFACE_LAN.route_localnet=0
|
||||||
|
iptables -D INPUT -i $IFACE_LAN -j input_lan_rule_zapret
|
||||||
|
iptables -F input_lan_rule_zapret
|
||||||
|
iptables -X input_lan_rule_zapret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unprepare_tpws_fw()
|
||||||
|
{
|
||||||
|
unprepare_tpws_fw4
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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" ] || {
|
||||||
|
[ "$1" = 1 ] && prepare_tpws_fw4
|
||||||
|
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 $TPWS_LOCALHOST4:$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 $TPWS_LOCALHOST4:$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
|
||||||
|
}
|
||||||
|
do_tpws()
|
||||||
|
{
|
||||||
|
# $1 : 1 - run, 0 - stop
|
||||||
|
# $2 : daemon number
|
||||||
|
# $3 : daemon args
|
||||||
|
|
||||||
|
[ "$1" = "1" ] && prepare_user
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] && [ "$DISABLE_IPV6" = "1" ] && return 0
|
||||||
|
|
||||||
|
local OPT="$TPWS_OPT_BASE"
|
||||||
|
|
||||||
|
[ "$DISABLE_IPV4" = "1" ] || OPT="$OPT $TPWS_OPT_BASE4"
|
||||||
|
[ "$DISABLE_IPV6" = "1" ] || {
|
||||||
|
OPT="$OPT $TPWS_OPT_BASE6"
|
||||||
|
[ -n "$IFACE_LAN" ] && OPT="$OPT --bind-iface6=$IFACE_LAN $TPWS_OPT_BASE6_PRE"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_daemon $1 $2 $TPWS "$OPT $3"
|
||||||
|
}
|
||||||
|
do_nfqws()
|
||||||
|
{
|
||||||
|
# $1 : 1 - run, 0 - stop
|
||||||
|
# $2 : daemon number
|
||||||
|
# $3 : daemon args
|
||||||
|
|
||||||
|
[ "$1" = "1" ] && prepare_user
|
||||||
|
do_daemon $1 $2 $NFQWS "$NFQWS_OPT_BASE $3"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
filter_apply_port_target()
|
||||||
|
{
|
||||||
|
# $1 - var name of iptables filter
|
||||||
|
local f
|
||||||
|
if [ "$MODE_HTTP" = "1" ] && [ "$MODE_HTTPS" = "1" ]; then
|
||||||
|
f="-m multiport --dports 80,443"
|
||||||
|
elif [ "$MODE_HTTPS" = "1" ]; then
|
||||||
|
f="--dport 443"
|
||||||
|
elif [ "$MODE_HTTP" = "1" ]; then
|
||||||
|
f="--dport 80"
|
||||||
|
else
|
||||||
|
echo WARNING !!! HTTP and HTTPS are both disabled
|
||||||
|
fi
|
||||||
|
eval $1="\"\$$1 $f\""
|
||||||
|
}
|
||||||
|
filter_apply_ipset_target()
|
||||||
|
{
|
||||||
|
# $1 - var name of ipv4 iptables filter
|
||||||
|
# $2 - var name of ipv6 iptables filter
|
||||||
|
if [ "$MODE_FILTER" = "ipset" ]; then
|
||||||
|
eval $1="\"\$$1 -m set --match-set zapret dst\""
|
||||||
|
eval $2="\"\$$2 -m set --match-set zapret6 dst\""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
filter_apply_hostlist_target()
|
||||||
|
{
|
||||||
|
# $1 - var name of tpws or nfqws params
|
||||||
|
[ "$MODE_FILTER" = "hostlist" ] && eval $1="\"\$$1 --hostlist=$HOSTLIST\""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
create_ipset()
|
||||||
|
{
|
||||||
|
echo "Creating ipset"
|
||||||
|
"$IPSET_CR" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
zapret_do_firewall()
|
||||||
|
{
|
||||||
|
# $1 - 1 - add, 0 - del
|
||||||
|
|
||||||
|
local first_packet_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4"
|
||||||
|
local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
|
||||||
|
local f4 f6
|
||||||
|
|
||||||
|
# always create ipsets. ip_exclude ipset is required
|
||||||
|
[ "$1" != "1" ] || create_ipset no-update
|
||||||
|
|
||||||
|
case "${MODE}" in
|
||||||
|
tpws)
|
||||||
|
if [ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ]; then
|
||||||
|
echo both http and https are disabled. not applying redirection.
|
||||||
|
else
|
||||||
|
filter_apply_port_target f4
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_tpws $1 "$f4" "$f6" $TPPORT
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
nfqws)
|
||||||
|
if [ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ]; then
|
||||||
|
echo both http and https are disabled. not applying redirection.
|
||||||
|
else
|
||||||
|
if [ "$MODE_HTTP_KEEPALIVE" = "1" ]; then
|
||||||
|
if [ "$MODE_HTTP" = "1" ]; then
|
||||||
|
f4="--dport 80"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post $1 "$f4 $desync" "$f6 $desync" $QNUM
|
||||||
|
fi
|
||||||
|
if [ "$MODE_HTTPS" = "1" ]; then
|
||||||
|
f4="--dport 443 $first_packet_only"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post $1 "$f4 $desync" "$f6 $desync" $QNUM
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
filter_apply_port_target f4
|
||||||
|
f4="$f4 $first_packet_only"
|
||||||
|
f6=$f4
|
||||||
|
filter_apply_ipset_target f4 f6
|
||||||
|
fw_nfqws_post $1 "$f4 $desync" "$f6 $desync" $QNUM
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
custom)
|
||||||
|
existf zapret_custom_firewall && zapret_custom_firewall $1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
[ "$1" = 0 ] && unprepare_tpws_fw
|
||||||
|
}
|
||||||
|
zapret_apply_firewall()
|
||||||
|
{
|
||||||
|
zapret_do_firewall 1 "$@"
|
||||||
|
}
|
||||||
|
zapret_unapply_firewall()
|
||||||
|
{
|
||||||
|
zapret_do_firewall 0 "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
zapret_do_daemons()
|
||||||
|
{
|
||||||
|
# $1 - 1 - run, 0 - stop
|
||||||
|
|
||||||
|
local opt
|
||||||
|
|
||||||
|
case "${MODE}" in
|
||||||
|
tpws)
|
||||||
|
opt="$TPWS_OPT"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
do_tpws $1 1 "$opt"
|
||||||
|
;;
|
||||||
|
nfqws)
|
||||||
|
opt="$NFQWS_OPT_DESYNC"
|
||||||
|
filter_apply_hostlist_target opt
|
||||||
|
do_nfqws $1 1 "$opt"
|
||||||
|
;;
|
||||||
|
custom)
|
||||||
|
existf zapret_custom_daemons && zapret_custom_daemons $1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
zapret_run_daemons()
|
||||||
|
{
|
||||||
|
zapret_do_daemons 1 "$@"
|
||||||
|
}
|
||||||
|
zapret_stop_daemons()
|
||||||
|
{
|
||||||
|
zapret_do_daemons 0 "$@"
|
||||||
|
}
|
50
init.d/sysv/zapret
Executable file
50
init.d/sysv/zapret
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
EXEDIR=$(dirname "$SCRIPT")
|
||||||
|
ZAPRET_BASE=$(readlink -f "$EXEDIR/../..")
|
||||||
|
. "$EXEDIR/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
|
75
install_bin.sh
Executable file
75
install_bin.sh
Executable file
@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
EXEDIR="$(dirname "$0")"
|
||||||
|
EXEDIR="$(cd "$EXEDIR"; pwd)"
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
UNAME=$(uname)
|
||||||
|
if [ "$UNAME" = "Linux" ]; then
|
||||||
|
ARCHLIST="my x86_64 x86 aarch64 arm mips64r2-msb mips32r1-lsb mips32r1-msb ppc"
|
||||||
|
elif [ "$UNAME" = "Darwin" ]; then
|
||||||
|
ARCHLIST="my mac64"
|
||||||
|
else
|
||||||
|
ARCHLIST="my"
|
||||||
|
fi
|
||||||
|
|
||||||
|
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
|
||||||
|
if [ "$(uname)" = "Linux" ]; then
|
||||||
|
ccp $arch/nfqws nfq
|
||||||
|
else
|
||||||
|
ccp $arch/dvtws nfq
|
||||||
|
fi
|
||||||
|
ccp $arch/tpws tpws
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo $arch is NOT OK
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo no compatible binaries found
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 1
|
1235
install_easy.sh
Executable file
1235
install_easy.sh
Executable file
File diff suppressed because it is too large
Load Diff
17
ip2net/Makefile
Normal file
17
ip2net/Makefile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
CC ?= gcc
|
||||||
|
CFLAGS += -std=gnu99 -s -O3
|
||||||
|
CFLAGS_BSD = -Wno-address-of-packed-member -Wno-logical-op-parentheses -Wno-switch
|
||||||
|
LIBS =
|
||||||
|
SRC_FILES = *.c
|
||||||
|
|
||||||
|
all: ip2net
|
||||||
|
mac: bsd
|
||||||
|
|
||||||
|
ip2net: $(SRC_FILES)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $(SRC_FILES) $(LDFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
bsd: $(SRC_FILES)
|
||||||
|
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o ip2net $(SRC_FILES) $(LDFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f ip2net *.o
|
1
ip2net/ip2net
Symbolic link
1
ip2net/ip2net
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../binaries/x86_64/ip2net
|
397
ip2net/ip2net.c
Normal file
397
ip2net/ip2net.c
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
// 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 <netinet/in.h>
|
||||||
|
#include <sys/socket.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, i;
|
||||||
|
|
||||||
|
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=$(LANG=C wc -c "$ZIPLISTTMP" | xargs | cut -f 1 -d ' ')
|
||||||
|
if [ $dlsize -lt 204800 ]; then
|
||||||
|
echo list file is too small. can be bad.
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
ip2net4 <"$ZIPLISTTMP" | zz "$2"
|
||||||
|
rm -f "$ZIPLISTTMP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
ipset/clear_lists.sh
Executable file
8
ipset/clear_lists.sh
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
IPSET_DIR="$(dirname "$0")"
|
||||||
|
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
|
||||||
|
|
||||||
|
. "$IPSET_DIR/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"*
|
201
ipset/create_ipset.sh
Executable file
201
ipset/create_ipset.sh
Executable file
@ -0,0 +1,201 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# create ipset or ipfw table from resolved ip's
|
||||||
|
# $1=no-update - do not update ipset, only create if its absent
|
||||||
|
|
||||||
|
IPSET_DIR="$(dirname "$0")"
|
||||||
|
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
|
||||||
|
|
||||||
|
. "$IPSET_DIR/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
|
||||||
|
[ "$1" = "clear" ] && DO_CLEAR=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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ipset_get_script()
|
||||||
|
{
|
||||||
|
# $1 - filename
|
||||||
|
# $2 - ipset name
|
||||||
|
zzcat "$1" | sort -u | sed -nEe "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"
|
||||||
|
[ "$svram" = "1" ] && T="$T, saveram"
|
||||||
|
T="$T) : $f"
|
||||||
|
echo $T
|
||||||
|
|
||||||
|
if [ "$svram" = "1" ]; then
|
||||||
|
ipset_get_script "$1" "$2" >"$IPSET_CMD"
|
||||||
|
ipset_restore_chunked "$IPSET_CMD" $IPSET_SAVERAM_CHUNK_SIZE
|
||||||
|
rm -f "$IPSET_CMD"
|
||||||
|
else
|
||||||
|
ipset_get_script "$1" "$2" | ipset -! restore
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
create_ipset()
|
||||||
|
{
|
||||||
|
if [ "$1" -eq "6" ]; then
|
||||||
|
FAMILY=inet6
|
||||||
|
else
|
||||||
|
FAMILY=inet
|
||||||
|
fi
|
||||||
|
ipset create $2 $3 $4 family $FAMILY 2>/dev/null || {
|
||||||
|
[ "$NO_UPDATE" = "1" ] && return
|
||||||
|
}
|
||||||
|
ipset flush $2
|
||||||
|
[ "$DO_CLEAR" = "1" ] || {
|
||||||
|
for f in "$5" "$6" ; do
|
||||||
|
ipset_restore "$f" "$2" $1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
add_ipfw_table()
|
||||||
|
{
|
||||||
|
# $1 - table name
|
||||||
|
sed -nEe "s/^.+$/table $1 add &/p" | ipfw -q /dev/stdin
|
||||||
|
}
|
||||||
|
populate_ipfw_table()
|
||||||
|
{
|
||||||
|
# $1 - table name
|
||||||
|
# $2 - ip list file
|
||||||
|
zzexist "$2" || return
|
||||||
|
zzcat "$2" | sort -u | add_ipfw_table $1
|
||||||
|
}
|
||||||
|
create_ipfw_table()
|
||||||
|
{
|
||||||
|
# $1 - table name
|
||||||
|
# $2 - table options
|
||||||
|
# $3,$4, ... - ip list files. can be v4,v6 or mixed
|
||||||
|
|
||||||
|
local name=$1
|
||||||
|
ipfw table "$name" create $2 2>/dev/null || {
|
||||||
|
[ "$NO_UPDATE" = "1" ] && return
|
||||||
|
}
|
||||||
|
ipfw -q table $1 flush
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
[ "$DO_CLEAR" = "1" ] || {
|
||||||
|
while [ -n "$1" ]; do
|
||||||
|
populate_ipfw_table $name "$1"
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print_reloading_backend()
|
||||||
|
{
|
||||||
|
# $1 - backend name
|
||||||
|
local s="reloading $1 backend"
|
||||||
|
if [ "$NO_UPDATE" = 1 ]; then
|
||||||
|
s="$s (no-update)"
|
||||||
|
else
|
||||||
|
s="$s (forced-update)"
|
||||||
|
fi
|
||||||
|
echo $s
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
oom_adjust_high
|
||||||
|
|
||||||
|
if [ -n "$LISTS_RELOAD" ] ; then
|
||||||
|
if [ "$LISTS_RELOAD" = "-" ] ; then
|
||||||
|
echo not reloading ip list backend
|
||||||
|
true
|
||||||
|
else
|
||||||
|
echo executing custom ip list reload command : $LISTS_RELOAD
|
||||||
|
$LISTS_RELOAD
|
||||||
|
fi
|
||||||
|
elif exists ipset; then
|
||||||
|
# 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
|
||||||
|
}
|
||||||
|
print_reloading_backend ipset
|
||||||
|
[ "$DISABLE_IPV4" != "1" ] && {
|
||||||
|
create_ipset 4 $ZIPSET hash:net "$IPSET_OPT" "$ZIPLIST" "$ZIPLIST_USER"
|
||||||
|
create_ipset 4 $ZIPSET_IPBAN hash:net "$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:net "$IPSET_OPT" "$ZIPLIST6" "$ZIPLIST_USER6"
|
||||||
|
create_ipset 6 $ZIPSET_IPBAN6 hash:net "$IPSET_OPT" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
|
||||||
|
create_ipset 6 $ZIPSET_EXCLUDE6 hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6"
|
||||||
|
}
|
||||||
|
true
|
||||||
|
elif exists ipfw; then
|
||||||
|
print_reloading_backend "ipfw table"
|
||||||
|
if [ "$DISABLE_IPV4" != "1" ] && [ "$DISABLE_IPV6" != "1" ]; then
|
||||||
|
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST" "$ZIPLIST_USER" "$ZIPLIST6" "$ZIPLIST_USER6"
|
||||||
|
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
|
||||||
|
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE" "$ZIPLIST_EXCLUDE6"
|
||||||
|
elif [ "$DISABLE_IPV4" != "1" ]; then
|
||||||
|
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST" "$ZIPLIST_USER"
|
||||||
|
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN"
|
||||||
|
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE"
|
||||||
|
elif [ "$DISABLE_IPV6" != "1" ]; then
|
||||||
|
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST6" "$ZIPLIST_USER6"
|
||||||
|
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
|
||||||
|
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6"
|
||||||
|
else
|
||||||
|
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT"
|
||||||
|
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT"
|
||||||
|
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE"
|
||||||
|
fi
|
||||||
|
true
|
||||||
|
else
|
||||||
|
echo no supported ip list backend found
|
||||||
|
true
|
||||||
|
fi
|
186
ipset/def.sh
Normal file
186
ipset/def.sh
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
. "$IPSET_DIR/../config"
|
||||||
|
|
||||||
|
[ -z "$TMPDIR" ] && TMPDIR=/tmp
|
||||||
|
[ -z "$GZIP_LISTS" ] && GZIP_LISTS=1
|
||||||
|
|
||||||
|
[ -z "$IPSET_OPT" ] && IPSET_OPT="hashsize 262144 maxelem 2097152"
|
||||||
|
[ -z "$IPSET_OPT_EXCLUDE" ] && IPSET_OPT_EXCLUDE="hashsize 1024 maxelem 65536"
|
||||||
|
|
||||||
|
[ -z "$IPFW_TABLE_OPT" ] && IPFW_TABLE_OPT="algo addr:radix"
|
||||||
|
[ -z "$IPFW_TABLE_OPT_EXCLUDE" ] && IPFW_TABLE_OPT_EXCLUDE="algo addr:radix"
|
||||||
|
|
||||||
|
|
||||||
|
ZIPSET=zapret
|
||||||
|
ZIPSET6=zapret6
|
||||||
|
ZIPSET_EXCLUDE=nozapret
|
||||||
|
ZIPSET_EXCLUDE6=nozapret6
|
||||||
|
ZIPLIST="$IPSET_DIR/zapret-ip.txt"
|
||||||
|
ZIPLIST6="$IPSET_DIR/zapret-ip6.txt"
|
||||||
|
ZIPLIST_EXCLUDE="$IPSET_DIR/zapret-ip-exclude.txt"
|
||||||
|
ZIPLIST_EXCLUDE6="$IPSET_DIR/zapret-ip-exclude6.txt"
|
||||||
|
ZIPLIST_USER="$IPSET_DIR/zapret-ip-user.txt"
|
||||||
|
ZIPLIST_USER6="$IPSET_DIR/zapret-ip-user6.txt"
|
||||||
|
ZUSERLIST="$IPSET_DIR/zapret-hosts-user.txt"
|
||||||
|
ZHOSTLIST="$IPSET_DIR/zapret-hosts.txt"
|
||||||
|
|
||||||
|
ZIPSET_IPBAN=ipban
|
||||||
|
ZIPSET_IPBAN6=ipban6
|
||||||
|
ZIPLIST_IPBAN="$IPSET_DIR/zapret-ip-ipban.txt"
|
||||||
|
ZIPLIST_IPBAN6="$IPSET_DIR/zapret-ip-ipban6.txt"
|
||||||
|
ZIPLIST_USER_IPBAN="$IPSET_DIR/zapret-ip-user-ipban.txt"
|
||||||
|
ZIPLIST_USER_IPBAN6="$IPSET_DIR/zapret-ip-user-ipban6.txt"
|
||||||
|
ZUSERLIST_IPBAN="$IPSET_DIR/zapret-hosts-user-ipban.txt"
|
||||||
|
ZUSERLIST_EXCLUDE="$IPSET_DIR/zapret-hosts-user-exclude.txt"
|
||||||
|
|
||||||
|
|
||||||
|
IP2NET="$IPSET_DIR/../ip2net/ip2net"
|
||||||
|
MDIG="$IPSET_DIR/../mdig/mdig"
|
||||||
|
[ -z "$MDIG_THREADS" ] && MDIG_THREADS=30
|
||||||
|
|
||||||
|
|
||||||
|
exists()
|
||||||
|
{
|
||||||
|
which "$1" >/dev/null 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# BSD grep is damn slow with -f option. prefer GNU grep (ggrep) if present
|
||||||
|
# MacoS in cron does not include /usr/local/bin to PATH
|
||||||
|
if [ -x /usr/local/bin/ggrep ] ; then
|
||||||
|
GREP=/usr/local/bin/ggrep
|
||||||
|
elif exists ggrep; then
|
||||||
|
GREP=$(which ggrep)
|
||||||
|
else
|
||||||
|
GREP=$(which grep)
|
||||||
|
fi
|
||||||
|
|
||||||
|
grep_supports_b()
|
||||||
|
{
|
||||||
|
# \b does not work with BSD grep
|
||||||
|
$GREP --version 2>&1 | $GREP -qE "BusyBox|GNU"
|
||||||
|
}
|
||||||
|
get_ip_regex()
|
||||||
|
{
|
||||||
|
REG_IPV4='((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/([0-9]|[12][0-9]|3[012]))?'
|
||||||
|
REG_IPV6='[0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}|:)+(/([0-9][0-9]?|1[01][0-9]|12[0-8]))?'
|
||||||
|
# good but too slow
|
||||||
|
# REG_IPV6='([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,7}:(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}(/[0-9]+)?|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})(/[0-9]+)?|:((:[0-9a-fA-F]{1,4}){1,7}|:)(/([0-9][0-9]?|1[01][0-9]|12[0-8]))?'
|
||||||
|
grep_supports_b && {
|
||||||
|
REG_IPV4="\b$REG_IPV4\b"
|
||||||
|
REG_IPV6="\b$REG_IPV6\b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ip2net4()
|
||||||
|
{
|
||||||
|
if [ -x "$IP2NET" ]; then
|
||||||
|
"$IP2NET" -4 $IP2NET_OPT4
|
||||||
|
else
|
||||||
|
sort -u
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
ip2net6()
|
||||||
|
{
|
||||||
|
if [ -x "$IP2NET" ]; then
|
||||||
|
"$IP2NET" -6 $IP2NET_OPT6
|
||||||
|
else
|
||||||
|
sort -u
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
zzexist()
|
||||||
|
{
|
||||||
|
[ -f "$1.gz" ] || [ -f "$1" ]
|
||||||
|
}
|
||||||
|
zzcat()
|
||||||
|
{
|
||||||
|
if [ -f "$1.gz" ]; then
|
||||||
|
gunzip -c "$1.gz"
|
||||||
|
else
|
||||||
|
cat "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
zz()
|
||||||
|
{
|
||||||
|
if [ "$GZIP_LISTS" = "1" ]; then
|
||||||
|
gzip -c >"$1.gz"
|
||||||
|
rm -f "$1"
|
||||||
|
else
|
||||||
|
cat >"$1"
|
||||||
|
rm -f "$1.gz"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
zzsize()
|
||||||
|
{
|
||||||
|
local f="$1"
|
||||||
|
[ -f "$1.gz" ] && f="$1.gz"
|
||||||
|
wc -c <"$f" | xargs
|
||||||
|
}
|
||||||
|
|
||||||
|
digger()
|
||||||
|
{
|
||||||
|
# $1 - hostlist
|
||||||
|
# $2 - family (4|6)
|
||||||
|
>&2 echo digging $(wc -l <"$1" | xargs) 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()
|
||||||
|
{
|
||||||
|
[ -f /proc/$$/oom_score_adj ] && {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hup_zapret_daemons()
|
||||||
|
{
|
||||||
|
echo forcing zapret daemons to reload their hostlist
|
||||||
|
if exists killall; then
|
||||||
|
kcmd=killall
|
||||||
|
killall -HUP tpws nfqws dvtws 2>/dev/null
|
||||||
|
elif exists pkill; then
|
||||||
|
pkill -HUP ^tpws$ ^nfqws$ ^dvtws$
|
||||||
|
else
|
||||||
|
echo no mass killer available ! cant HUP zapret daemons
|
||||||
|
fi
|
||||||
|
}
|
14
ipset/get_antifilter_ip.sh
Executable file
14
ipset/get_antifilter_ip.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
IPSET_DIR="$(dirname "$0")"
|
||||||
|
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
|
||||||
|
|
||||||
|
. "$IPSET_DIR/def.sh"
|
||||||
|
|
||||||
|
getuser
|
||||||
|
|
||||||
|
. "$IPSET_DIR/antifilter.helper"
|
||||||
|
|
||||||
|
get_antifilter https://antifilter.network/download/ip.lst "$ZIPLIST"
|
||||||
|
|
||||||
|
"$IPSET_DIR/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
|
||||||
|
|
||||||
|
IPSET_DIR="$(dirname "$0")"
|
||||||
|
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
|
||||||
|
|
||||||
|
. "$IPSET_DIR/def.sh"
|
||||||
|
|
||||||
|
getuser
|
||||||
|
|
||||||
|
. "$IPSET_DIR/antifilter.helper"
|
||||||
|
|
||||||
|
get_antifilter https://antifilter.network/download/ipsmart.lst "$ZIPLIST"
|
||||||
|
|
||||||
|
"$IPSET_DIR/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
|
||||||
|
|
||||||
|
IPSET_DIR="$(dirname "$0")"
|
||||||
|
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
|
||||||
|
|
||||||
|
. "$IPSET_DIR/def.sh"
|
||||||
|
|
||||||
|
getuser
|
||||||
|
|
||||||
|
. "$IPSET_DIR/antifilter.helper"
|
||||||
|
|
||||||
|
get_antifilter https://antifilter.network/download/ipsum.lst "$ZIPLIST"
|
||||||
|
|
||||||
|
"$IPSET_DIR/create_ipset.sh"
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user