#!/usr/bin/env python3 """tcp_mtu_probe.py — бинарный поиск MTU-порога по TCP к шлюзу Nubes. Критерий: время до ответа nginx. Тело дошло → мгновенный ответ (<2с). Сегмент дропнут (MTU/PMTUD) → ответ не приходит (nginx ждёт тело) → считаем "не прошло" по таймауту FAST_TIMEOUT. Порог ищем делением пополам между LO и HI, потом уточняем повторными пробами вокруг порога. """ import socket import ssl import time HOST = "185.247.187.151" PORT = 443 # реальный путь клиентов; 32391 (NodePort) снаружи закрыт LO = 512 # заведомо проходит HI = 20000 # заведомо не проходит (14+ сегментов по MSS) FAST_TIMEOUT = 20 # быстрее полного зависания ~51с FAST_OK = 2.0 # ответ быстрее этого = прошло ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE def probe(n): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(FAST_TIMEOUT) t0 = time.time() try: s.connect((HOST, PORT)) mss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG) ss = ctx.wrap_socket(s, server_hostname="sqs.containerk8s.dev.nubes.ru") body = b"x" * n req = (b"POST / HTTP/1.1\r\n" b"Host: sqs.containerk8s.dev.nubes.ru\r\n" b"Content-Type: application/x-www-form-urlencoded\r\n" b"Content-Length: " + str(n).encode() + b"\r\n" b"Connection: close\r\n\r\n" + body) ss.sendall(req) data = ss.recv(4096) dt = time.time() - t0 first = data.split(b"\r\n")[0][:40].decode("latin1", "replace") return dt, mss, first except Exception as e: return time.time() - t0, None, "%s" % type(e).__name__ finally: try: s.close() except Exception: pass def passed(n): dt, mss, resp = probe(n) ok = dt < FAST_OK print(" size=%6d: %.2fs mss=%s %s -> %s" % (n, dt, mss if mss else "-", resp, "OK" if ok else "DROP"), flush=True) return ok def main(): # санпроверка границ print("check LO=%d:" % LO) if not passed(LO): print("LO не проходит — границы неверны, выход") return print("check HI=%d:" % HI) if passed(HI): print("HI тоже проходит мгновенно — порога нет в диапазоне, выход") return lo, hi = LO, HI while hi - lo > 50: mid = (lo + hi) // 2 if passed(mid): lo = mid else: hi = mid print("\nпорог найден: lo=%d hi=%d (проходит <=lo, дроп >=hi)" % (lo, hi)) # уточнение вокруг порога: 3 пробы на каждую из 4 точек рядом print("\nуточнение (по 3 пробы):") for n in (lo - 200, lo, (lo + hi) // 2, hi + 200): res = sum(1 for _ in range(3) if passed(max(n, 1))) print(" size=%d: %d/3 прошли" % (n, res)) if __name__ == "__main__": main()