Цели и задачи
Работающий интернет в частный дом в Московской области. Проводных аналогов нет.
Что имеем на руках
Роутер Mikrotik hap ac3 LTE. Но можно любой микротик + LTE модем - я настраивал сначала все именно так, а потом переносил на микротик LTE. Всю сложную логику будем реализовывать на микротике.
Прошивка RouterOS 7.20.7 long term.
Мобильный тариф с безлимитным интернетом. Тут развилка:
Либо у нас тариф для роутера - тогда перед нами все дороги открыты. Можем использовать роутер микротик LTE - просто вставляем симку в него и настраиваем. Модем использовать микротик без LTE - тогда покупаем любой LTE модем с USB и втыкаем его в микротик. Главное чтобы модем не был прошит под IMEI смартфона, т.к. в смартфоне данная симкарта может не заработать (зависит от оператора)
Либо у нас тариф для смартфона. Тогда симку нельзя вставлять в микротик LTE или другой LTE роутер, потому что оператор это просечет (по IMEI LTE модуля роутера и может в добавок по TTL) и заблокирует интернет на симке. Тут нужен как раз LTE модем прошитый под телефонный IMEI - такие сейчас продаются готовые на любом маркетплейсе. Смотрите его IMEI в настройках роутера в GUI и проверяете на любом сайте проверке IMEI - должен показать модель телефона к которому он относится. Тут у нас получится вариант микротик (любой) + прошитый LTE модем USB.
Нулевой вариант
Вставляем симкарту в LTE роутер и интернет работает. Но не стабильно, медленно, многие сайты периодически не открываются. Если у вас все стабильно и все всегда открывается, нет проблем с сайтами 4pda, xiaomi, не говоря уже у google play, дальше можете не читать - статья для тех у кого есть проблемы при настройках по умолчанию, как было у меня.
Базовые (простейшие) настройки
Закрепить в настройках LTE роутера выбор сети - только LTE, остальные варианты отключаем.

Закрепить конкретную частоту в настроке LTE роутера. Для этого смотрим за какую частоту он зацепился, выбираем только ее, остальные выключаем.


Смотрим качество сигнала, меряем скорость интернета.

Сила сигнала чем больше тем лучше (ближе к нулю лучше, всегда показывает с минусом)
Качество сигнала (SINR) чем больше тем лучше (должно быть 10 и выше, если сигнал хороший).
Далее в настройках оставляем все частоты, кроме той которую замерили, и повторяем все по кругу - смотрим за какую частоту теперь зацепился, проверяем ее качество и скорость. Так пока не переберем все доступные частоты (т.е. у нас остануться включенными часть частот на которые уже не будет подключаться интернет).
В принципе для ускорения предварительно можно посмотреть доступные частоты в точке установки LTE модема через телефон, приложение NetMonster.
Ставите в телефон ту симкарту которая будет в модеме, телефон в ту точку где будет стоять LTE модем, и смотрите доступные частоты и силу и качество сигнала в них. Но учтите, что модем в телефоне гораздо мощнее чем в LTE модеме, кроме того в телефоне есть агрегация частот, и мгновенное переключение между ними (подключение новых). Так что сила и качество сигнала которую покажет телефон будет намного лучше чем в модеме потом. Но нам главное понять 1-2 самых лучших частоты в данной точке и потом проверить их в LTE модеме.

Управление качеством
Если после выполнения базовых настроек у нас все равно что-то отваливается, не открываются сайты периодически, то читаем дальше.
У решил сделать комплексную систему мониторинга, оповещения и управления качеством.
На самом деле вариантов управления у нас не так много:
Уменьшать / балансировать MSS (размер пакета) при плохом качестве интернета
Перезапускать LTE модуль для получения нового ip и выхода из блокировок DPI / залипания сессии и т.п.
Для этого нам нужно 1) мониторить качество интернета - настраиваем 2 правила mangle (счетчики качества) на микротик. Заходим в GUI (192.168.88.1), терминал:
/ip firewall mangle add action=passthrough chain=forward comment=DATA_TRAFFIC connection-state=established protocol=tcp add action=passthrough chain=forward comment=TCP_ERRORS connection-state=invalid protocol=tcp
Проверить можно в IP --> Firewall --> Mangle
Качество = 100% - %ошибок, где %ошибок = TCP-ошибки / TCP-траффик за период
Можно также смотреть ошибки TCP_RST, но я их не пока не успел настроить.
Если ошибок > порога, будем снижать MSS для стабилизации канала. Если ошибок < порога в течении определенного времени, будем медленно повышать MSS обратно.
2) проверять доступность ключевых сервисов (сайтов). В моем случае это базовые Android и Microsoft (те которые проверяются смартфоном и виндой и если с ними проблемы появляется восклицательный знак на сети Wifi) и важные для меня xiaomi и 4pda. Базовые я рекомендую оставить, а дополнительные у вас могут быть свои.
Если не доступна часть (или хотя бы один) сервисов - пробуем резко снизить mss, и проверить этот сервис еще раз. Если не получается даже с очень низким MSS, то имеем либо физическую проблему на канале, либо зависание сессии LTE, либо что чаще всего для тяжелых https сервисов типа 4pda - DPI блокировку оператора.
Во всех этих случаях наш вариант - перезагрузка LTE модема. Я делал мягкую перезагрузку LTE интерфейса в микротик (disable/enable), но можно попробовать и более быстрый вариант (режим полета вкл/выкл) - через AT команду. Цель этого - перерегистрироваться в сети оператора и получить новый ip, который пройдет по новым чистым путям до наших сервисов и они станут доступны.
Автоматизация - мониторинг качества интернета
Создаем счетчики mangle для расчета качества (см. выше скрипт)
Создаем правило mangle для управление параметром MSS
Создаем скрипт для анализа счетчиков, расчета качества и динамическим управлением MSS. Дополнительно этот скрипт будет логировать метрики если %ошибок > 0 или если меняем MSS (в любую сторону) (скрипт будет ниже)
Ставим на график запуска - каждые 5 сек (чтобы быстрее реагировать на падение качества)
Правило mangle для управления параметром MSS (обратите внимание на out-interface - там должно быть имя вашего LTE интерфейса в микротик)
/ip firewall mangle add action=change-mss chain=forward comment=MSS_ADAPTIVE connection-state=new new-mss=1100 out-interface=lte1 protocol=tcp tcp-flags=syn tcp-mss=501-65535
Скрипт "script_changeMSS_on_Network_Quality"
# Запускать каждые 5 сек! ########################## :local runPeriodSec 5 :local dataComment "DATA_TRAFFIC" :local errorComment "TCP_ERRORS" :local mssComment "MSS_ADAPTIVE" #################################### ### ВЫСТАВИТЬ ВЕРСИЮ СКРИПТА!!! #### #################################### :global gMSSVersion "1.82" #################################### #################################### :global gLastDataPacketsMangle :global gLastErrPacketsMangle :global gNoErrCycles :global gMSSnotGoDownCycles :global gSumData :global gMSSchangedWhileCheckingServices :global gIsCheckingServices :local mssMax 1360 :local mssMin 800 :local mssGood (($mssMax + $mssMin) / 2) :local mssStepUpSlow 10 :local mssStepUpFast 50 :local mssStepUp $mssStepUpSlow :local mssStepDownFast 300 :local mssStepDownMiddle 150 :local mssStepDownSlow 100 :local mssStepDown $mssStepDownMiddle :local rateThreshold 3 :local noErrCyclesForMssUp (50 / $runPeriodSec); # Сколько циклов тишины ждем (50 сек) :local minCyclesBetweenMssDown (30 / $runPeriodSec); :local dataPacketsIgnore (3 * $runPeriodSec) :local errPacketsIgnore 2 :local currData [/ip firewall mangle get [find comment=$dataComment] packets] :local currErr [/ip firewall mangle get [find comment=$errorComment] packets] :if ([:len $gLastDataPacketsMangle] = 0) do={ :set gLastDataPacketsMangle $currData; :set gLastErrPacketsMangle $currErr; :set gNoErrCycles 0; :set gSumData 0; :set gMSSnotGoDownCycles 0; } :if ([:len $gIsCheckingServices] = 0) do={ :set gIsCheckingServices false; } :local dataPackets ($currData - $gLastDataPacketsMangle) :local errPackets ($currErr - $gLastErrPacketsMangle) :set gLastDataPacketsMangle $currData :set gLastErrPacketsMangle $currErr :local mssID [/ip firewall mangle find comment=$mssComment] # ЕСЛИ Правило Mangle задано (есть чем управлять) и есть данные с предыдушего запуска :if ([:len $mssID] > 0 && $dataPackets > 0) do={ :local currentMSS [/ip firewall mangle get $mssID new-mss] :if ([:len $currentMSS] = 0 || [:typeof [:tonum $currentMSS]] = "nil") do={ :set currentMSS $mssGood } :local nextMSS $currentMSS :if ($currentMSS < $mssGood) do={ :set mssStepUp $mssStepUpFast } else={ :set mssStepUp $mssStepUpSlow } :local rawRate 0 :if ($dataPackets > 0) do={ :set rawRate (($errPackets * 10000) / $dataPackets) } :local intRate ($rawRate / 100) :local fracRate ($rawRate % 100) :local padding "" :if ($fracRate < 10) do={ :set padding "0" } :if ($errPackets > 0) do={ :set gNoErrCycles 0; :set gSumData $dataPackets; } else={ :set gNoErrCycles ($gNoErrCycles + 1); :set gSumData ($gSumData + $dataPackets); } # Увеличиваем счетчик здесь, чтобы не зависить от объема трафика ниже. Если будет сброс ниже, то этот счетчик все равно обнулится :set gMSSnotGoDownCycles ($gMSSnotGoDownCycles + 1) # Формируем строку NoErrCycles для лога (потому что потом gNoErrCycles может изменится, а мы хотим в логе видеть то что было) :local noErrCyclesLogString $gNoErrCycles :if ($dataPackets >= $dataPacketsIgnore) do={ # ЛОГИКА СБРОСА (Снижение) - превышен порог ошибок как в % так и в шт., есть куда опускаться, не опускались в ближайших предыдущих циклах :if ($intRate >= $rateThreshold && $errPackets > $errPacketsIgnore && $currentMSS > $mssMin && $gMSSnotGoDownCycles >= $minCyclesBetweenMssDown) do={ :if ($intRate > 30) do={ :set mssStepDown $mssStepDownFast } else={ :if ($intRate > 10) do={ :set mssStepDown $mssStepDownMiddle } else={ :set mssStepDown $mssStepDownSlow } } :set nextMSS ($currentMSS - $mssStepDown) :if ($nextMSS < $mssMin) do={ :set nextMSS $mssMin } :set gMSSnotGoDownCycles 0; } else={ # ЛОГИКА ПОДЪЕМА - есть куда подниматься, нет ошибок в течении нескольких последних циклов, не выполняются проверки сервисов :if ($currentMSS < $mssMax && $gNoErrCycles >= $noErrCyclesForMssUp && $gIsCheckingServices = false) do={ :set nextMSS ($currentMSS + $mssStepUp) :if ($nextMSS > $mssMax) do={ :set nextMSS $mssMax } :set gNoErrCycles 0; } } } # Формируем строку MSS для лога И МЕНЯЕМ MSS! :local mssLogString $currentMSS :if ($nextMSS != $currentMSS) do={ /ip firewall mangle set $mssID new-mss=$nextMSS :set mssLogString "$currentMSS -> $nextMSS" :if ($gIsCheckingServices = true) do={ :set gMSSchangedWhileCheckingServices true; } } # ЛОГИРОВАНИЕ С РАЗДЕЛЕНИЕМ УРОВНЕЙ (только если есть ошибочные пакеты или изменили MSS) :if ($errPackets > 0 || $nextMSS != $currentMSS) do={ :local logMsg "MSS_ADAPT: Traffic: $gSumData, Errors: $errPackets, Rate: $intRate.$padding$fracRate%, MSS: $mssLogString, NoErrCycles: $noErrCyclesLogString" :if ($nextMSS < $currentMSS || ($intRate >= $rateThreshold && $errPackets > $errPacketsIgnor)) do={ # Если стало хуже (снижаем MSS) - пишем Warning :log warning $logMsg } else={ # Если стало лучше (повышаем) или сохраняем стабильный MSS - пишем Info :log info $logMsg } # Если напечатали трафик, сбрасываем чтобы в следующий раз считать заново :set gSumData 0 } }
Ставим на график запуска:
/system scheduler add interval=5s name=job_changeMSS_on_Network_Quality on-event=script_changeMSS_on_Network_Quality policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon start-time=startup
Через несколько секунд/минут в логе должны пойти примерно такие записи:
2026-01-23 09:03:59 script,info MSS_ADAPT: Traffic: 151, Errors: 0, Rate: 0.00%, MSS: 910 -> 915, NoErrCycles: 4 2026-01-23 09:04:39 script,info MSS_ADAPT: Traffic: 255, Errors: 0, Rate: 0.00%, MSS: 915 -> 920, NoErrCycles: 4 2026-01-23 09:05:19 script,info MSS_ADAPT: Traffic: 429, Errors: 0, Rate: 0.00%, MSS: 920 -> 925, NoErrCycles: 4 2026-01-23 09:05:59 script,info MSS_ADAPT: Traffic: 432, Errors: 0, Rate: 0.00%, MSS: 925 -> 930, NoErrCycles: 4 2026-01-23 09:06:19 script,info MSS_ADAPT: Traffic: 63, Errors: 1, Rate: 1.58%, MSS: 800, NoErrCycles: 0 2026-01-23 09:07:09 script,warning MSS_ADAPT: Traffic: 18, Errors: 4, Rate: 22.22%, MSS: 800, NoErrCycles: 0 2026-01-23 09:07:59 script,info MSS_ADAPT: Traffic: 82, Errors: 0, Rate: 0.00%, MSS: 800 -> 805, NoErrCycles: 5 2026-01-23 09:08:09 script,info MSS_ADAPT: Traffic: 90, Errors: 1, Rate: 1.11%, MSS: 805, NoErrCycles: 0
Автоматизация - мониторинг доступности сервисов и жесткие действия
Создаем бота в телеге который будет нам слать ошибки
Создаем скрипт мониторинга доступности сервисов, тестирования на экстремально низком MSS и перезагрузки LTE-модема. А также будем слать уведомления в Telegram при ошибках. В лог конечно тоже все запишем.
Ставим на график раз в 3 минуты
Скрипт "script_Check_Limited_Internet"
:global gIsCheckingServices true; :global gMSSchangedWhileCheckingServices; :if ([:len $gMSSchangedWhileCheckingServices] = 0) do={ :set gMSSchangedWhileCheckingServices false; } :local mssComment "MSS_ADAPTIVE" :local isDPIbock false :local androidCheckHttps "ER"; :local windowsCheckHttp "ER"; :local miCheckHttps "ER"; :local forPDACheckHttps "ER"; #################################### ### ВЫСТАВИТЬ ВЕРСИЮ СКРИПТА!!! #### #################################### :global gServicesVersion "4.64" #################################### ########################################################## # ЗАМЕНИТЬ МНОГОТОЧИЯ НА ID СВОЕГО БОТА и ЧАТА В ТЕЛЕГЕ ## ########################################################## :local sBotId "........" :local sChatId "......." ########################################################## :local sDiagnoseMessages "" :local logGlobalPfx "CHECK_INET:" # Считывем текущее значение MSS :local mssID [/ip firewall mangle find comment=$mssComment] :local initialMSS "" :if ([:len $mssID] > 0) do={ :set initialMSS [/ip firewall mangle get $mssID new-mss] } # Объявляем функцию сброса MSS :local dropMSS do={ :local newMSSonError 500 :global gNoErrCycles 0; :global gSumData 0; :if ([:len $3] > 0) do={ :global gPrevMSS [/ip firewall mangle get $3 new-mss] :if ($gPrevMSS > $newMSSonError) do={ /ip firewall mangle set $3 new-mss=$newMSSonError :log warning "$1 $2 failure detected. Try dropping MSS: $gPrevMSS -> $newMSSonError, NoErrCycles: 0" :delay 2s } } } # Объявляем функцию восстановления MSS :local restoreMSS do={ :if ([:len $3] > 0) do={ :local curMSS [/ip firewall mangle get $3 new-mss] :global gPrevMSS; :if ($curMSS != $gPrevMSS) do={ /ip firewall mangle set $3 new-mss=$gPrevMSS :log warning "$1 $2 DPI block detected, restoring MSS: $gPrevMSS" :delay 2s } } } # 1. ЗАЩИТА: Не трогаем модем первые 5 минут после старта роутера :local lteStatus [/interface lte monitor lte1 once as-value]; :local lteUp ($lteStatus->"session-uptime"); :if ([:len $lteUp] = 0) do={ :set lteUp ($lteStatus->"uptime") }; # Если аптайм пустой (модем инициализируется) или меньше 5 минут :if ([:len $lteUp] = 0 or $lteUp < 00:05:00) do={ :log warning "$logGlobalPfx LTE Guard: Modem is warming up (current: $lteUp). Skipping check."; :error "Warmup mode"; } # 1.1 Проверка Android Style HTTPS (max 9 sec) :do { :set $gMSSchangedWhileCheckingServices false /tool fetch url="https://connectivitycheck.gstatic.com/generate_204" check-certificate=no keep-result=no idle-timeout=3s; :set androidCheckHttps "ok"; } on-error={ :log warning "$logGlobalPfx Android Check HTTPS: FAILED (https://connectivitycheck.gstatic.com/generate_204)" # МГНОВЕННЫЙ СБРОС MSS ДЛЯ БЫСТРОГО ВОССТАНОВЛЕНИЯ [$dropMSS $logGlobalPfx "Android" $mssID]; :do { /tool fetch url="https://www.google.com/generate_204" check-certificate=no keep-result=no idle-timeout=3s; :set androidCheckHttps "ok"; } on-error={ :log warning "$logGlobalPfx Android Check HTTPS: FAILED (https://www.google.com/generate_204)" :do { /tool fetch url="https://play.google.com/generate_204" check-certificate=no keep-result=no idle-timeout=3s; :set androidCheckHttps "ok"; } on-error={ :log warning "$logGlobalPfx Android Check HTTPS: FAILED (https://play.google.com/generate_204)" # Если после сброса все проверки отвалились, то восстанавливаем MSS (имеем дело с DPI блокировкой или полным отсутствием интернета или попали на опускание MSS другим скриптом) [$restoreMSS $logGlobalPfx "Android" $mssID]; #если при этом попали на опускание mss другим скриптом, то не доверяем результату - ставим skipped if ($gMSSchangedWhileCheckingServices = true) do={ :set androidCheckHttps "skipped" :log info "$logGlobalPfx Android Check HTTPS: skipped due to MSS change while checking" } else={ :set isDPIbock true; } }; }; }; # 1.2 Проверка Microsoft Style (max 15 sec) # если все проверки Android провалились, не тратим время на MS - ставим skipped, нужно быстрее перезагружать модем! :if ($androidCheckHttps = "ER" ) do={ :set windowsCheckHttp "skipped" :log info "$logGlobalPfx Microsoft Check HTTP: skipped due to Android HTTPS fail" } else={ :set $gMSSchangedWhileCheckingServices false :local isMSSdroped false :for i from=1 to=3 do={ :if ($windowsCheckHttp != "ok") do={ :do { /tool fetch url="http://www.msftconnecttest.com/connecttest.txt" keep-result=no idle-timeout=3s; :set windowsCheckHttp "ok"; } on-error={ :log warning "$logGlobalPfx Microsoft Check HTTP: FAILED, i=$i" # МГНОВЕННЫЙ СБРОС MSS ДЛЯ БЫСТРОГО ВОССТАНОВЛЕНИЯ if ($isMSSdroped = false) do={ [$dropMSS $logGlobalPfx "Microsoft" $mssID]; :set isMSSdroped true; } } } } # Если после сброса все проверки отвалились, то восстанавливаем MSS (имеем дело с DPI блокировкой или полным отсутствием интернета или попали на опускание MSS другим скриптом) if ($windowsCheckHttp != "ok") do={ [$restoreMSS $logGlobalPfx "Microsoft" $mssID]; #��сли при этом попали на опускание mss другим скриптом, то не доверяем результату - ставим skipped if ($gMSSchangedWhileCheckingServices = true) do={ :set windowsCheckHttp "skipped" :log info "$logGlobalPfx Microsoft Check HTTP: skipped due to MSS change while checking" } else={ :set isDPIbock true; } } } # 1.3 Проверка Xiaomi (max 21 sec) # если все проверки Android провалились или проверка Microsoft провалилась, не тратим время на Xiaomi - ставим skipped :if ($windowsCheckHttp = "skipped" or $windowsCheckHttp = "ER") do={ :set miCheckHttps "skipped" :log info "$logGlobalPfx Xiaomi Check HTTPS: skipped due to Android/Microsoft HTTPS/HTTP fail or DPI block" } else={ :set $gMSSchangedWhileCheckingServices false :local isMSSdroped false :for i from=1 to=2 do={ :if ($miCheckHttps != "ok") do={ :do { /tool fetch url="https://region.hlth.io.mi.com/abroad_download?redir=11817" check-certificate=no keep-result=no idle-timeout=5s http-max-redirect-count=5; :set miCheckHttps "ok"; } on-error={ :log warning "$logGlobalPfx Xiaomi Check HTTPS: FAILED, i=$i" # МГНОВЕННЫЙ СБРОС MSS ДЛЯ БЫСТРОГО ВОССТАНОВЛЕНИЯ if ($isMSSdroped = false) do={ [$dropMSS $logGlobalPfx "Xiaomi" $mssID]; :set isMSSdroped true; } } } } # Если после сброса все проверки отвалились, то восстанавливаем MSS (имеем дело с DPI блокировкой или полным отсутствием интернета или попали на опускание MSS другим скриптом) if ($miCheckHttps != "ok") do={ [$restoreMSS $logGlobalPfx "Xiaomi" $mssID]; #если при этом попали на опускание mss другим скриптом, то не доверяем результату - ставим skipped if ($gMSSchangedWhileCheckingServices = true) do={ :set miCheckHttps "skipped" :log info "$logGlobalPfx Xiaomi Check HTTPS: skipped due to MSS change while checking" } else={ :set isDPIbock true; } } }; # 1.4 Проверка forPDA (max 21 sec) # если все проверки Android провалились или проверка Microsoft провалилась не тратим время на forPDA - ставим skipped :if ($windowsCheckHttp = "skipped" or $windowsCheckHttp = "ER" or $isDPIbock) do={ :set forPDACheckHttps "skipped" :log info "$logGlobalPfx forPDA Check HTTPS: skipped due to Android/Microsoft HTTPS/HTTP fail or DPI block" } else={ :set $gMSSchangedWhileCheckingServices false :local isMSSdroped false :for i from=1 to=2 do={ :if ($forPDACheckHttps != "ok") do={ :do { /tool fetch url="https://4pda.to/forum/index.php?showtopic=1065946&st=2240#entry141234147" check-certificate=no keep-result=no idle-timeout=5s http-max-redirect-count=5; :set forPDACheckHttps "ok"; } on-error={ :log warning "$logGlobalPfx forPDA Check HTTPS: FAILED, i=$i" # МГНОВЕННЫЙ СБРОС MSS ДЛЯ БЫСТРОГО ВОССТАНОВЛЕНИЯ if ($isMSSdroped = false) do={ [$dropMSS $logGlobalPfx "4pda" $mssID]; :set isMSSdroped true; } } } } # Если после сброса все проверки отвалились, то восстанавливаем MSS (имеем дело с DPI блокировкой или полным отсутствием интернета или попали на опускание MSS другим скриптом) if ($forPDACheckHttps != "ok") do={ [$restoreMSS $logGlobalPfx "4pda" $mssID]; #если при этом попали на опускание mss другим скриптом, то не доверяем результату - ставим skipped if ($gMSSchangedWhileCheckingServices = true) do={ :set forPDACheckHttps "skipped" :log info "$logGlobalPfx 4pda Check HTTPS: skipped due to MSS change while checking" } else={ :set isDPIbock true; } } }; :local finalMSS "" :local MSSmsg "" :if ([:len $mssID] > 0) do={ :set finalMSS [/ip firewall mangle get $mssID new-mss] :if ($finalMSS != $initialMSS) do={ :set MSSmsg "$initialMSS -> $finalMSS" } else={ :set MSSmsg $initialMSS } } # Получаем внешний IP: Сначала Yandex API, если нет - Ipify :global gLastPublicIP :local currentPublicIP "unknown" :do { :do { # Попытка через Yandex API :local result [/tool fetch url="https://yandex.ru/internet/api/v0/ip" mode=https output=user as-value duration=3s] # Проверяем наличие данных перед обработкой :if ([:len ($result->"data")] > 0) do={ :local rawData ($result->"data") :set currentPublicIP [:pick $rawData ([:find $rawData "\""] + 1) [:find $rawData "\"" ([:find $rawData "\""] + 1)]] } else={ :error "fail" } } on-error={ # Если Яндекс не ответил или данные пустые, пробуем Ipify :local result [/tool fetch url="https://api.ipify.org" mode=https output=user as-value duration=3s] :if ([:len ($result->"data")] > 0) do={ :set currentPublicIP ($result->"data") } else={ :error "fail" } } } on-error={ # Сюда попадем, если оба запроса не вернули данные или упали по таймауту :set currentPublicIP "timeout" } # Очистка от возможных невидимых символов (переносы строк) :if ($currentPublicIP != "timeout") do={ :if ([:find $currentPublicIP "\n"] > 0) do={ :set currentPublicIP [:pick $currentPublicIP 0 [:find $currentPublicIP "\n"]] } :if ([:find $currentPublicIP "\r"] > 0) do={ :set currentPublicIP [:pick $currentPublicIP 0 [:find $currentPublicIP "\r"]] } } # Логика уведомления о смене IP :local ipMsg "" :if ([:len $currentPublicIP] > 0 && [:len $gLastPublicIP] > 0 && $currentPublicIP != $gLastPublicIP && $currentPublicIP != "timeout") do={ :set ipMsg "$gLastPublicIP -> $currentPublicIP" } else={ :set ipMsg $currentPublicIP } :if ([:len $currentPublicIP] > 0 && $currentPublicIP != "timeout") do={ :set gLastPublicIP $currentPublicIP } :local sStatus ("As: " . $androidCheckHttps . ", MS: " . $windowsCheckHttp . ", XiaoMIs: " . $miCheckHttps . ", 4pda: " . $forPDACheckHttps . ", MSS: " . $MSSmsg . ", IP: " . $ipMsg) :if ($isDPIbock = true) do={ :set sStatus ($sStatus . ", DPI BLOCK!!!") } :set gIsCheckingServices false; :set gMSSchangedWhileCheckingServices false; # 2. Логика перезапуска и формирование сообщения для телеграмм: если не прошел AndroidHttpS или WindowsHttp :if ($androidCheckHttps = "ER" or $windowsCheckHttp = "ER" or $isDPIbock = true) do={ :log error "$logGlobalPfx INTERNET LIMITED!!! ($sStatus). Restarting LTE..."; :set sStatus ("FAILED! " . $sStatus . ". Restarting LTE...") :if ([:len $sDiagnoseMessages] > 0) do={:set sStatus ($sStatus . "\r\n" . $sDiagnoseMessages)} /interface lte disable lte1; :delay 10s; /interface lte enable lte1; # Пауза 30 секунд, чтобы модем успел подключиться, прежде чем слать уведомление :delay 30s; } else={ :if ($androidCheckHttps = "ok" and $windowsCheckHttp = "ok" and $miCheckHttps = "ok" and $forPDACheckHttps = "ok") do={ :log info "$logGlobalPfx FULL ACCESS (All OK: $sStatus)"; :set sStatus ("success. " . $sStatus) } else= { :log warning "$logGlobalPfx CORE SERVICES ACCESS (Warning: $sStatus)"; :set sStatus ("warning. " . $sStatus) } :if ([:len $sDiagnoseMessages] > 0) do={:set sStatus ($sStatus . "\r\n" . $sDiagnoseMessages)} } # 3. Отправка сообщения в телеграмм :if ($androidCheckHttps = "ER" or $windowsCheckHttp = "ER" or $miCheckHttps = "ER" or $forPDACheckHttps = "ER") do={ :do { /tool fetch http-method=post \ url="https://api.telegram.org/bot$sBotId/sendMessage" \ http-data="chat_id=$sChatId&text=$sStatus" \ keep-result=no } on-error= { :log error "$logGlobalPfx Error sending statis message in Telegram , No Internet!" } }
ВАЖНО!
Впишите id бота и чата телеграм в переменные в начале скрипта
Проверьте имя своего LTE интерфейса в микротике. У меня lte1, если у вас другое, поменяйте в скрипте
Данная версия скрипта делаем мягкую перезагрузку модема при недоступности базовых сервисов (Android или Microsoft) или наличии DPI блокировки. Вы можете изменить это условие - добавить другой критичный вам сервис или отключить перезагрузку при DPI блокировке (она снимается перезагрузкой LTE модема, если он получает новый ip адрес). Ищите к в коде раздел "#2. Логика перезапуска ". Также можете поиграться с вкл/выкл режим полета через AT команды вместо мягкой перезагрузки, это должно работать быстрее чем перезагрузка и меньше вредить абонентам подключенным к роутеру.
Для проверки наличия DPI блокировки я обваливаю MSS в пол (500) и делаю повторную проверку. Если она проходит, оставляю MSS=500, если нет возвращаю MSS обратно и ставлю флаг DPI блокировка. Возможно для вас даже краткосрочное падение MSS до 500 будет катастрофично (для каких то других сервисов), тогда скорректируйте уровень пола под себя. Это настраивается в функции dropMSS.
Ставим скрипт на график
/system scheduler add interval=3m name=job_Check_Limited_Internet on-event=script_Check_Limited_Internet policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon start-time=startup
Через несколько минут в логе пойдут сообщения, например таки��:
2026-01-25 19:35:09 script,warning CHECK_INET: Xiaomi Check HTTPS: FAILED, i=1 2026-01-25 19:35:10 script,warning CHECK_INET: Xiaomi failure detected. Try dropping MSS: 1000 -> 500, NoErrCycles: 0 2026-01-25 19:35:43 script,warning CHECK_INET: Xiaomi Check HTTPS: FAILED, i=2 2026-01-25 19:35:43 script,warning CHECK_INET: Xiaomi DPI block detected, restoring MSS: 1000 2026-01-25 19:35:45 script,info CHECK_INET: forPDA Check HTTPS: skipped due to Android/Microsoft HTTPS/HTTP fail or DPI block 2026-01-25 19:35:46 script,error CHECK_INET: INTERNET LIMITED!!! (As: ok, MS: ok, XiaoMIs: ER, 4pda: skipped, MSS: 1000, IP: 31.173.86.163, DPI BLOCK!!!). Restarting LTE... 2026-01-25 19:38:55 script,info CHECK_INET: FULL ACCESS (All OK: As: ok, MS: ok, XiaoMIs: ok, 4pda: ok, MSS: 1050, IP: 31.173.86.163 -> 31.173.81.232) 2026-01-25 19:40:55 script,info CHECK_INET: FULL ACCESS (All OK: As: ok, MS: ok, XiaoMIs: ok, 4pda: ok, MSS: 1110, IP: 31.173.81.232)
В случае ошибок - будут сыпаться сообщения в телегу!
Расчет и накопление статистики для анализа
Самом сложным в всем этом управлении качеством был поиск стратегии балансировки MSS чтобы уменьшить кол-во DPI блокировок, недоступности сервисов и перезагрузки LTE модема.
Насколько быстро опускать, как поднимать MSS, насколько быстро и жестко реагировать про появлении ошибок TCP или ошибок в сервисах.
Я хотел добиться поведения приближенного к android смартфону. Ясно что он управляет не только MSS, и в первую очередь у него есть другие инструменты которые не применимы здесь. Но уподобиться идеалу хотелось. Меня всегда возмущало что при появлении сообщения DPI block в моем скрипте тот же сайт в ту же секунду открывался через мобильный интернет того же самого оператора на смартфоне.
Я думаю я не достиг идеала в поиске стратегии, но для того чтобы хотя бы попытаться мне нужна была статистика для сравнения разных версий стратегий (разных версий скриптов).
Я решил запилить третий скрипт, который читает логи, рассчитывает метрики качества и отправляет их в телегу и (самое вкусное!) в excel-таблицу для последующего анализа!
Для записи в excel-таблицу мы будем использовать google -форму с сохранением результатов в таблицу. Я хотел по православному писать в яндекс-форму, с сохранением в таблицу, но когда сделал столкнулся с тем что из скрипта в нее отправить не получается - ругается 302 предлагает заполнить капчу)). Пришлось на делать на гуглоформе.
Создаем бота в телеге, куда будем получать отчеты качества
Создаем google-форму и включаем сохранение результатов в таблицу
Создаем скрипт расчета статистики качества и ее отправки по 2 каналам
Ставим на график запуска 1 раз в час
Создаем гуглоформу с такими полями:
Кого мониторим | Дата | Час | Доступность сервисов, % | Доступность 4pda, % | Доступность Android, % | Доступность Microsoft, % | Доступность xiaomi, % | RTO_AVG 4pda, мин | RTO_AVG ANDROID, мин | RTO_AVG MICROSOFT, мин | RTO_AVG XIAOMI, мин | RTO_MIN 4PDA, мин | RTO_MIN ANDROID, мин | RTO_MIN MICROSOFT, мин | RTO_MIN XIAOMI, мин | RTO_MAX 4PDA, мин | RTO_MAX ANDROID, мин | RTO_MAX MICROSOFT, мин | RTO_MAX XIAOMI, мин | DPI_blocks, шт | DPI_blocks, % | MSS_AVG | MSS_MIN | MSS_MAX | MSS_STABILITY, % | MSS_CHANGES | Версия скриптов подстройки |
Включаем сохранение результатов в таблицу: Ответы --> Установить связь с Таблицами
Выгружаем id формы и id ее полей для скрипта: Три точки справа вверху --> Форма с предварительным заполнением. Заполняем поля формы краткими названиями полей по-английски (иначе потом не разберетесь где какое поле) и нажимаем Получить ссылку. Копируем ее в блокнот и в ней будут все id.
Скрипт "script_Analyze_InternetStability_Logs"
:local periodInHours 1 :local sendTelegram true ########################################################## # ЗАМЕНИТЬ МНОГОТОЧИЯ НА ID СВОЕГО БОТА и ЧАТА В ТЕЛЕГЕ ## # Если у вас несколько устройств - для разных укажите ## # разный sMonitoringSource ## ########################################################## :local sBotId "........" :local sChatId "......." :local sMonitoringSource "me" :local googleFormId "........" ########################################################## :global gMSSVersion; :if ([:len $gMSSVersion] = 0) do={ :set gMSSVersion 0; } :global gServicesVersion; :if ([:len $gServicesVersion] = 0) do={ :set gServicesVersion 0; } :local period [:totime ($periodInHours . "h")] :local hMinMss 2000; :local hMaxMss 0; :local hSumMss 0; :local hCountMss 0; :local hStabChanges 0 :local totalDPI 0; :local stabThreshold (60 * 2 * $periodInHours) :local svcNames {"ANDROID";"MICROSOFT";"XIAOMI";"4PDA"} :local stats [:toarray ""] # --- УНИВЕРСАЛЬНЫЙ БЛОК ДАТЫ --- :local cDate [/system clock get date] :local cTime [/system clock get time] :local fDate "" :local gDate "" :if ([:pick $cDate 4] = "-" || [:pick $cDate 7] = "-") do={ # Формат ISO (YYYY-MM-DD) :set fDate ([:pick $cDate 8 10] . "." . [:pick $cDate 5 7] . "." . [:pick $cDate 0 4]) :set gDate $cDate } :local startTime ($cTime - $period) :if ($startTime < 0s) do { :set startTime (00:00:00) } :local timeRange ($fDate . " " . [:pick $startTime 0 5] . " - " . [:pick $cTime 0 5]) :foreach s in=$svcNames do={ :set ($stats->$s) {"err"=0; "ok"=0; "recSum"=0; "recCnt"=0; "maxRec"=0; "minRec"=999; "lastFail"=""} } :local logEntries [/log find where ((([:timestamp] + ([/system clock get gmt-offset] . "s")) - [:totime (time)]) <= $period)] :foreach i in=$logEntries do={ :local item [/log get $i] :local msg ($item->"message") :local lTime ($item->"time") :if ($msg ~ "MSS_ADAPT" || $msg ~ "MSS_STAT") do={ :local p [:find $msg "MSS: "] :if ([:len $p] > 0) do={ :local pStart ($p + 5) :local arrow [:find $msg "->" $p] :if ([:len $arrow] > 0) do={ :set pStart ($arrow + 3); :set hStabChanges ($hStabChanges + 1) } :local pEnd [:find $msg "," $pStart] :if ([:len $pEnd] = 0) do={ :set pEnd [:find $msg " " $pStart] } :if ([:len $pEnd] = 0) do={ :set pEnd [:len $msg] } :local mVal [:tonum [:pick $msg $pStart $pEnd]] :if ($mVal > 0) do={ :set hSumMss ($hSumMss + $mVal); :set hCountMss ($hCountMss + 1) :if ($mVal < $hMinMss) do={ :set hMinMss $mVal }; :if ($mVal > $hMaxMss) do={ :set hMaxMss $mVal } } } } :if ($msg ~ "DPI BLOCK!!!" || $msg ~ "BLOCKED") do={ :set totalDPI ($totalDPI + 1) } :if ($msg ~ "As:" || $msg ~ "MS:" || $msg ~ "XiaoMIs:" || $msg ~ "4pda:") do={ :foreach sName in=$svcNames do={ :local found false; :local sErr false; :local sOk false :if ($sName = "ANDROID" && $msg ~ "As:") do={ :set found true; if ($msg ~ "As: ok") do={:set sOk true} else={:if ($msg ~ "As: ER") do={:set sErr true} } } :if ($sName = "MICROSOFT" && $msg ~ "MS:") do={ :set found true; if ($msg ~ "MS: ok") do={:set sOk true} else={:if ($msg ~ "MS: ER") do={:set sErr true} } } :if ($sName = "XIAOMI" && $msg ~ "XiaoMIs:") do={ :set found true; if ($msg ~ "XiaoMIs: ok") do={:set sOk true} else={:if ($msg ~ "XiaoMIs: ER") do={:set sErr true} } } :if ($sName = "4PDA" && $msg ~ "4pda:") do={ :set found true; if ($msg ~ "4pda: ok") do={:set sOk true} else={:if ($msg ~ "4pda: ER") do={:set sErr true} } } :if ($found) do={ :if ($sErr) do={ :set ($stats->$sName->"err") (($stats->$sName->"err") + 1) :if ([:len ($stats->$sName->"lastFail")] = 0) do={ :set ($stats->$sName->"lastFail") $lTime } } :if ($sOk) do={ :set ($stats->$sName->"ok") (($stats->$sName->"ok") + 1) :local fTime ($stats->$sName->"lastFail") :if ([:len $fTime] > 0) do={ :local diff ($lTime - $fTime) :local dur (([:tonum [:pick $diff 0 2]] * 60) + [:tonum [:pick $diff 3 5]]) :set ($stats->$sName->"recSum") (($stats->$sName->"recSum") + $dur) :set ($stats->$sName->"recCnt") (($stats->$sName->"recCnt") + 1) :if ($dur > ($stats->$sName->"maxRec")) do={ :set ($stats->$sName->"maxRec") $dur } :if ($dur < ($stats->$sName->"minRec")) do={ :set ($stats->$sName->"minRec") $dur } :set ($stats->$sName->"lastFail") "" } } } } } } :local avgMss 0; :if ($hCountMss > 0) do={ :set avgMss ($hSumMss / $hCountMss) } :local stabPct (100 - (($hStabChanges * 100) / $stabThreshold)); :if ($stabPct < 0) do={ :set stabPct 0 } :local dpiPct (($totalDPI * 100) / (30 * $periodInHours) ); :local weightSum 0 :foreach sn,sd in=$stats do={ :local eC ($sd->"err"); :local oC ($sd->"ok"); :local av 100 :if (($eC + $oC) > 0) do={ :set av (($oC * 100) / ($eC + $oC)) } :local w 1; :if ($sn = "ANDROID" || $sn = "MICROSOFT") do={ :set w 2 } :set weightSum ($weightSum + ($av * $w)) } :local integralAvail ($weightSum / 6) :local out "\r\n=== ОТЧЕТ КАЧЕСТВА ===\r\n" :set out ($out . "$timeRange ($periodInHours ч.)\r\n") :set out ($out . "1. доступность сервисов $integralAvail%\r\n") :foreach sName,sData in=$stats do={ :local eC ($sData->"err"); :local oC ($sData->"ok"); :local av 100 :local total ($eC + $oC) :if ($total > 0) do={ :set av (($oC * 100) / $total) } :local rSum ($sData->"recSum"); :local rCnt ($sData->"recCnt"); :local rAvg 0 :if ($rCnt > 0) do={ :set rAvg ($rSum / $rCnt) } :local rMin ($sData->"minRec"); :if ($rMin = 999) do={ :set rMin 0 } :local rMax ($sData->"maxRec") :local sRTO ""; :if ($rAvg > 0) do={ :set sRTO ", RTO ~$rAvg мин " :if ($rMin != $rMax) do={ :set sRTO "$sRTO ($rMin..$rMax мин)" } } :set out ($out . " - $sName: $av%$sRTO\r\n") } :set out ($out . "2. блокировки DPI: $totalDPI шт. ($dpiPct%)\r\n") :set out ($out . "3. MSS: ~$avgMss ($hMinMss..$hMaxMss), стабильность: $stabPct% ($hStabChanges изм.)\r\n") :log info $out # Отправка в Telegram :if ($sendTelegram) do={ :do { /tool fetch http-method=post \ url="https://api.telegram.org/bot$sBotId/sendMessage" \ http-data="chat_id=$sChatId&text=$out" \ keep-result=no } on-error= { :log error "Error sending statis message in Telegram , No Internet!" } } # --- ОТПРАВКА В GOOGLE ФОРМУ --- :local currentHour [:tonum [:pick $cTime 0 2]] :local aAv 100; :local mAv 100; :local xAv 100; :local pAv 100 :local aSum (($stats->"ANDROID"->"ok") + ($stats->"ANDROID"->"err")) :local mSum (($stats->"MICROSOFT"->"ok")+ ($stats->"MICROSOFT"->"err")) :local xSum (($stats->"XIAOMI"->"ok") + ($stats->"XIAOMI"->"err")) :local pSum (($stats->"4PDA"->"ok") + ($stats->"4PDA"->"err")) :if ($aSum > 0) do={ :set aAv (($stats->"ANDROID"->"ok" * 100) / $aSum) } :if ($mSum > 0) do={ :set mAv (($stats->"MICROSOFT"->"ok" * 100) / $mSum) } :if ($xSum > 0) do={ :set xAv (($stats->"XIAOMI"->"ok" * 100) / $xSum) } :if ($pSum > 0) do={ :set pAv (($stats->"4PDA"->"ok" * 100) / $pSum) } :local aRa 0; :if (($stats->"ANDROID"->"recCnt") > 0) do={ :set aRa (($stats->"ANDROID"->"recSum") / $stats->"ANDROID"->"recCnt") } :local mRa 0; :if (($stats->"MICROSOFT"->"recCnt") > 0) do={ :set mRa (($stats->"MICROSOFT"->"recSum") / $stats->"MICROSOFT"->"recCnt") } :local xRa 0; :if (($stats->"XIAOMI"->"recCnt") > 0) do={ :set xRa (($stats->"XIAOMI"->"recSum") / $stats->"XIAOMI"->"recCnt") } :local pRa 0; :if (($stats->"4PDA"->"recCnt") > 0) do={ :set pRa (($stats->"4PDA"->"recSum") / $stats->"4PDA"->"recCnt") } :local aMi ($stats->"ANDROID"->"minRec"); :if ($aMi = 999) do={:set aMi 0}; :local aMa ($stats->"ANDROID"->"maxRec") :local mMi ($stats->"MICROSOFT"->"minRec"); :if ($mMi = 999) do={:set mMi 0}; :local mMa ($stats->"MICROSOFT"->"maxRec") :local xMi ($stats->"XIAOMI"->"minRec"); :if ($xMi = 999) do={:set xMi 0}; :local xMa ($stats->"XIAOMI"->"maxRec") :local pMi ($stats->"4PDA"->"minRec"); :if ($pMi = 999) do={:set pMi 0}; :local pMa ($stats->"4PDA"->"maxRec") :local urlData ("entry.263257593=" . $sMonitoringSource . \ "&entry.922317815=MSS v$gMSSVersion, Services v$gServicesVersion" . \ "&entry.1972153670=" . $gDate . \ "&entry.1309071647=" . $currentHour . \ "&entry.1232031999=" . $integralAvail . \ "&entry.1037735613=" . $pAv . \ "&entry.310815387=" . $aAv . \ "&entry.235660758=" . $mAv . \ "&entry.1138524409=" . $xAv . \ "&entry.1825690509=" . $pRa . \ "&entry.472580822=" . $aRa . \ "&entry.1981616555=" . $mRa . \ "&entry.1297338224=" . $xRa . \ "&entry.1749190856=" . $pMi . \ "&entry.939473659=" . $aMi . \ "&entry.355961275=" . $mMi . \ "&entry.1374920035=" . $xMi . \ "&entry.2101070622=" . $pMa . \ "&entry.1109708284=" . $aMa . \ "&entry.1109226438=" . $mMa . \ "&entry.1999941440=" . $xMa . \ "&entry.1938952119=" . $totalDPI . \ "&entry.1999467399=" . $dpiPct . \ "&entry.1882390038=" . $avgMss . \ "&entry.631664603=" . $hMinMss . \ "&entry.1412379302=" . $hMaxMss . \ "&entry.1772357571=" . $stabPct . \ "&entry.1644530981=" . $hStabChanges) :do { /tool fetch http-method=post \ url="https://docs.google.com/forms/d/e/$googleFormId/formResponse" \ http-data=$urlData \ check-certificate=no \ keep-result=no } on-error={ :log error "GoogleForm: Send failed." }
ВАЖНО!
Впишите id бота и чата телеграм в переменные в начале скрипта
Впишите id google формы в начале скрипта
Замените id полей гугл формы (entry.*) в скрипте на ваши. Как их узнать см. выше
Ставим скрипт на график:
/system scheduler add interval=1h name=job_script_Analyze_InternetStability_Logs on-event=script_Analyze_InternetStability_Logs policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon start-date=2026-01-24 start-time=17:00:00
Через час в телегу начнут приходить такие уведомления:

А в google таблице должны накапливаться такие записи:

Поиск идеала
Проверяем что все логи пишутся, уведомления приходят, а записи падают в гугл таблицу. Затем даем поработать несколько дней или лучше неделю - накапливаем статистику.
После этого можно изменить параметры скриптов, например скорость падения MSS при ошибках, порог %ошибки для падения, скорость роста MSS при отсутствии ошибок, а также изменить реакцию на недоступность сервисов. Не забудьте в скриптах поменять версию в глобальной переменной, чтобы в excel-файле со статистой потом можно было разобраться к какой версии относятся те или иные записи.
Даем системе еще поработать с новыми параметрами несколько дней или неделю.
Затем открываем файл в старом добром excel, строим сводную отчетность, графики, короче проводим анализ всеми известными способами. Цель - сравнить разные версии скриптов доступность базовых сервисов, %DPI блокировок, стабильность MSS (частые дерганья MSS не идут на пользу). Выбираем версию которая показала себя лучше. Еще подкручиваем параметры у нее и повторяем все по кругу. И так можно до бесконечности. Работать также хорошо как на смартфоне android все равно не будет. Но увеличить доступность сервисов с базового уровня можно очень существенно!
Кстати ради интереса можете установить постоянную MSS и посмотреть, что будет. Для этого параметры max и min MSS можно установить в одно и тоже значение (например 1100 при плохом сигнале), такое же значение прописать в функции dropMSS.Накопить статистику также за неделю и сравнить. Не забывайте при любых изменениях менять версию скрипта в глобальной переменной в начале скрипта.
Выводы
Данная работа была посвящена изучению возможностей роутеров микротик в деле управления качеством мобильного интернета с плохим качеством LTE сигнала, т.е. в удаленных уголках. И да, качество можно поднять антенной, но оно все равно не будет таким как по проводному интернету.
Дополнительно, по ходу дела мы научились пулять сообщения в телегу из микротика, парсить логи микротика, а также отправлять статистику работы в гугл таблицу для накопления и последующего удаленного анализа.