Цели и задачи

Работающий интернет в частный дом в Московской области. Проводных аналогов нет.

Что имеем на руках

Роутер Mikrotik hap ac3 LTE. Но можно любой микротик + LTE модем - я настраивал сначала все именно так, а потом переносил на микротик LTE. Всю сложную логику будем реализовывать на микротике.

Прошивка RouterOS 7.20.7 long term.

Мобильный тариф с безлимитным интернетом. Тут развилка:

  1. Либо у нас тариф для роутера - тогда перед нами все дороги открыты. Можем использовать роутер микротик LTE - просто вставляем симку в него и настраиваем. Модем использовать микротик без LTE - тогда покупаем любой LTE модем с USB и втыкаем его в микротик. Главное чтобы модем не был прошит под IMEI смартфона, т.к. в смартфоне данная симкарта может не заработать (зависит от оператора)

  2. Либо у нас тариф для смартфона. Тогда симку нельзя вставлять в микротик 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 модеме.

Здесь мы видим 2 частоты: 2600 и 1800, проверять на LTE модеме будем именно их
Здесь мы видим 2 частоты: 2600 и 1800, проверять на LTE модеме будем именно их

Управление качеством

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

У решил сделать комплексную систему мониторинга, оповещения и управления качеством.

На самом деле вариантов управления у нас не так много:

  1. Уменьшать / балансировать MSS (размер пакета) при плохом качестве интернета

  2. Перезапускать 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, который пройдет по новым чистым путям до наших сервисов и они станут доступны.

Автоматизация - мониторинг качества интернета

  1. Создаем счетчики mangle для расчета качества (см. выше скрипт)

  2. Создаем правило mangle для управление параметром MSS

  3. Создаем скрипт для анализа счетчиков, расчета качества и динамическим управлением MSS. Дополнительно этот скрипт будет логировать метрики если %ошибок > 0 или если меняем MSS (в любую сторону) (скрипт будет ниже)

  4. Ставим на график запуска - каждые 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 

Автоматизация - мониторинг доступности сервисов и жесткие действия

  1. Создаем бота в телеге который будет нам слать ошибки

  2. Создаем скрипт мониторинга доступности сервисов, тестирования на экстремально низком MSS и перезагрузки LTE-модема. А также будем слать уведомления в Telegram при ошибках. В лог конечно тоже все запишем.

  3. Ставим на график раз в 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!"
	}
}

ВАЖНО!

  1. Впишите id бота и чата телеграм в переменные в начале скрипта

  2. Проверьте имя своего LTE интерфейса в микротике. У меня lte1, если у вас другое, поменяйте в скрипте

  3. Данная версия скрипта делаем мягкую перезагрузку модема при недоступности базовых сервисов (Android или Microsoft) или наличии DPI блокировки. Вы можете изменить это условие - добавить другой критичный вам сервис или отключить перезагрузку при DPI блокировке (она снимается перезагрузкой LTE модема, если он получает новый ip адрес). Ищите к в коде раздел "#2. Логика перезапуска ". Также можете поиграться с вкл/выкл режим полета через AT команды вместо мягкой перезагрузки, это должно работать быстрее чем перезагрузка и меньше вредить абонентам подключенным к роутеру.

  4. Для проверки наличия 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 предлагает заполнить капчу)). Пришлось на делать на гуглоформе.

  1. Создаем бота в телеге, куда будем получать отчеты качества

  2. Создаем google-форму и включаем сохранение результатов в таблицу

  3. Создаем скрипт расчета статистики качества и ее отправки по 2 каналам

  4. Ставим на график запуска 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." }

ВАЖНО!

  1. Впишите id бота и чата телеграм в переменные в начале скрипта

  2. Впишите id google формы в начале скрипта

  3. Замените 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 сигнала, т.е. в удаленных уголках. И да, качество можно поднять антенной, но оно все равно не будет таким как по проводному интернету.

Дополнительно, по ходу дела мы научились пулять сообщения в телегу из микротика, парсить логи микротика, а также отправлять статистику работы в гугл таблицу для накопления и последующего удаленного анализа.

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


  1. ALxMrk
    29.01.2026 08:44

    Отличное руководство по стабилизации мобильного интернета через LTE на микротике. Ты описал целую систему:

    — Адаптивное управление MSS в реальном времени (скрипт каждые 5 сек проверяет ошибки TCP и подкручивает размер пакета) — Мониторинг ключевых сервисов с автоматической реакцией (если упал 4pda или базовые чеки — сброс MSS или перезагрузка модема) — Сбор статистики и отправка в телегу + гугл-таблицы для анализа эффективности разных версий скриптов

    Главный плюс — подход не «включил и забыл», а с обратной связью: система сама подстраивается под качество канала, а ты видишь метрики и можешь оптимизировать параметры.

    Единственное замечание: перезагрузка модема каждые 3 минуты при проблемах — это жёстко. Можно попробовать сначала мягче: режим полёта через AT-команду (AT+CFUN=0 / AT+CFUN=1) — быстрее и меньше раздражает подключённых пользователей.

    А так — рабочая схема для тех, у кого в деревне только сотовый интернет. Респект за детализаци


  1. 0ka
    29.01.2026 08:44

    Каким образом изменение MSS влияет на "качество интернета"? Только в худшую сторону? Вы в курсе протокола QUIC и того что MSS нельзя менять для уже установленных соединений? Вы проверяли что ваше изменение MSS работает? (В wireshark должно быть видно измененный MSS на входящих пакетах, обычно для этого нужно два правила, у вас одно, не знаю нужно ли в микроте второе)

    Что ещё за за "DPI блокировка оператора"? Мне кажется тут не блокировка от оператора, а вы себе что-то сломали и скриптом только ухудшаете качество интернета


    1. occ2 Автор
      29.01.2026 08:44

      1. в курсе, он для новых соединений и работает

      2. проверял на входящих пакетах вручную, MSS прилетает измененный

      3. периодическая недоступность сервисов была до существования этих скриптов и послужила как раз основанием для данной работы и данных скриптов. По факту % недоступности в сутки стал существенно меньше, чем при статическом MTU и MSS. То что это DPI доказывает мгновенное восстановление доступности до сервисов после получения нового ip на LTE модеме


    1. occ2 Автор
      29.01.2026 08:44

      Fetch по любому каждый раз устанавливает новое соединение, т.ч. результаты автоматизированной проверки сервисов с новым mss точно будут верными.

      Но wifi клиенты могут использовать старые (текущие) сессии как вы верно заметили.

      Если это будет проблемой, то можно килять все открытые сессии в сторону того сервиса с которым скрипт выявил проблему, в случае если снижение mss помогло (если сработал fetch с пониженным mss, а на исходном повышенном выдавал ошибку):

      :local serviceIp "1.2.3.4"
      /ip firewall connection remove [find where dst-address~"$serviceIp"]