Move to fakeip

This commit is contained in:
itdoginfo
2025-02-14 16:26:28 +03:00
parent 532fe10a1a
commit 7f6cc66eb1
8 changed files with 1387 additions and 1312 deletions

View File

@@ -1,6 +1,13 @@
Это альфа версия, может не работать. Обсуждение https://t.me/itdogchat - топик **Podkop** # Вещи, которые вам нужно знать перед установкой
Если у вас установлен Getdomains, то его следует удалить. - Это альфа версия, которая находится в активной разработке. Из версии в версию что-то может меняться.
- Основной функционал работает, но побочные штуки сейчас могут сбоить.
- При обновлении всегда заходите в конфигурацию и проверяйте свои настройки. Конфигурация может измениться.
- Необходимо минимум 15МБ свободного места на роутере. Роутерами с флешками на 16МБ сразу мимо.
- При старте программы редактируется конфиг Dnsmasq.
- Podkop редактирует конфиг sing-box. Обязательно сохраните ваш конфиг sing-box перед установкой, если он вам нужен.
- Информация здесь может быть устаревшей. Все изменения фиксируются в телеграм-чате https://t.me/itdogchat - топик **Podkop**.
- Если у вас установлен Getdomains, его следует удалить.
# Удаление GetDomains скриптом # Удаление GetDomains скриптом
``` ```
@@ -11,9 +18,9 @@ sh <(wget -O - https://raw.githubusercontent.com/itdoginfo/domain-routing-openwr
# Установка Podkop # Установка Podkop
Пакет работает на всех архитектурах. Пакет работает на всех архитектурах.
Будет точно работать только на OpenWrt 23.05. Тестировался на OpenWrt 23.05 и OpenWrt 24.10.
Нужен dnsmasq-full. В автоматическом режиме ставится сам. Вручную надо поставить [самостоятельно](https://github.com/itdoginfo/podkop/blob/952dd6215a2a83d65937cf9e33534c42809091ed/install.sh#L20). Поддержки APK на данный момент нет. APK будет сделан после того как разгребу основное.
## Автоматическая ## Автоматическая
``` ```
@@ -53,26 +60,20 @@ Luci: Services/podkop
### Proxy ### Proxy
Для VLESS и Shadowsocks. Другие протоколы тоже будут, кидайте в чат примеры строк без чувствительных данных. Для VLESS и Shadowsocks. Другие протоколы тоже будут, кидайте в чат примеры строк без чувствительных данных.
Для использования этого режима нужен sing-box:
```
opkg update && opkg install sing-box
```
В этом режиме просто копируйте строку в **Proxy String** и из неё автоматически настроится sing-box. В этом режиме просто копируйте строку в **Proxy String** и из неё автоматически настроится sing-box.
### VPN ### VPN
Здесь у вас должен быть уже настроен WG/OpenVPN/OpenConnect etc, создана Zone и Forwarding. Здесь у вас должен быть уже настроен WG/OpenVPN/OpenConnect etc, зона Zone и Forwarding не обязательны.
Просто выбрать интерфейс из списка. Просто выбрать интерфейс из списка.
## Настройка доменов и подсетей ## Настройка доменов и подсетей
**Domain list enable** - Включить общий список. **Community Lists** - Включить списки комьюнити
**Delist domains from main list enable** - Выключение заданных доменов из общего списка. Задавать списком.
**Subnets list enable** - Включить подсети из общего списка, выбрать из предложенных. **Subnets list enable** - Включить подсети из общего списка, выбрать из предложенных.
**Custom domains enable** - Добавить свои домены. Задавать списком. **Custom domains enable** - Добавить свои домены
**Custom subnets enable** - Добавить подсети или IP-адреса. Для подсетей задать маску. **Custom subnets enable** - Добавить подсети или IP-адреса. Для подсетей задать маску.
@@ -84,10 +85,15 @@ opkg update && opkg install sing-box
- [x] awg работает не стабильно - [x] awg работает не стабильно
- [x] Сеть рестартится при любом раскладе - [x] Сеть рестартится при любом раскладе
- [x] Выкл-вкл wg через luci не отрабатывает поднятие маршрута - [x] Выкл-вкл wg через luci не отрабатывает поднятие маршрута
- [ ] Проблема скачивания списка из github release. Нужен curl -L
- [ ] Если eof после последней строки в rt_tables, то скрипт не добавляет перенос строки - [ ] Если eof после последней строки в rt_tables, то скрипт не добавляет перенос строки
- [ ] Парсинг VLESS не отрабатывает, если в SNI два домена. Пример `sni=telegram.org%3Bwww.telegram.org`
- [ ] `service network restart` ломает маршруты при sing-box
- [ ] Совпадение секции с ruleset ломает конфиг sing-box
- [ ] В каких-то случаях плохо отрабатывает localfile
# ToDo # ToDo
Этот раздел не означает задачи, которые нужно брать и делать. Это общий список хотелок. Если вы хотите помочь, пожалуйста, спросите сначала в телеграмме.
Сделано Сделано
- [x] Скрипт для автоматической установки. - [x] Скрипт для автоматической установки.
- [x] Подсети дискорда. - [x] Подсети дискорда.
@@ -127,13 +133,18 @@ opkg update && opkg install sing-box
- [x] Проверка места в скрипте install. Если доступно меньше 20MB - exit 1 c выводом колько надо и сколько доступно. + показ модели роутера - [x] Проверка места в скрипте install. Если доступно меньше 20MB - exit 1 c выводом колько надо и сколько доступно. + показ модели роутера
- [ ] Правило запрещающее QUIC - [ ] Правило запрещающее QUIC
- [ ] Проверить обновление списков, отрабатывает ли - [ ] Проверить обновление списков, отрабатывает ли
- [ ] Проверка на ванильную openwrt
- [ ] Проверка откуда установлен sing-box. Например, проверять установлен ли он из официального репозитория
- [x] TG в сервисы
- [ ] Выбор ткуда направлять трафик в туннель. В том числе чтоб откуда угодно, а не только br-lan
- [ ] Диагностика: Proxy check completed successfully предположительно не показывает IP, если вернулся это IPv6.
- [ ] Диагностика: podkop_domains: 0 elements как проверять что доходят запросы при fakeip? Мб врубать логи dnsmasq и их чекать.
- [ ] Сделать галку запрещающую подкопу редачить dhcp. Допилить в исключение вместе с пустыми полями proxy и vpn
Приоритет 2 Приоритет 2
- [x] Списки доменов и подсетей с роутера - [x] Списки доменов и подсетей с роутера
- [ ] Кнопка обновления списка доменов и подсетей. Запихнуть в главное меню - [ ] Кнопка обновления списка доменов и подсетей. Запихнуть в главное меню
- [ ] IPv6 - [ ] IPv6
- [ ] Придумать автонастройку DNS через stubby итд. Как лучше это реализовать. Для sing-box не нужно
- [ ] Удаление подсетей CF из domain sets раз в N часов
Wiki Wiki
- [x] Тема - [x] Тема
@@ -143,7 +154,7 @@ Wiki
- [x] Переменная, раз во сколько часов обновлять списки - [x] Переменная, раз во сколько часов обновлять списки
- [ ] Галочка, которая режет доступ к doh серверам - [ ] Галочка, которая режет доступ к doh серверам
- [ ] Свой конфиг sing-box - [ ] Свой конфиг sing-box
- [ ] Поменять curl на wget, убрать зависимость. Проверять доступность списков лучше всего curl`ом - [x] Поменять curl на wget, убрать зависимость. Проверять доступность списков лучше всего curl`ом
Рефактор Рефактор
- [ ] Handle для sing-box - [ ] Handle для sing-box

View File

@@ -18,27 +18,6 @@ main() {
echo "opkg update" echo "opkg update"
opkg update opkg update
if opkg list-installed | grep -q dnsmasq-full; then
echo "dnsmasq-full already installed"
else
echo "Installed dnsmasq-full"
cd /tmp/ && opkg download dnsmasq-full
opkg remove dnsmasq && opkg install dnsmasq-full --cache /tmp/
[ -f /etc/config/dhcp-opkg ] && cp /etc/config/dhcp /etc/config/dhcp-old && mv /etc/config/dhcp-opkg /etc/config/dhcp
fi
openwrt_release=$(cat /etc/openwrt_release | grep -Eo [0-9]{2}[.][0-9]{2}[.][0-9]* | cut -d '.' -f 1 | tail -n 1)
if [ $openwrt_release -ge 24 ]; then
if uci get dhcp.@dnsmasq[0].confdir | grep -q /tmp/dnsmasq.d; then
echo "confdir alreadt set"
else
printf "Setting confdir"
uci set dhcp.@dnsmasq[0].confdir='/tmp/dnsmasq.d'
uci commit dhcp
fi
fi
if [ -f "/etc/init.d/podkop" ]; then if [ -f "/etc/init.d/podkop" ]; then
printf "\033[32;1mPodkop is already installed. Just upgrade it? (y/n)\033[0m\n" printf "\033[32;1mPodkop is already installed. Just upgrade it? (y/n)\033[0m\n"
printf "\033[32;1my - Only upgrade podkop\033[0m\n" printf "\033[32;1my - Only upgrade podkop\033[0m\n"
@@ -49,6 +28,7 @@ main() {
case $UPDATE in case $UPDATE in
y) y)
echo "Upgraded podkop..." echo "Upgraded podkop..."
sed -i '/second/d' /etc/config/podkop
break break
;; ;;
@@ -99,23 +79,17 @@ main() {
add_tunnel() { add_tunnel() {
echo "What type of VPN or proxy will be used? We also can automatically configure Wireguard and Amnezia WireGuard." echo "What type of VPN or proxy will be used? We also can automatically configure Wireguard and Amnezia WireGuard."
echo "1) VLESS, Shadowsocks (A sing-box will be installed)" echo "1) Wireguard"
echo "2) Wireguard" echo "2) AmneziaWG"
echo "3) AmneziaWG" echo "3) OpenVPN"
echo "4) OpenVPN" echo "4) OpenConnect"
echo "5) OpenConnect" echo "5) Skip this step"
echo "6) Skip this step"
while true; do while true; do
read -r -p '' TUNNEL read -r -p '' TUNNEL
case $TUNNEL in case $TUNNEL in
1) 1)
opkg install sing-box
break
;;
2)
opkg install wireguard-tools luci-proto-wireguard luci-app-wireguard opkg install wireguard-tools luci-proto-wireguard luci-app-wireguard
printf "\033[32;1mDo you want to configure the wireguard interface? (y/n): \033[0m\n" printf "\033[32;1mDo you want to configure the wireguard interface? (y/n): \033[0m\n"
@@ -130,7 +104,7 @@ add_tunnel() {
break break
;; ;;
3) 2)
install_awg_packages install_awg_packages
printf "\033[32;1mThere are no instructions for manual configure yet. Do you want to configure the amneziawg interface? (y/n): \033[0m\n" printf "\033[32;1mThere are no instructions for manual configure yet. Do you want to configure the amneziawg interface? (y/n): \033[0m\n"
@@ -143,19 +117,19 @@ add_tunnel() {
break break
;; ;;
4) 3)
opkg install opkg install openvpn-openssl luci-app-openvpn opkg install opkg install openvpn-openssl luci-app-openvpn
printf "\e[1;32mUse these instructions to configure https://itdog.info/nastrojka-klienta-openvpn-na-openwrt/\e[0m\n" printf "\e[1;32mUse these instructions to configure https://itdog.info/nastrojka-klienta-openvpn-na-openwrt/\e[0m\n"
break break
;; ;;
5) 4)
opkg install opkg install openconnect luci-proto-openconnect opkg install opkg install openconnect luci-proto-openconnect
printf "\e[1;32mUse these instructions to configure https://itdog.info/nastrojka-klienta-openconnect-na-openwrt/\e[0m\n" printf "\e[1;32mUse these instructions to configure https://itdog.info/nastrojka-klienta-openconnect-na-openwrt/\e[0m\n"
break break
;; ;;
6) 5)
echo "Skip. Use this if you're installing an upgrade." echo "Skip. Use this if you're installing an upgrade."
break break
;; ;;
@@ -395,8 +369,7 @@ check_system() {
# Check available space # Check available space
AVAILABLE_SPACE=$(df /tmp | awk 'NR==2 {print $4}') AVAILABLE_SPACE=$(df /tmp | awk 'NR==2 {print $4}')
# Change after switch sing-box REQUIRED_SPACE=15360 # 15MB in KB
REQUIRED_SPACE=1024 # 20MB in KB
echo "Available space: $((AVAILABLE_SPACE/1024))MB" echo "Available space: $((AVAILABLE_SPACE/1024))MB"
echo "Required space: $((REQUIRED_SPACE/1024))MB" echo "Required space: $((REQUIRED_SPACE/1024))MB"

View File

@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-podkop PKG_NAME:=luci-app-podkop
PKG_VERSION:=0.2.5 PKG_VERSION:=0.3.0
PKG_RELEASE:=1 PKG_RELEASE:=1
LUCI_TITLE:=LuCI podkop app LUCI_TITLE:=LuCI podkop app

View File

@@ -18,8 +18,8 @@ return view.extend({
o = s.tab('basic', _('Basic Settings')); o = s.tab('basic', _('Basic Settings'));
o = s.taboption('basic', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing')); o = s.taboption('basic', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing'));
o.value('vpn', ('VPN'));
o.value('proxy', ('Proxy')); o.value('proxy', ('Proxy'));
o.value('vpn', ('VPN'));
o.ucisection = 'main'; o.ucisection = 'main';
o = s.taboption('basic', form.ListValue, 'proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy')); o = s.taboption('basic', form.ListValue, 'proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy'));
@@ -76,47 +76,33 @@ return view.extend({
console.error('Error fetching devices:', error); console.error('Error fetching devices:', error);
} }
o = s.taboption('basic', form.Flag, 'domain_list_enabled', _('Community Domain Lists')); o = s.taboption('basic', form.Flag, 'domain_list_enabled', _('Community Lists'));
o.default = '0'; o.default = '0';
o.rmempty = false; o.rmempty = false;
o.ucisection = 'main'; o.ucisection = 'main';
o = s.taboption('basic', form.ListValue, 'domain_list', _('Domain List'), _('Select a list') + ' <a href="https://github.com/itdoginfo/allow-domains" target="_blank">github.com/itdoginfo/allow-domains</a>'); o = s.taboption('basic', form.DynamicList, 'domain_list', _('Service List'), _('Select predefined service for routing') + ' <a href="https://github.com/itdoginfo/allow-domains" target="_blank">github.com/itdoginfo/allow-domains</a>');
o.placeholder = 'placeholder'; o.placeholder = 'Service list';
o.value('ru_inside', 'Russia inside'); o.value('russia_inside', 'Russia inside');
o.value('ru_outside', 'Russia outside'); o.value('russia_outside', 'Russia outside');
o.value('ua', 'Ukraine'); o.value('ukraine_inside', 'Ukraine');
o.depends('domain_list_enabled', '1'); o.value('geoblock', 'GEO Block');
o.rmempty = false; o.value('block', 'Block');
o.ucisection = 'main'; o.value('porn', 'Porn');
o.value('news', 'News');
o = s.taboption('basic', form.Flag, 'delist_domains_enabled', _('Domain Exclusions'), _('Exclude specific domains from routing rules')); o.value('anime', 'Anime');
o.default = '0'; o.value('youtube', 'Youtube');
o.rmempty = false; o.value('discord', 'Discord');
o.ucisection = 'main';
o.depends('domain_list_enabled', '1');
o = s.taboption('basic', form.DynamicList, 'delist_domains', _('Excluded Domains'), _('Domains to be excluded from routing'));
o.placeholder = 'Delist domains';
o.depends('delist_domains_enabled', '1');
o.rmempty = false;
o.ucisection = 'main';
o = s.taboption('basic', form.Flag, 'subnets_list_enabled', _('Community Subnet Lists'), _('Enable routing for popular services like Twitter, Meta, and Discord'));
o.default = '0';
o.rmempty = false;
o.ucisection = 'main';
o = s.taboption('basic', form.DynamicList, 'subnets', _('Service Networks'), _('Select predefined service networks for routing'));
o.placeholder = 'Service network list';
o.value('twitter', 'Twitter(x.com)');
o.value('meta', 'Meta'); o.value('meta', 'Meta');
o.value('discord', 'Discord(voice)'); o.value('twitter', 'Twitter (X)');
o.depends('subnets_list_enabled', '1'); o.value('hdrezka', 'HDRezka');
o.value('tiktok', 'Tik-Tok');
o.value('telegram', 'Telegram');
o.depends('domain_list_enabled', '1');
o.rmempty = false; o.rmempty = false;
o.ucisection = 'main'; o.ucisection = 'main';
o = s.taboption('basic', form.ListValue, 'custom_domains_list_enabled', _('User Domain List Type'), _('Select how to add your custom domains')); o = s.taboption('basic', form.ListValue, 'custom_domains_list_type', _('User Domain List Type'), _('Select how to add your custom domains'));
o.value('disabled', _('Disabled')); o.value('disabled', _('Disabled'));
o.value('dynamic', _('Dynamic List')); o.value('dynamic', _('Dynamic List'));
o.value('text', _('Text List')); o.value('text', _('Text List'));
@@ -126,7 +112,7 @@ return view.extend({
o = s.taboption('basic', form.DynamicList, 'custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o = s.taboption('basic', form.DynamicList, 'custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)'));
o.placeholder = 'Domains list'; o.placeholder = 'Domains list';
o.depends('custom_domains_list_enabled', 'dynamic'); o.depends('custom_domains_list_type', 'dynamic');
o.rmempty = false; o.rmempty = false;
o.ucisection = 'main'; o.ucisection = 'main';
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
@@ -144,7 +130,7 @@ return view.extend({
o = s.taboption('basic', form.TextValue, 'custom_domains_text', _('User Domains List'), _('Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)')); o = s.taboption('basic', form.TextValue, 'custom_domains_text', _('User Domains List'), _('Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)'));
o.placeholder = 'example.com, sub.example.com\ndomain.com test.com\nsubdomain.domain.com another.com, third.com'; o.placeholder = 'example.com, sub.example.com\ndomain.com test.com\nsubdomain.domain.com another.com, third.com';
o.depends('custom_domains_list_enabled', 'text'); o.depends('custom_domains_list_type', 'text');
o.rows = 10; o.rows = 10;
o.rmempty = false; o.rmempty = false;
o.ucisection = 'main'; o.ucisection = 'main';
@@ -244,7 +230,6 @@ return view.extend({
return _('Invalid format. Use format: X.X.X.X or X.X.X.X/Y'); return _('Invalid format. Use format: X.X.X.X or X.X.X.X/Y');
} }
// Разбираем IP и маску
const [ip, cidr] = value.split('/'); const [ip, cidr] = value.split('/');
const ipParts = ip.split('.'); const ipParts = ip.split('.');
@@ -398,6 +383,11 @@ return view.extend({
return true; return true;
}; };
o = s.taboption('basic', form.Flag, 'socks5', _('Mixed enable'), _('Browser port: 2080'));
o.default = '0';
o.rmempty = false;
o.ucisection = 'main';
// Additional Settings Tab // Additional Settings Tab
o = s.tab('additional', _('Additional Settings')); o = s.tab('additional', _('Additional Settings'));
@@ -408,12 +398,6 @@ return view.extend({
o.rmempty = false; o.rmempty = false;
o.ucisection = 'main'; o.ucisection = 'main';
o = s.taboption('additional', form.Flag, 'socks5', _('Mixed enable'), _('Browser port: 2080'));
o.default = '0';
o.depends('mode', 'proxy');
o.rmempty = false;
o.ucisection = 'main';
o = s.taboption('additional', form.Flag, 'exclude_ntp', _('Exclude NTP'), _('For issues with open connections sing-box')); o = s.taboption('additional', form.Flag, 'exclude_ntp', _('Exclude NTP'), _('For issues with open connections sing-box'));
o.default = '0'; o.default = '0';
o.depends('mode', 'proxy'); o.depends('mode', 'proxy');
@@ -421,238 +405,15 @@ return view.extend({
o.ucisection = 'main'; o.ucisection = 'main';
o = s.taboption('additional', form.ListValue, 'update_interval', _('List Update Frequency'), _('Select how often the lists will be updated')); o = s.taboption('additional', form.ListValue, 'update_interval', _('List Update Frequency'), _('Select how often the lists will be updated'));
o.value('0 */1 * * *', _('Every hour')); o.value('1h', _('Every hour'));
o.value('0 */2 * * *', _('Every 2 hours')); o.value('3h', _('Every 3 hours'));
o.value('0 */4 * * *', _('Every 4 hours')); o.value('12h', _('Every 12 hours'));
o.value('0 */6 * * *', _('Every 6 hours')); o.value('1d', _('Every day'));
o.value('0 */12 * * *', _('Every 12 hours')); o.value('3d', _('Every 3 days'));
o.value('0 4 * * *', _('Once a day at 04:00')); o.default = '1d';
o.value('0 4 * * 0', _('Once a week on Sunday at 04:00'));
o.default = '0 4 * * *';
o.rmempty = false; o.rmempty = false;
o.ucisection = 'main'; o.ucisection = 'main';
// Secondary Settings Tab
o = s.tab('secondary_config', _('Secondary Config'));
o = s.taboption('secondary_config', form.Flag, 'second_enable', _('Secondary VPN/Proxy Enable'), _('Enable secondary VPN/Proxy configuration'));
o.default = '0';
o.rmempty = false;
o.ucisection = 'second';
o = s.taboption('secondary_config', form.ListValue, 'second_mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing'));
o.value('vpn', ('VPN'));
o.value('proxy', ('Proxy'));
o.depends('second_enable', '1');
o.ucisection = 'second';
o = s.taboption('secondary_config', form.ListValue, 'second_proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy'));
o.value('url', _('Connection URL'));
o.value('outbound', _('Outbound Config'));
o.default = 'url';
o.depends('second_mode', 'proxy');
o.ucisection = 'second';
o = s.taboption('secondary_config', form.TextValue, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration'));
o.depends('second_proxy_config_type', 'url');
o.rows = 5;
o.ucisection = 'second';
o = s.taboption('secondary_config', form.TextValue, 'second_outbound_json', _('Outbound Configuration'), _('Enter complete outbound configuration in JSON format'));
o.depends('second_proxy_config_type', 'outbound');
o.rows = 10;
o.ucisection = 'second';
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
try {
const parsed = JSON.parse(value);
if (!parsed.type || !parsed.server || !parsed.server_port) {
return _('JSON must contain at least type, server and server_port fields');
}
return true;
} catch (e) {
return _('Invalid JSON format');
}
};
o = s.taboption('secondary_config', form.ListValue, 'second_interface', _('Network Interface'), _('Select network interface for VPN connection'));
o.depends('second_mode', 'vpn');
o.ucisection = 'second';
try {
const devices = await network.getDevices();
const excludeInterfaces = ['br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0'];
devices.forEach(function (device) {
if (device.dev && device.dev.name) {
const deviceName = device.dev.name;
const isExcluded = excludeInterfaces.includes(deviceName) || /^lan\d+$/.test(deviceName);
if (!isExcluded) {
o.value(deviceName, deviceName);
}
}
});
} catch (error) {
console.error('Error fetching devices:', error);
}
o = s.taboption('secondary_config', form.Flag, 'second_domain_service_enabled', _('Service Domain List Enable'), _('Enable predefined service domain lists for routing'));
o.default = '0';
o.rmempty = false;
o.depends('second_enable', '1');
o.ucisection = 'second';
o = s.taboption('secondary_config', form.ListValue, 'second_service_list', _('Service List'), _('Select predefined services for routing'));
o.placeholder = 'placeholder';
o.value('youtube', 'Youtube');
o.depends('second_domain_service_enabled', '1');
o.rmempty = false;
o.ucisection = 'second';
o = s.taboption('secondary_config', form.ListValue, 'second_custom_domains_list_enabled', _('User Domain List Type'), _('Select how to add your custom domains'));
o.value('disabled', _('Disabled'));
o.value('dynamic', _('Dynamic List'));
o.value('text', _('Text List'));
o.default = 'disabled';
o.rmempty = false;
o.depends('second_enable', '1');
o.ucisection = 'second';
o = s.taboption('secondary_config', form.DynamicList, 'second_custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)'));
o.placeholder = 'Domains list';
o.depends('second_custom_domains_list_enabled', 'dynamic');
o.rmempty = false;
o.ucisection = 'second';
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
if (!domainRegex.test(value)) {
return _('Invalid domain format. Enter domain without protocol (example: sub.example.com)');
}
return true;
};
o = s.taboption('secondary_config', form.TextValue, 'second_custom_domains_text', _('User Domains List'), _('Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)'));
o.placeholder = 'example.com, sub.example.com\ndomain.com test.com\nsubdomain.domain.com another.com, third.com';
o.depends('second_custom_domains_list_enabled', 'text');
o.rows = 10;
o.rmempty = false;
o.ucisection = 'second';
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
const domains = value.split(/[,\s\n]/)
.map(d => d.trim())
.filter(d => d.length > 0);
const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
for (const domain of domains) {
if (!domainRegex.test(domain)) {
return _('Invalid domain format: ' + domain + '. Enter domain without protocol');
}
}
return true;
};
o = s.taboption('secondary_config', form.ListValue, 'second_custom_subnets_list_enabled', _('User Subnet List Type'), _('Select how to add your custom subnets'));
o.value('disabled', _('Disabled'));
o.value('dynamic', _('Dynamic List'));
o.value('text', _('Text List'));
o.default = 'disabled';
o.rmempty = false;
o.depends('second_enable', '1');
o.ucisection = 'second';
o = s.taboption('secondary_config', form.DynamicList, 'second_custom_subnets', _('User Subnets'), _('Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses'));
o.placeholder = 'IP or subnet';
o.depends('second_custom_subnets_list_enabled', 'dynamic');
o.rmempty = false;
o.ucisection = 'second';
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
if (!subnetRegex.test(value)) {
return _('Invalid format. Use format: X.X.X.X or X.X.X.X/Y');
}
const [ip, cidr] = value.split('/');
const ipParts = ip.split('.');
for (const part of ipParts) {
const num = parseInt(part);
if (num < 0 || num > 255) {
return _('IP address parts must be between 0 and 255');
}
}
if (cidr !== undefined) {
const cidrNum = parseInt(cidr);
if (cidrNum < 0 || cidrNum > 32) {
return _('CIDR must be between 0 and 32');
}
}
return true;
};
o = s.taboption('secondary_config', form.TextValue, 'second_custom_subnets_text', _('User Subnets List'), _('Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline'));
o.placeholder = '103.21.244.0/22\n8.8.8.8\n1.1.1.1/32, 9.9.9.9 10.10.10.10';
o.depends('second_custom_subnets_list_enabled', 'text');
o.rows = 10;
o.rmempty = false;
o.ucisection = 'second';
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
const subnets = value.split(/[,\s\n]/)
.map(s => s.trim())
.filter(s => s.length > 0);
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
for (const subnet of subnets) {
if (!subnetRegex.test(subnet)) {
return _('Invalid format: ' + subnet + '. Use format: X.X.X.X or X.X.X.X/Y');
}
const [ip, cidr] = subnet.split('/');
const ipParts = ip.split('.');
for (const part of ipParts) {
const num = parseInt(part);
if (num < 0 || num > 255) {
return _('IP parts must be between 0 and 255 in: ' + subnet);
}
}
if (cidr !== undefined) {
const cidrNum = parseInt(cidr);
if (cidrNum < 0 || cidrNum > 32) {
return _('CIDR must be between 0 and 32 in: ' + subnet);
}
}
}
return true;
};
o = s.tab('diagnostics', _('Diagnostics')); o = s.tab('diagnostics', _('Diagnostics'));
function formatDiagnosticOutput(output) { function formatDiagnosticOutput(output) {
@@ -668,7 +429,7 @@ return view.extend({
`: ${status === 'available' ? '✓' : '✗'}`); `: ${status === 'available' ? '✓' : '✗'}`);
} }
// Check All - полная диагностика // Check All - full diagnostic
o = s.taboption('diagnostics', form.Button, '_check_all'); o = s.taboption('diagnostics', form.Button, '_check_all');
o.title = _('Main Check'); o.title = _('Main Check');
o.description = _('Run a comprehensive diagnostic check of all components'); o.description = _('Run a comprehensive diagnostic check of all components');
@@ -810,6 +571,333 @@ return view.extend({
]); ]);
}; };
// Add new section 'extra'
var s = m.section(form.TypedSection, 'extra', _('Extra configurations'));
s.anonymous = false;
s.addremove = true;
s.addbtntitle = _('Add Section');
o = s.tab('basic', _('Extra configuration'));
o = s.taboption('basic', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing'));
o.value('proxy', ('Proxy'));
o.value('vpn', ('VPN'));
o = s.taboption('basic', form.ListValue, 'proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy'));
o.value('url', _('Connection URL'));
o.value('outbound', _('Outbound Config'));
o.default = 'url';
o.depends('mode', 'proxy');
o = s.taboption('basic', form.TextValue, 'proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration'));
o.depends('proxy_config_type', 'url');
o.rows = 5;
o = s.taboption('basic', form.TextValue, 'outbound_json', _('Outbound Configuration'), _('Enter complete outbound configuration in JSON format'));
o.depends('proxy_config_type', 'outbound');
o.rows = 10;
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
try {
const parsed = JSON.parse(value);
if (!parsed.type || !parsed.server || !parsed.server_port) {
return _('JSON must contain at least type, server and server_port fields');
}
return true;
} catch (e) {
return _('Invalid JSON format');
}
};
o = s.taboption('basic', form.ListValue, 'interface', _('Network Interface'), _('Select network interface for VPN connection'));
o.depends('mode', 'vpn');
try {
const devices = await network.getDevices();
const excludeInterfaces = ['br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0'];
devices.forEach(function (device) {
if (device.dev && device.dev.name) {
const deviceName = device.dev.name;
const isExcluded = excludeInterfaces.includes(deviceName) || /^lan\d+$/.test(deviceName);
if (!isExcluded) {
o.value(deviceName, deviceName);
}
}
});
} catch (error) {
console.error('Error fetching devices:', error);
}
o = s.taboption('basic', form.Flag, 'domain_list_enabled', _('Community Lists'));
o.default = '0';
o.rmempty = false;
o = s.taboption('basic', form.DynamicList, 'domain_list', _('Service List'), _('Select predefined service networks for routing') + ' <a href="https://github.com/itdoginfo/allow-domains" target="_blank">github.com/itdoginfo/allow-domains</a>');
o.placeholder = 'Service list';
o.value('russia_inside', 'Russia inside');
o.value('russia_outside', 'Russia outside');
o.value('ukraine_inside', 'Ukraine');
o.value('geoblock', 'GEO Block');
o.value('block', 'Block');
o.value('porn', 'Porn');
o.value('news', 'News');
o.value('anime', 'Anime');
o.value('youtube', 'Youtube');
o.value('discord', 'Discord');
o.value('meta', 'Meta');
o.value('twitter', 'Twitter (X)');
o.value('hdrezka', 'HDRezka');
o.value('tiktok', 'Tik-Tok');
o.value('telegram', 'Telegram');
o.depends('domain_list_enabled', '1');
o.rmempty = false;
o = s.taboption('basic', form.ListValue, 'custom_domains_list_type', _('User Domain List Type'), _('Select how to add your custom domains'));
o.value('disabled', _('Disabled'));
o.value('dynamic', _('Dynamic List'));
o.value('text', _('Text List'));
o.default = 'disabled';
o.rmempty = false;
o = s.taboption('basic', form.DynamicList, 'custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)'));
o.placeholder = 'Domains list';
o.depends('custom_domains_list_type', 'dynamic');
o.rmempty = false;
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
if (!domainRegex.test(value)) {
return _('Invalid domain format. Enter domain without protocol (example: sub.example.com)');
}
return true;
};
o = s.taboption('basic', form.TextValue, 'custom_domains_text', _('User Domains List'), _('Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)'));
o.placeholder = 'example.com, sub.example.com\ndomain.com test.com\nsubdomain.domain.com another.com, third.com';
o.depends('custom_domains_list_type', 'text');
o.rows = 10;
o.rmempty = false;
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
const domains = value.split(/[,\s\n]/)
.map(d => d.trim())
.filter(d => d.length > 0);
const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
for (const domain of domains) {
if (!domainRegex.test(domain)) {
return _('Invalid domain format: ' + domain + '. Enter domain without protocol');
}
}
return true;
};
o = s.taboption('basic', form.Flag, 'custom_local_domains_list_enabled', _('Local Domain Lists'), _('Use the list from the router filesystem'));
o.default = '0';
o.rmempty = false;
o = s.taboption('basic', form.DynamicList, 'custom_local_domains', _('Local Domain Lists Path'), _('Enter to the list file path'));
o.placeholder = '/path/file.lst';
o.depends('custom_local_domains_list_enabled', '1');
o.rmempty = false;
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
try {
const pathRegex = /^\/[a-zA-Z0-9_\-\/\.]+$/;
if (!pathRegex.test(value)) {
throw new Error(_('Invalid path format. Path must start with "/" and contain only valid characters (letters, numbers, "-", "_", "/", ".")'));
}
return true;
} catch (e) {
return _('Invalid path format');
}
};
o = s.taboption('basic', form.Flag, 'custom_download_domains_list_enabled', _('Remote Domain Lists'), _('Download and use domain lists from remote URLs'));
o.default = '0';
o.rmempty = false;
o = s.taboption('basic', form.DynamicList, 'custom_download_domains', _('Remote Domain URLs'), _('Enter full URLs starting with http:// or https://'));
o.placeholder = 'URL';
o.depends('custom_download_domains_list_enabled', '1');
o.rmempty = false;
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
try {
const url = new URL(value);
if (!['http:', 'https:'].includes(url.protocol)) {
return _('URL must use http:// or https:// protocol');
}
return true;
} catch (e) {
return _('Invalid URL format. URL must start with http:// or https://');
}
};
o = s.taboption('basic', form.ListValue, 'custom_subnets_list_enabled', _('User Subnet List Type'), _('Select how to add your custom subnets'));
o.value('disabled', _('Disabled'));
o.value('dynamic', _('Dynamic List'));
o.value('text', _('Text List (comma/space/newline separated)'));
o.default = 'disabled';
o.rmempty = false;
o = s.taboption('basic', form.DynamicList, 'custom_subnets', _('User Subnets'), _('Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses'));
o.placeholder = 'IP or subnet';
o.depends('custom_subnets_list_enabled', 'dynamic');
o.rmempty = false;
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
if (!subnetRegex.test(value)) {
return _('Invalid format. Use format: X.X.X.X or X.X.X.X/Y');
}
// Разбираем IP и маску
const [ip, cidr] = value.split('/');
const ipParts = ip.split('.');
for (const part of ipParts) {
const num = parseInt(part);
if (num < 0 || num > 255) {
return _('IP address parts must be between 0 and 255');
}
}
if (cidr !== undefined) {
const cidrNum = parseInt(cidr);
if (cidrNum < 0 || cidrNum > 32) {
return _('CIDR must be between 0 and 32');
}
}
return true;
};
o = s.taboption('basic', form.TextValue, 'custom_subnets_text', _('User Subnets List'), _('Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline'));
o.placeholder = '103.21.244.0/22\n8.8.8.8\n1.1.1.1/32, 9.9.9.9 10.10.10.10';
o.depends('custom_subnets_list_enabled', 'text');
o.rows = 10;
o.rmempty = false;
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
// Split by commas, spaces and newlines
const subnets = value.split(/[,\s\n]/)
.map(s => s.trim())
.filter(s => s.length > 0);
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
for (const subnet of subnets) {
if (!subnetRegex.test(subnet)) {
return _('Invalid format: ' + subnet + '. Use format: X.X.X.X or X.X.X.X/Y');
}
const [ip, cidr] = subnet.split('/');
const ipParts = ip.split('.');
for (const part of ipParts) {
const num = parseInt(part);
if (num < 0 || num > 255) {
return _('IP parts must be between 0 and 255 in: ' + subnet);
}
}
if (cidr !== undefined) {
const cidrNum = parseInt(cidr);
if (cidrNum < 0 || cidrNum > 32) {
return _('CIDR must be between 0 and 32 in: ' + subnet);
}
}
}
return true;
};
o = s.taboption('basic', form.Flag, 'custom_download_subnets_list_enabled', _('Remote Subnet Lists'), _('Download and use subnet lists from remote URLs'));
o.default = '0';
o.rmempty = false;
o = s.taboption('basic', form.DynamicList, 'custom_download_subnets', _('Remote Subnet URLs'), _('Enter full URLs starting with http:// or https://'));
o.placeholder = 'URL';
o.depends('custom_download_subnets_list_enabled', '1');
o.rmempty = false;
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
try {
const url = new URL(value);
if (!['http:', 'https:'].includes(url.protocol)) {
return _('URL must use http:// or https:// protocol');
}
return true;
} catch (e) {
return _('Invalid URL format. URL must start with http:// or https://');
}
};
o = s.taboption('basic', form.Flag, 'all_traffic_from_ip_enabled', _('IP for full redirection'), _('Specify local IP addresses whose traffic will always use the configured route'));
o.default = '0';
o.rmempty = false;
o = s.taboption('basic', form.DynamicList, 'all_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses'));
o.placeholder = 'IP';
o.depends('all_traffic_from_ip_enabled', '1');
o.rmempty = false;
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/;
if (!ipRegex.test(value)) {
return _('Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)');
}
const ipParts = value.split('.');
for (const part of ipParts) {
const num = parseInt(part);
if (num < 0 || num > 255) {
return _('IP address parts must be between 0 and 255');
}
}
return true;
};
// For future
// o = s.taboption('basic', form.Flag, 'socks5', _('Mixed enable'), _('Browser port: 2080 (extra +1)'));
// o.default = '0';
// o.rmempty = false;
return m.render(); return m.render();
} }
}); });

View File

@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=podkop PKG_NAME:=podkop
PKG_VERSION:=0.2.5 PKG_VERSION:=0.3.0
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_MAINTAINER:=ITDog <podkop@itdog.info> PKG_MAINTAINER:=ITDog <podkop@itdog.info>
@@ -12,7 +12,7 @@ include $(INCLUDE_DIR)/package.mk
define Package/podkop define Package/podkop
SECTION:=net SECTION:=net
CATEGORY:=Network CATEGORY:=Network
DEPENDS:=+dnsmasq-full +curl +jq +kmod-nft-tproxy +coreutils-base64 DEPENDS:=+sing-box +curl +jq +kmod-nft-tproxy +coreutils-base64
TITLE:=Domain routing app TITLE:=Domain routing app
URL:=https://itdog.info URL:=https://itdog.info
PKGARCH:=all PKGARCH:=all
@@ -33,8 +33,6 @@ define Package/podkop/prerm
grep -q "105 podkop" /etc/iproute2/rt_tables && sed -i "/105 podkop/d" /etc/iproute2/rt_tables grep -q "105 podkop" /etc/iproute2/rt_tables && sed -i "/105 podkop/d" /etc/iproute2/rt_tables
rm -f /etc/hotplug.d/iface/50-podkop
exit 0 exit 0
endef endef
@@ -49,9 +47,6 @@ define Package/podkop/install
$(INSTALL_DIR) $(1)/etc/config $(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/etc/config/podkop $(1)/etc/config/podkop $(INSTALL_CONF) ./files/etc/config/podkop $(1)/etc/config/podkop
$(INSTALL_DIR) $(1)/etc/hotplug.d/iface
$(INSTALL_DATA) ./files/etc/hotplug.d/iface/50-podkop $(1)/etc/hotplug.d/iface/50-podkop
endef endef
$(eval $(call BuildPackage,podkop)) $(eval $(call BuildPackage,podkop))

View File

@@ -1,13 +1,12 @@
config main 'main' config main 'main'
option mode '' option mode 'proxy'
option interface '' #option interface ''
option proxy_config_type '' option proxy_config_type ''
#option outbound_json '' #option outbound_json ''
#option proxy_string '' option proxy_string ''
option domain_list_enabled '1' option domain_list_enabled '1'
option domain_list 'ru_inside' option domain_list 'russia_inside'
option subnets_list_enabled '0' option subnets_list_enabled '0'
#list subnets 'twitter'
option custom_domains_list_type 'disable' option custom_domains_list_type 'disable'
#list custom_domains '' #list custom_domains ''
#option custom_domains_text '' #option custom_domains_text ''
@@ -29,21 +28,5 @@ config main 'main'
option yacd '0' option yacd '0'
option socks5 '0' option socks5 '0'
option exclude_ntp '0' option exclude_ntp '0'
option update_interval '' option update_interval '1d'
option custom_domains_text option custom_domains_text
config second 'second'
option second_enable '0'
option second_mode 'proxy'
option second_interface ''
option second_proxy_config_type ''
#option second_outbound_json ''
#option second_proxy_string ''
option second_domain_service_enabled '0'
#list second_service_list 'youtube'
option second_custom_domains_type 'disable'
#list second_custom_domains 'ifconfig.io'
#option second_custom_domains_text ''
option second_custom_subnets_type 'disable'
#list second_custom_subnets ''
#porion second_custom_subnets_text ''

View File

@@ -1,28 +0,0 @@
#!/bin/sh
. /lib/functions.sh
config_load "/etc/config/podkop"
. /etc/init.d/podkop
config_get "interface" "main" "interface" "0"
config_get "mode" "main" "mode" "0"
if [ "$mode" = "vpn" ] && [ -n "$interface" ]; then
add_route_interface "$interface" "podkop"
fi
if [ "$mode" = "proxy" ]; then
echo "Add route for main tproxy"
add_route_tproxy podkop
fi
config_get second_enable "second" "second_enable" "0"
config_get second_interface "second" "second_interface" "0"
config_get "second_mode" "second" "second_mode" "0"
if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "vpn" ] && [ -n "$second_interface" ]; then
add_route_interface "$second_interface" "podkop2"
fi
if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "proxy" ]; then
echo "Add route for second tproxy"
add_route_tproxy podkop2
fi

File diff suppressed because it is too large Load Diff