Files
SQS-service/tests/tcp_mtu_probe.py
T

91 lines
3.3 KiB
Python

#!/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()