Поддерживаемые версии --------------------- 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 file "/opt/zapret/ipset/zapret-ip.txt" table file "/opt/zapret/ipset/zapret-ip-user.txt" table file "/opt/zapret/ipset/zapret-ip-exclude.txt" pass out quick on em0 inet proto tcp to port {80,443} pass out quick on em0 inet proto tcp to port {80,443} divert-packet port 989 pass out quick on em0 inet proto tcp to port {80,443} divert-packet port 989 table file "/opt/zapret/ipset/zapret-ip6.txt" table file "/opt/zapret/ipset/zapret-ip-user6.txt" table file "/opt/zapret/ipset/zapret-ip-exclude6.txt" pass out quick on em0 inet6 proto tcp to port {80,443} pass out quick on em0 inet6 proto tcp to port {80,443} divert-packet port 989 pass out quick on em0 inet6 proto tcp to 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.