В отличие от Podkop и ему подобных, этот способ позволяет более точно заворачивать трафик посредством снифинга целевого домена и подключать внешние списки заблокированных сайтов с автоматическим обновлением.
Не нашёл хороших русскоязычных руководств по этой теме, настроил у себя и решил написать инструкцию.

Требования к оборудованию

  • OpenWrt 24.10.0 или новее.

  • Не менее 256 МБ ОЗУ.

  • Не менее 64 МБ ПЗУ (я не проверял на 64 МБ — зависит от того, сколько занимает система для вашего роутера; 128 МБ точно хватит). В теории можно упаковать исполняемый файл sing-box с помощью UPX, но это может увеличить расход оперативной памяти.

  • На Filogic 820 примерная пропускная способность — 600–800 Мбит/с. На MT7621 должно получиться не менее 100 Мбит/с, возможно больше.

Перенаправление трафика в sing-box. OpenWrt-Momo

Большинство руководств, которые я видел, предполагают использовать софт, в котором маршрутизация происходит до sing-box (например, Podkop или RuAntiBlock).
Если пользователь хочет проводить всю маршрутизацию силами sing-box или xray, обычно предлагается самостоятельно создавать все необходимые правила перенаправления трафика, как в этом руководстве. Это сложно и неудобно (что если вы не хотите направлять весь трафик, а только от определённых клиентов локальной сети или на определённые порты?). Я пробовал настраивать это вручную ещё в 2024 году, у меня ничего не получилось и я надолго забросил эту идею.

Недавно обнаружил, что китайские разработчики сделали пакет для OpenWrt, который делает ровно то, что нужно: направляет трафик в sing-box, позволяя ограничить перенаправляемый трафик по целевым портам и по источникам в локальной сети. Также этот софт может получать настройки из внешнего источника (получает файл конфига по ссылке; для безопасности можно использовать HTTP Basic Authorization).

Итак, рассмотрим интерфейс китайского пакета Momo для OpenWrt:

Главная страница.
Здесь есть кнопки для перезагрузки конфигурации и полного перезапуска сервиса.

  • Choose Profile: выбрать из настроенных профилей.

  • Start Delay: задать задержку запуска после старта устройства.

  • Scheduled Restart: создать cron-задачу для перезапуска сервиса по расписанию (основное назначение - очистка лога sing-box, поскольку он сбрасывается только после перезапуска сервиса).

  • Test Profile: перед запуском профиля выполняет sing-box check для проверки корректности конфига.

  • Fast Reload: при изменении конфига вместо перезапуска sing-box заставляет текущий процесс перечитать конфиг.

  • Core Only: отключает все функции перенаправления и просто запускает sing-box.

Загрузка файлов конфига с устройства, их удаление или добавление подписки.

Интерфейс для добавления подписки.

Вкладка, дающая возможность редактировать конфиги прямо из интерфейса.

Вкладка с логами самого Momo и sing-box.

Главная функциональная вкладка для настройки перенаправления трафика в sing-box.

  • IPv4 DNS Hijack и IPv6 DNS Hijack: перехват IPv4/IPv6 DNS-запросов и их направление в sing-box. Авторы рекомендуют включать оба, если вы планируете обрабатывать DNS силами sing-box: даже если у вас нет IPv6-интернета, локальные компьютеры всё равно могут делать DNS-запросы к роутеру по IPv6 (через link-local).

  • IPv4 Proxy и IPv6 Proxy: перенаправление IPv4 и IPv6 трафика в sing-box. IPv6 можно выключить, если у вас его нет.

  • Fake-IP Ping Hijack: в sing-box есть механизм выдачи вместо реальных IP адресов адресов из заданного диапазона (fake-IP) для доменов, которые нужно обрабатывать внутри sing-box. На слабых устройствах можно выдавать адреса из этого диапазона и перенаправлять только запросы к этим адресам в sing-box. Эта опция позволяет возвращать реальный ping до конечного адреса, а не 1 мс до самого роутера. Если вы не настраивали Fake-IP, вам это не нужно, далее я не буду касаться настройки Fake-IP.

  • TCP Mode: режим перенаправления для TCP-трафика. Нам нужен TPROXY, т.к. TUN гораздо более затратен по CPU для sing-box.

  • UDP Mode: режим перенаправления для UDP-трафика. Также предпочитаем TPROXY по тем же причинам.

Позволяет по различным критериям включить или отключить перенаправление трафика, источником которого является сам роутер в sing-box. Я просто выключил.

Позволяет по IPv4 / IPv6 / MAC клиента включить или отключить для него перенаправление трафика или перехват DNS-запросов.

Приоритет правил - от первого к последнему, таким образом, если вы хотите включить для всех, кроме какого-то хоста, делайте так:

А если только для определённого хоста то так:

Помимо локальной сети можно добавить другой интерфейс, например гостевой или интерфейс клиентов VPN.

  • Bypass China Mainland IP: опция, не нужная нам (не направляет трафик к китайским подсетям в sing-box).

  • Destination TCP Port to Proxy: направление в sing-box только части целевых TCP-портов. Используется, чтобы не нагружать sing-box торрент-трафиком (для большого количества соединений требуется много ОЗУ; при 256 МБ ОЗУ может перестать хватать примерно на ~2000 соединений).
    Порты, которые я добавил:

    • 20, 21 FTP

    • 80, 443, 8080–8880 HTTP/HTTPS

    • 110, 143, 465, 587, 993, 995 почта

    • 2000–2099 есть информация, что используются для Discord

  • Destination UDP Port to Proxy перенаправление UDP-портов в sing-box:

    • 443 QUIC

    • 19294–19344, 50000–50032 Discord

  • Bypass DSCP пропуск трафика с определённой DSCP-меткой.

Настройки портов, куда перенаправляется трафик, в Momo нет, он читает их из конфигурации sing-box.

Составление конфигурации sing-box

Sing-box использует JSON-формат конфига, описанный на официальном сайте. Здесь упомяну только то, что нам потребуется.

Каркас конфигурации

{
  "log": {},
  "dns": {},
  "endpoints": [],
  "inbounds": [],
  "outbounds": [],
  "route": {},
  "experimental": {}
}

Раздел log

Задаёт уровень логов, файл для вывода и т.д.
https://sing-box.sagernet.org/configuration/log/

"log": {
  "level": "warn"
},

Раздел dns

Позволяет задать DNS-серверы, на которых будет происходить обработка запросов, и выбирать DNS-сервер в зависимости от домена.
https://sing-box.sagernet.org/configuration/dns/

"dns": {
  "servers": [
    {
      "type": "udp",
      "tag": "yandex-dns",
      "server": "77.88.8.8",
      "detour": "direct-out"
    },
    {
      "type": "udp",
      "tag": "NSDI-dns",
      "server": "195.208.5.1",
      "detour": "direct-out"
    },
    {
      "type": "https",
      "tag": "cloudflare-dns",
      "server": "1.1.1.1",
      "detour": "direct-out"
    },
    {
      "type": "https",
      "tag": "google-dns",
      "server": "8.8.8.8",
      "detour": "direct-out"
    },
    {
      "type": "https",
      "tag": "Quad9-dns",
      "server": "dns.quad9.net",
      "domain_resolver": "yandex-dns",
      "detour": "direct-out"
    }
  ],
  "rules": [
    {
      "rule_set": "geosite-category-ru",
      "server": "NSDI-dns"
    },
    {
      "rule_set": "refilter_domains",
      "server": "Quad9-dns"
    }
  ]
},

Quad9 используется, поскольку этот сервис не передаёт IP клиента, запросившего адрес (были случаи, когда зарубежные DNS намеренно отдавали некорректный адрес при запросе с русского IP).

Раздел endpoints

Позволяет задавать WireGuard и Tailscale-соединения.
https://sing-box.sagernet.org/configuration/endpoint/

"endpoints": [
  {
    "type": "wireguard",
    "tag": "warp",
    "detour": "direct-out",
    "system": false,
    "mtu": 1280,
    "address": [
      "172.16.0.2/32",
      "*:*:*/128"
    ],
    "private_key": "*",
    "peers": [
      {
        "address": "162.159.192.1",
        "port": 2408,
        "public_key": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
        "allowed_ips": [
          "0.0.0.0/0",
          "::/0"
        ],
        "persistent_keepalive_interval": 30,
        "reserved": "dHbH"
      }
    ],
    "amnezia": {
      "jc": 120,
      "jmin": 23,
      "jmax": 911
    }
  }
],

Внимание: amnezia доступна только при использовании форка.

Раздел inbounds

Здесь настраиваются способы получения трафика.
https://sing-box.sagernet.org/configuration/inbound/

"inbounds": [
  {
    "type": "tproxy",
    "tag": "tproxy-in",
    "listen": "::",
    "listen_port": 12345,
    "tcp_fast_open": true
  },
  {
    "type": "direct",
    "tag": "dns-in",
    "listen": "::",
    "listen_port": 5354
  }
],

Важно: блоки tproxy-in и dns-in надо называть именно так, Momo ищет их при чтении файла, чтобы получить порт. Можно дополнительно создать mixed-прокси, для которого весь трафик будет идти в туннель.

Раздел outbounds

Выходы для трафика: нужно настроить прокси, через который будет выводиться заблокированный трафик. При использовании форка доступны дополнительные типы, включая XHTTP.
https://sing-box.sagernet.org/configuration/outbound/

"outbounds": [
  {
    "type": "direct",
    "tag": "direct-out",
    "connect_timeout": "20s",
    "domain_resolver": {
      "server": "yandex-dns",
      "strategy": "ipv4_only"
    }
  },
  {
    "type": "urltest",
    "tag": "block-check",
    "outbounds": [
      "warp",
      "tunnel"
    ],
    "url": "http://cp.cloudflare.com/generate_204",
    "interval": "1m"
  },
  {
    "packet_encoding": "xudp",
    "server": "*",
    "server_port": 443,
    "uuid": "*",
    "type": "vless",
    "tag": "tunnel",
    "tcp_fast_open": true,
    "tcp_multi_path": true,
    "reuse_addr": true,
    "flow": "xtls-rprx-vision",
    "tls": {
      "enabled": true,
      "server_name": "*",
      "utls": {
        "enabled": true,
        "fingerprint": "firefox"
      },
      "reality": {
        "enabled": true,
        "public_key": "*",
        "short_id": "*"
      }
    },
    "domain_resolver": {
      "server": "Quad9-dns",
      "strategy": "prefer_ipv6"
    }
  }
],
  • direct отправляет трафик напрямую.

  • urltest позволяет выбрать наиболее быстрый/рабочий outbound (поддерживает endpoints).

  • Для каждого outbound можно задать, какой DNS-сервер использовать и как выбирать IP (стратегии: ipv4_only, prefer_ipv6 и т.д.).

Раздел route

Один из ключевых разделов: определяет, какой трафик куда пойдёт. Также позволяет подключать rule_set, подготовленные другими людьми.
https://sing-box.sagernet.org/configuration/route/

Я вижу два основных подхода:

  1. Направить все соединения к российским ресурсам напрямую; остальное в туннель.

  2. Направлять в туннель только то, что есть в списках заблокированного; всё остальное напрямую.

Также можно сочинить множество собственных правил (например, для блокировки рекламы). Я знаю два возможных источника списков заблокированного:

  • antizapret-sing-box: огромный список; с большой вероятностью там будет любой заблокированный ресурс, но из-за отсутствия аккуратной очистки там много мёртвых доменов, что негативно влияет на производительность и потребление памяти.

  • Re-filter-lists: попытка создать более чистый список (была статья на Хабре). Минусы: автор редко обновляет список (раз в пару месяцев или реже) и принимает решения о добавлении доменов по своим критериям. (Личный пример: я предложил три аниме-сайта с подтверждением блокировки с сайта Роскомнадзора, включая известный MyAnimeList; все три предложения были отклонены без объяснения. Другие предложения, в том числе мои, обрабатывались нормально.)

В примерах ниже использую Re-filter.

Вариант направления по спискам

"route": {
  "rules": [
    {
      "action": "sniff"
    },
    {
      "protocol": "dns",
      "action": "hijack-dns"
    },
    {
      "inbound": "dns-in",
      "action": "hijack-dns"
    },
    {
      "ip_is_private": true,
      "outbound": "direct-out"
    },
    {
      "protocol": "bittorrent",
      "outbound": "direct-out"
    },
    {
      "rule_set": "refilter_domains",
      "outbound": "block-check"
    },
    {
      "action": "resolve",
      "strategy": "ipv4_only"
    },
    {
      "rule_set": "refilter_ipsum",
      "outbound": "block-check"
    }
  ],
  "rule_set": [
    {
      "tag": "refilter_domains",
      "type": "remote",
      "format": "binary",
      "url": "https://github.com/1andrevich/Re-filter-lists/releases/latest/download/ruleset-domain-refilter_domains.srs"
    },
    {
      "tag": "refilter_ipsum",
      "type": "remote",
      "format": "binary",
      "url": "https://github.com/1andrevich/Re-filter-lists/releases/latest/download/ruleset-ip-refilter_ipsum.srs"
    }
  ],
  "auto_detect_interface": true,
  "default_domain_resolver": "google-dns"
},

Объяснение правил:

  • sniff позволяет sing-box определить протокол трафика. Без него нельзя узнать, что трафик DNS, bittorrent и т.д.

  • hijack-dns перенаправляет обработку DNS во внутренний DNS sing-box. Отдельно для dns-in потому что у меня был случай, когда трафик не распознавался как DNS, и тысячи UDP-соединений забили память роутера.

  • ip_is_private если на tproxy попали соединения на локальные IP, их нужно отправить напрямую (Momo, по идее, не должен их заворачивать, но перестраховка не лишняя).

  • bittorrent если торрент-трафик оказался на портах, которые мы заворачиваем на sing-box, лучше направить его напрямую (включая соображения возможных проблем от хостера).

  • refilter_domains и refilter_ipsum разделены, т.к. туннель может иметь IPv6-адрес при отсутствии такового у провайдера; при попадании на соответствующий outbound разрешение будет происходить по правилам этого outbound.

  • resolve меняем в зависимости от наличия или отсутствия IPv6 у домашнего провайдера.

Вариант: российские ресурсы напрямую, остальное в туннель

"route": {
  "rules": [
    {
      "action": "sniff"
    },
    {
      "protocol": "dns",
      "action": "hijack-dns"
    },
    {
      "inbound": "dns-in",
      "action": "hijack-dns"
    },
    {
      "ip_is_private": true,
      "outbound": "direct-out"
    },
    {
      "protocol": "bittorrent",
      "outbound": "direct-out"
    },
    {
      "rule_set": "geosite-category-ru",
      "outbound": "direct-out"
    },
    {
      "action": "resolve",
      "strategy": "ipv4_only"
    },
    {
      "rule_set": "geoip-ru",
      "outbound": "direct-out"
    }
  ],
  "rule_set": [
    {
      "tag": "geosite-category-ru",
      "type": "remote",
      "format": "binary",
      "url": "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-category-ru.srs"
    },
    {
      "tag": "geoip-ru",
      "type": "remote",
      "format": "binary",
      "url": "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-ru.srs"
    }
  ],
  "auto_detect_interface": true,
  "default_domain_resolver": "google-dns",
  "final": "block-check"
},

final sing-box по умолчанию отправляет трафик в первый outbound; с помощью final можно переопределить выход по умолчанию.

Раздел experimental

Позволяет управлять хранением кэша rule_set и включить внешний мониторинг.
https://sing-box.sagernet.org/configuration/experimental/

"experimental": {
  "cache_file": {
    "enabled": false
  },
  "clash_api": {
    "external_controller": "[::]:9090"
  }
}

Здесь я отключаю кэш, чтобы приложение лишний раз не записывало в постоянную память роутера. Мониторинг можно посмотреть через YACD
Внимание: если открыть HTTPS-версию вместо HTTP, современные браузеры могут разрешать доступ только к localhost, это ограничение браузеров ради безопасности (чтобы сайты не могли сканировать локальную сеть).

Пример окна мониторинга:

Дополнительно

  • Для перенаправления на TPROXY требуются пакеты kmod-nf-tproxy и kmod-nft-socket.

  • Если у вас, как и у меня, не работает китайский репозиторий (у меня он заблокирован и даже через европейский прокси не работает), просто скачайте из релизов пакеты под вашу архитектуру и установите, например: momo_2025.08.11-r3_aarch64_cortex-a53.ipk и luci-app-momo_1.0.5-r1_all.ipk.

  • Рекомендую установить kmod-tcp-bbr, он даёт преимущества BBR для всех tcp соединений, которые будут идти через sing-box.

  • Чтобы уменьшить шанс падения при нехватке памяти при неожиданно большом количестве соединений, можно поставить zram-swap.

  • После установки sing-box отключите его сервис, дальше его будет запускать Momo. Внимание: OpenWrt отключает автозагрузку только после перезапуска.

  • Можно попробовать избежать падений, добавив в init.d-файл Momo значение переменной окружения GOMEMLIMIT с нужным лимитом. Это заставит sing-box раньше вызывать сборщик мусора, но замедлит его в эти моменты. Для 256 МБ памяти без использования zram-swap разумный лимит около 100 МБ.

Методы установки DSCP метки

  • transmission поддерживает установку меток через параметр peer_socket_tos. Исходный код

  • qBittorrent имеет параметры peer-tos и peer-dscp но по информации от 23 года добавляет метку не изначально а уже после установления соединения с пиром что мешает обработке соединений правилами.

  • в Windows можно любому приложению задать DSCP групповой политикой или через powershell команды.

New-NetQosPolicy -Name "qb" -AppPathNameMatchCondition "qbittorrent.exe" -PolicyStore GPO:localhost -DSCPAction 8
Get-NetQosPolicy
Remove-NetQosPolicy -Name "qb"

Комментарии (10)


  1. glebliutsko
    18.12.2025 06:11

    В Dnsmasq (который по умолчанию используется в openwrt как DNS) есть опция NFTSet/IPSet. Позволяет задать список доменов и их отрезолвенные IP класть в nftables/ipset.

    Ну а далее, уже правилами nftables с трафиком можно делать что угодно. Например ставить метку на пакет и маршрутизирвоать по ней, натравливать на соединение zapret для обмана DPI.


    1. Zipdots
      18.12.2025 06:11

      С ресурсами за CDN будет проблемка


    1. abubaca4 Автор
      18.12.2025 06:11

      Podkop и RuAntiBlock примерно так и работают.
      Вносят все домены в NFTSet, далее когда по какому то домену есть запрос сохраняют ip со сроком жизни кеша 2 часа и делают для этого ip маршрут в прокси. Если у домена было много ip то берётся только один из них.
      Я не отрицаю что этот способ более производительный но
      Что если у пользователя настроен DoH(многие современные браузеры это делают автоматически и это надо отключать)?
      Что если пользователь уже получил ip от dns на мобильном интернете, пришёл домой и у него в кеше остался ip про который роутер не знает?
      Что если множество сайтов сидят за CDN и блока по ip нет, только по домену силами тпсу? Гоним трафик на все сайты в прокси?
      Sing-box лишён всех этих проблем так как достаёт домен прямо из запроса.


      1. glebliutsko
        18.12.2025 06:11

        Что если пользователь уже получил ip от dns на мобильном интернете, пришёл домой и у него в кеше остался ip про который роутер не знает?

        По моим наблюдениям android сбрасывает локальный dns кэш при переключении между сетями.

        Что если множество сайтов сидят за CDN и блока по ip нет, только по домену силами тпсу? Гоним трафик на все сайты в прокси?

        Не проверял, но подозреваю, что sing-box тоже пустит трафик для всего IP в VPN. Определить нужен ли VPN для сайта нужно ещё во время отправки TCP-SYN, а это происходит сильно раньше, чем браузер раскроет домен отправив SNI.

        Если у вас нет своей ASN, то маршрутизировать трафик анализом SNI не получится физически, т.к. если мы переключем маршрут для уже установленного TCP соединения, то оно сломается, т.к. разный внешний IP.

        Что если у пользователя настроен DoH(многие современные браузеры это делают автоматически и это надо отключать)?

        Тут согласен. К сожалению, браузеры решили, что они в праве по умолчанию переопределять настойки ОС. Приходится постоянно отключать, что бы не только NFTSet работал, но и локальные домены резолвились.

        В целом не имею ничего против sing-box, и не настаиваю на своем способе. Просто он мне кажется слишком громостким для роутера.


  1. Rubilnik
    18.12.2025 06:11

    В отличие от Podkop и ему подобных, этот способ позволяет более точно заворачивать трафик посредством снифинга целевого домена и подключать внешние списки заблокированных сайтов с автоматическим обновлением.

    Стоп, но podkop же тоже так умеет. Как локальные списки, так и текстовые и из внешних источников.


    1. SlavaSch
      18.12.2025 06:11

      И маршрутизацию подкоп делает силами sing-box


      1. abubaca4 Автор
        18.12.2025 06:11

        Версия которой я пользовался примерно год назад делала NFTSet из заблокированных доменов и при запросе адреса оттуда делала маршрут на этот адрес в прокси.

        Если они перешли полностью на использование sing-box, то это в чём то положительное изменение тк точность стала выше, но одновременно и отрицательное, тк маршрутизация с его помощью более ресурсо затратна и помешает работе на слабых роутерах.


        1. SlavaSch
          18.12.2025 06:11

          Как раз почти год назад переезд на коробку и случился)

          Для слабых устройств есть вариант, например, со скрипто getDomains - да, не так удобно, но вполне рабочее решение. А кому надо удобно - всегда можно взять роутер за 3-4к рублей с 256-512МБ ОЗУ, 128МБ ПЗУ и на 820 филоджике


    1. abubaca4 Автор
      18.12.2025 06:11

      В последний раз когда я пользовался подкопом он поддерживал только .lst списки с одним доменом/подсетью на строку. При этом внешний список с периодическим обновлением можно было задать только на один из двух выходов трафика, при этом второй выход трафика имел меньший приоритет(те нельзя направить трафик во второй выход если он был задан в первом например в автоматически обновляемом списке). При этом какие то 25к подсетей вешали подкоп намертво поскольку он их добавлял правилами маршрутизации по 1 штуке.

      Поддержка списков sing-box прямо это позитивное изменение, но я всё равно предпочту сам написать конфиг для sing-box, как я его написал для всех телефонов где он используется и компьютеров.


  1. itdog
    18.12.2025 06:11

    В отличие от Podkop и ему подобных, этот способ позволяет более точно заворачивать трафик посредством снифинга целевого домена

    Вам уже указали на ошибку. Как видите, лучше всё-таки смотреть исходный код проекта перед публикацией статьи на Хабре.

    Или даже проще, можно поставить podkop и посмотреть конфиг sing-box и dnsmasq.

    Или ещё проще, посмотрите схему работы на картинке: https://podkop.net/docs/fakeip/

    А по вашей реализации. Процессор, который сниффает весь трафик - это лёгкий, но не лучший подход для soho-роутера. Тем более вы знаете, что такое FakeIP/FakeDNS.

    За рекламу, хоть и такую, спасибо.