├── LICENSE ├── README.md ├── config ├── install.sh ├── parser.sh ├── start-stop.sh └── uninstall.sh /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Данный проект позволяет перенаправлять трафик для отдельных ресурсов в VPN-туннель на роутерах [Keenetic](https://keenetic.ru/) с использованием репозитория [Entware](https://entware.net/). 2 | 3 | ## Установка 4 | Для загрузки и работы установочного скрипта требуется [curl](https://curl.se/). При отсутствии — установить командой: 5 | 6 | ```shell 7 | opkg install curl 8 | ``` 9 | 10 | Для начала процесса установки выполните команду: 11 | 12 | ```shell 13 | curl -sfL https://raw.githubusercontent.com/rustrict/keenetic-traffic-via-vpn/main/install.sh | sh 14 | ``` 15 | 16 | Установщик создаст каталог `/opt/etc/unblock` (если такой не существует) и поместит в него необходимые файлы. Также будут созданы два симлинка для отслеживания состояния VPN-туннеля и автоматического обновления маршрутов раз в сутки. Для работы скрипта `parser.sh` требуются `bind-dig`, `cron` и `grep` — они будут установлены при отсутствии. 17 | 18 | После окончания установки понадобится: 19 | - Отредактировать файл `/opt/etc/unblock/config`, указав в переменной `IFACE` название интерфейса VPN, которое можно увидеть в выводе команды `ip address show` или `ifconfig`. Например, `ovpn_br0` (=`OpenVPN0`) или `nwg0` (=`Wireguard0`); 20 | - Заполнить файл `/opt/etc/unblock/unblock-list.txt` доменами и (или) IPv4-адресами (как с префиксом, так и без) ресурсов, трафик до которых вы хотите пустить через VPN; 21 | - Запустить VPN-соединение (или перезапустить, если оно было запущено до установки). 22 | 23 | ### Примеры заполнения config 24 | Для OpenVPN-туннеля: 25 | 26 | ```shell 27 | # Название интерфейса VPN-туннеля из ifconfig или ip address show 28 | IFACE="ovpn_br0" 29 | 30 | # Расположение файла с адресами и доменами 31 | FILE="/opt/etc/unblock/unblock-list.txt" 32 | ``` 33 | 34 | Для WireGuard-туннеля: 35 | 36 | ```shell 37 | # Название интерфейса VPN-туннеля из ifconfig или ip address show 38 | IFACE="nwg0" 39 | 40 | # Расположение файла с адресами и доменами 41 | FILE="/opt/etc/unblock/unblock-list.txt" 42 | ``` 43 | 44 | ### Пример заполнения unblock-list.txt 45 | ``` 46 | example.com 47 | 1.1.1.1 48 | 93.184.220.0/24 49 | ``` 50 | 51 | ## Замечание 52 | Учтите, что по умолчанию трафик перенаправляется только для устройств из сегмента «Домашняя сеть» (Bridge0). При попытке доступа непосредственно с роутера, трафик не отправится в VPN-туннель. Если вас это не устраивает, последовательно выполните следующие три команды: 53 | 54 | ```shell 55 | ip rule del priority 1995 2>/dev/null 56 | ip rule add table 1000 priority 1995 57 | sed -i 's/iif br0 //' /opt/etc/unblock/start-stop.sh 58 | ``` 59 | 60 | После этого под перенаправление попадут все устройства, включая сам роутер. 61 | 62 | ## Удаление 63 | Для удаления выполните команду: 64 | 65 | ```shell 66 | /opt/etc/unblock/uninstall.sh 67 | ``` 68 | 69 | Будут удалены **все** скаченные и созданные установщиком файлы, а также каталог `/opt/etc/unblock`, если в нём не окажется ничего постороннего. 70 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | # Название интерфейса VPN-туннеля из ifconfig или ip address show 2 | IFACE="foobar" 3 | 4 | # Расположение файла с адресами и доменами 5 | FILE="/opt/etc/unblock/unblock-list.txt" 6 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | check_command() { 4 | command -v "$1" >/dev/null 2>&1 5 | } 6 | 7 | msg() { 8 | printf "%s\n" "$1" 9 | } 10 | 11 | error_msg() { 12 | printf "[!] %s\n" "$1" 13 | } 14 | 15 | failure() { 16 | error_msg "$1" 17 | exit 1 18 | } 19 | 20 | pkg_install() { 21 | msg "Установка пакета \"${1}\"..." 22 | if opkg install "$1" >/dev/null 2>&1; then 23 | msg "Пакет \"${1}\" установлен." 24 | else 25 | failure "Ошибка при установке пакета \"${1}\"." 26 | fi 27 | } 28 | 29 | download() { 30 | check_command curl || failure "Для загрузки файлов требуется curl." 31 | if curl -sfL --connect-timeout 7 "$1" -o "$2"; then 32 | msg "Файл \"${2##*/}\" скачан." 33 | else 34 | failure "Не удалось скачать файл \"${2##*/}\"." 35 | fi 36 | } 37 | 38 | mk_file_exec() { 39 | check_command chmod || failure "Для изменения прав на файлы требуется chmod." 40 | if chmod +x "$1" 2>/dev/null; then 41 | msg "Установлены права на исполнение для файла \"${1}\"." 42 | else 43 | failure "Не удалось установить права на исполнение для файла \"${1}\"." 44 | fi 45 | } 46 | 47 | crt_symlink() { 48 | check_command ln || failure "Для создания симлинков требуется ln." 49 | if ln -sf "$1" "$2" 2>/dev/null; then 50 | msg "В каталоге \"${2%/*}\" создан симлинк \"${2##*/}\"." 51 | else 52 | failure "Не удалось создать симлинк \"${2##*/}\"." 53 | fi 54 | } 55 | 56 | msg "Выполняется установка keenetic-traffic-via-vpn..." 57 | 58 | INSTALL_DIR="/opt/etc/unblock" 59 | REPO_URL="https://raw.githubusercontent.com/rustrict/keenetic-traffic-via-vpn/main" 60 | 61 | check_command opkg || failure "Для установки пакетов требуется opkg." 62 | opkg update >/dev/null 2>&1 || failure "Не удалось обновить список пакетов Entware." 63 | 64 | for pkg in bind-dig cron grep; do 65 | [ -n "$(opkg status ${pkg})" ] && continue 66 | 67 | pkg_install "$pkg" 68 | sleep 1 69 | 70 | if [ "$pkg" = "cron" ]; then 71 | sed -i 's/^ARGS="-s"$/ARGS=""/' /opt/etc/init.d/S10cron && \ 72 | msg "Отключен флуд cron в логе роутера." 73 | /opt/etc/init.d/S10cron restart >/dev/null 74 | fi 75 | done 76 | 77 | if [ ! -d "$INSTALL_DIR" ]; then 78 | if mkdir -p "$INSTALL_DIR"; then 79 | msg "Каталог \"${INSTALL_DIR}\" создан." 80 | else 81 | failure "Не удалось создать каталог \"${INSTALL_DIR}\"." 82 | fi 83 | fi 84 | 85 | [ ! -f "${INSTALL_DIR}/config" ] && download "${REPO_URL}/config" "${INSTALL_DIR}/config" 86 | 87 | for _file in parser.sh start-stop.sh uninstall.sh; do 88 | download "${REPO_URL}/${_file}" "${INSTALL_DIR}/${_file}" 89 | mk_file_exec "${INSTALL_DIR}/${_file}" 90 | done 91 | 92 | crt_symlink "${INSTALL_DIR}/parser.sh" "/opt/etc/cron.daily/routing_table_update" 93 | crt_symlink "${INSTALL_DIR}/start-stop.sh" "/opt/etc/ndm/ifstatechanged.d/ip_rule_switch" 94 | 95 | if [ ! -f "${INSTALL_DIR}/unblock-list.txt" ]; then 96 | if touch "${INSTALL_DIR}/unblock-list.txt" 2>/dev/null; then 97 | msg "Файл \"${INSTALL_DIR}/unblock-list.txt\" создан." 98 | else 99 | error_msg "Не удалось создать файл \"${INSTALL_DIR}/unblock-list.txt\"." 100 | fi 101 | fi 102 | 103 | printf "%s\n" "---" "Установка завершена." 104 | msg "Не забудьте вписать название интерфейса VPN в файл config, а также заполнить файл unblock-list.txt." 105 | 106 | exit 0 107 | -------------------------------------------------------------------------------- /parser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | add_ip() { 4 | ip route add table 1000 "$1" dev "$IFACE" 2>/dev/null 5 | } 6 | 7 | check_ip() { 8 | # https://stackoverflow.com/a/36760050 9 | if echo "$1" | grep -qP \ 10 | '^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|(\/(3[0-2]|[12][0-9]|[0-9]))?$)){4}$'; then 11 | return 0 12 | else 13 | return 1 14 | fi 15 | } 16 | 17 | cut_special() { 18 | grep -vE -e '^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)' \ 19 | -e '^(0\.|127\.|100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\.|255\.255\.255\.255)' 20 | } 21 | 22 | logger_msg() { 23 | logger -s -t parser "$1" 24 | } 25 | 26 | logger_failure() { 27 | logger_msg "Error: ${1}" 28 | exit 1 29 | } 30 | 31 | CONFIG="/opt/etc/unblock/config" 32 | if [ -f "$CONFIG" ]; then 33 | . "$CONFIG" 34 | else 35 | logger_failure "Не удалось обнаружить файл \"config\"." 36 | fi 37 | 38 | for _tool in dig grep ip rm seq sleep; do 39 | command -v "$_tool" >/dev/null 2>&1 || \ 40 | logger_failure "Для работы скрипта требуется \"${_tool}\"." 41 | done 42 | 43 | PIDFILE="${PIDFILE:-/tmp/parser.sh.pid}" 44 | [ -e "$PIDFILE" ] && logger_failure "Обнаружен файл \"${PIDFILE}\"." 45 | ( echo $$ > "$PIDFILE" ) 2>/dev/null || logger_failure "Не удалось создать файл \"${PIDFILE}\"." 46 | trap 'rm -f "$PIDFILE"' EXIT 47 | trap 'exit 2' INT TERM QUIT HUP 48 | 49 | [ -f "$FILE" ] || logger_failure "Отсутствует файл \"${FILE}\"." 50 | 51 | if ! ip address show dev "$IFACE" >/dev/null 2>&1; then 52 | logger_failure "Не удалось обнаружить интерфейс \"${IFACE}\"." 53 | elif [ -z "$(ip link show "${IFACE}" up 2>/dev/null)" ]; then 54 | logger_failure "Интерфейс \"${IFACE}\" отключен." 55 | fi 56 | 57 | for _attempt in $(seq 0 10); do 58 | if dig +short +tries=1 ripe.net @localhost 2>/dev/null | grep -qvE '^$|^;'; then 59 | break 60 | elif [ "$_attempt" -eq 10 ]; then 61 | logger_failure "Не удалось разрешить проверочное доменное имя." 62 | fi 63 | sleep 1 64 | done 65 | 66 | if ip route flush table 1000; then 67 | logger_msg "Таблица маршрутизации #1000 очищена." 68 | else 69 | logger_failure "Не удалось очистить таблицу маршрутизации #1000." 70 | fi 71 | 72 | logger_msg "Парсинг $(grep -c "" "$FILE") строк(-и) в файле \"${FILE}\"..." 73 | 74 | while read -r line || [ -n "$line" ]; do 75 | [ -z "$line" ] && continue 76 | [ "${line:0:1}" = "#" ] && continue 77 | 78 | if check_ip "$line"; then 79 | add_ip "$line" 80 | else 81 | dig_host=$(dig +short "$line" @localhost 2>&1 | grep -vE '[a-z]+' | cut_special) 82 | if [ -n "$dig_host" ]; then 83 | for i in $dig_host; do check_ip "$i" && add_ip "$i"; done 84 | else 85 | logger_msg "Не удалось разрешить доменное имя: строка \"${line}\" проигнорирована." 86 | fi 87 | fi 88 | done < "$FILE" 89 | 90 | logger_msg "Парсинг завершен. #1000: $(ip route list table 1000 | wc -l)." 91 | 92 | exit 0 93 | -------------------------------------------------------------------------------- /start-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CONFIG="/opt/etc/unblock/config" 4 | [ -f "$CONFIG" ] || exit 0 5 | . "$CONFIG" 6 | 7 | # https://github.com/ndmsystems/packages/wiki/Opkg-Component 8 | [ "$1" != "hook" ] && exit 0 9 | [ "$system_name" != "$IFACE" ] && exit 0 10 | 11 | kill_parser() { 12 | PIDFILE="${PIDFILE:-/tmp/parser.sh.pid}" 13 | if [ -e "$PIDFILE" ]; then 14 | PID="$(cat "$PIDFILE")" 15 | if [ -n "$PID" ] && [ -d "/proc/${PID}" ]; then 16 | kill "$PID" 17 | else 18 | rm -f "$PIDFILE" 19 | fi 20 | fi 21 | } 22 | 23 | case ${connected}-${link}-${up} in 24 | yes-up-up) 25 | ip rule add iif br0 table 1000 priority 1995 2>/dev/null 26 | /opt/etc/unblock/parser.sh & 27 | ;; 28 | no-down-*) 29 | kill_parser 30 | ip rule del iif br0 table 1000 priority 1995 2>/dev/null 31 | ;; 32 | esac 33 | 34 | exit 0 35 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | msg() { 4 | printf "%s\n" "$1" 5 | } 6 | 7 | error_msg() { 8 | printf "[!] %s\n" "$1" 9 | } 10 | 11 | delete_file() { 12 | if [ -f "$1" ]; then 13 | if rm "$1" 2>/dev/null; then 14 | msg "${2:-"Файл"} \"${1##*/}\" удален." 15 | else 16 | error_msg "Не удалось удалить ${3:-"файл"} \"${1##*/}\"." 17 | fi 18 | else 19 | msg "${2:-"Файл"} \"${1##*/}\" отсутствует." 20 | fi 21 | } 22 | 23 | PRJ_DIR="/opt/etc/unblock" 24 | 25 | for _tool in ip rm; do 26 | command -v "$_tool" >/dev/null 2>&1 || { 27 | error_msg "Для работы скрипта требуется \"${_tool}\"." 28 | exit 1 29 | } 30 | done 31 | 32 | # https://stackoverflow.com/a/226724 33 | read -p "Приступить к удалению? [y/n] " yn 34 | case "$yn" in 35 | [Yy]*) ;; 36 | *) msg "Удаление отменено."; exit 1;; 37 | esac 38 | 39 | if ip route flush table 1000; then 40 | msg "Таблица маршрутизации #1000 очищена." 41 | fi 42 | 43 | if ip rule del priority 1995 2>/dev/null; then 44 | msg "Правило маршрутизации удалено." 45 | fi 46 | 47 | delete_file "/opt/etc/cron.daily/routing_table_update" "Симлинк" "симлинк" 48 | delete_file "/opt/etc/ndm/ifstatechanged.d/ip_rule_switch" "Симлинк" "симлинк" 49 | 50 | for _file in \ 51 | config parser.sh start-stop.sh uninstall.sh unblock-list.txt; do 52 | delete_file "${PRJ_DIR}/${_file}" 53 | done 54 | 55 | # https://unix.stackexchange.com/a/615900 56 | if [ -d "${PRJ_DIR}" ] && \ 57 | [ "$(echo "${PRJ_DIR}/"*)" = "${PRJ_DIR}/*" ]; then 58 | if rm -r "${PRJ_DIR}" 2>/dev/null; then 59 | msg "Каталог \"${PRJ_DIR}\" удален." 60 | else 61 | error_msg "Не удалось удалить каталог \"${PRJ_DIR}\"." 62 | fi 63 | fi 64 | 65 | printf "%s\n" "---" "Удаление завершено." 66 | 67 | exit 0 68 | --------------------------------------------------------------------------------