mirror of
https://github.com/bol-van/zapret.git
synced 2025-12-19 14:08:46 +03:00
Update and rename bsd.eng.txt to bsd.eng.md
This commit is contained in:
369
docs/bsd.eng.md
Normal file
369
docs/bsd.eng.md
Normal file
@@ -0,0 +1,369 @@
|
||||
## 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 3 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 in quick on em0 proto tcp from port {80,443} no state
|
||||
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
|
||||
```
|
||||
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 in quick on em0 inet proto tcp from <zapret> port {80,443} no state
|
||||
pass out quick on em0 inet proto tcp to <zapret> port {80,443} divert-packet port 989 no state
|
||||
pass in quick on em0 inet proto tcp from <zapret-user> port {80,443} no state
|
||||
pass out quick on em0 inet proto tcp to <zapret-user> port {80,443} divert-packet port 989 no state
|
||||
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 in quick on em0 inet6 proto tcp from <zapret6> port {80,443} no state
|
||||
pass out quick on em0 inet6 proto tcp to <zapret6> port {80,443} divert-packet port 989 no state
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6-user> port {80,443} no state
|
||||
pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-packet port 989 no state
|
||||
```
|
||||
pfctl -f /etc/pf.conf
|
||||
./dvtws --port=989 --dpi-desync=split2
|
||||
|
||||
divert-packet automatically adds the reverse rule. By default also incoming traffic will be passwed to dvtws.
|
||||
This is highly undesired because it is waste of cpu resources and speed limiter.
|
||||
The trick with "no state" and "in" rules allows to bypass auto reverse rule.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
/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=lo0 --bind-linklocal=force
|
||||
|
||||
|
||||
tpws transparent mode for both passthrough and outgoing connections. 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=lo0 --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.
|
||||
Reference in New Issue
Block a user