91 lines
3.3 KiB
Python
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()
|
|
|