Потери 3% на входящий трафик TCP/UDP в РФ с некоторых хостеров для некоторых портов

В результате мониторинга и изучения проблем с рабочими VPN за последний 1.5 месяца стала примерно ясна суть еще одной проблемы.
Судя по всему вносятся потери во входящий трафик, весьма приблизительно по такой схеме: if (hash(src_ip, src_port, dst_ip, dst_port) % koef == 0) → drop packets with 3% probability.
При этом без разницы, что за трафик там идет - хоть даже Iperf3. То видно, что пока не меняются src_ip, dst_ip, dst_port, набор проблемных портов src_port не меняется в выборке из 100 портов (сейчас в такой выборке - около 5-6 проблемных портов).
Вероятность того что порт плохой (koef) - иногда больше, иногда меньше становится.
Отсюда следует, что при установке соединения - когда src_port выбирается рандомный, то с некоторой вероятностью мы получаем потерю пакетов и тормоза.
Наблюдается такое на TCP и на UDP, в связках (hetzner, leaseweb, other) ↔ (ростелеком, транстелеком).
Схема выглядит достаточно странно.
Находятся эти проблемные порты через Iperf, 3 секунды гоняем на порту, смотрим потери UDP или ретрансмиты TCP.

Вот к примеру график потерь пакетов из Гонконга на наборе из 1000 src портов от 1300 до 65000 с шагом 65:

А по вертикальной оси что отложено? Процент потерь?

Да, по вертикали процент потерь.

Интересно, значит пора начать повсеместно использовать UDPspeeder и udp2raw.
Если кто-то объединит функции этих программ с, например, TunSafe с автоматическим слежением качества связи дайте мне знать — мобильные процессоры не особо справляются с такой нагрузкой.

У меня есть такая проблема, но только в связке с одним провайдером на моей стороне (РФ) и одним зарубежным провайдером: не доходит значимая часть TCP SYN’ов, в обе стороны (ни я не могу установить соединение, ни они ко мне), на определённых портах.
Это обычный HTTP-вебсайт, не VPN.

Я пока не уверен, что это блокировки. Больше похоже либо на роутер с битой памятью где-то ближе к РФ (думал, что у моего провайдера, вы говорите, что разные каналы подвержены проблеме), либо как-то забавно настроенные фильтры BCP38.

Если я отправляю себе TCP SYN’ы от любого IP-адреса с определённой площадки (которая позволяет подменять IP-адрес источника), вижу 100% потерей при определённых портах.
Например, ни один TCP SYN-пакет из 10 от 8.8.8.8:65248 до меня:2233 не дошел, а от 8.8.8.8:55945 — никаких проблем, все дошли.


Вот еще аналогичный график потерь снятый с hetzner в сторону ростелеком. Не очень похоже на нормальный random / hash, либо используется плохая функция хэша.
Сегодня, после 3 ночи MSK, раскладка проблемных портов сменилась, хотя более суток была неизменной, с начала наблюдения.
Ранее также была зафиксирована зависимость потерь от размеров пакетов iperf (внутри vpn):
20 bytes - 0.37%
50 bytes - 0.5%
200 bytes - 0.76%
1350 bytes - 2.8%
А вообще эта байда длится уже не менее 1.5 месяцев.

А с помощью долбёжки с TTL можно ли выяснить проблемный маршрутизатор?

Наверное можно, но mtr под линукс не поддерживает --localport, а --port работает только в tcp режиме, судя по мануалу.

Рабочий исходящий порт:

% sudo tcptraceroute 94.100.24.67 80 -P 10016 -q 6
Selected device enp2s0, address 192.168.69.10, port 10016 for outgoing packets
Tracing the path to 94.100.24.67 on TCP port 80 (http), 30 hops max
 1  192.168.69.1  1.975 ms  0.749 ms  1.166 ms  2.423 ms  2.253 ms *
 2  95-161-156-121.obit.ru (95.161.156.121)  1.421 ms  2.675 ms  1.134 ms  4.958 ms  1.556 ms  1.889 ms
 3  172.29.194.72  1.304 ms  2.454 ms  18.128 ms  9.192 ms  16.138 ms  14.347 ms
 4  172.29.192.121  16.369 ms  12.064 ms  12.735 ms  9.513 ms  6.279 ms  3.141 ms
 5  172.29.194.77  1.159 ms  0.963 ms  5.978 ms  1.091 ms  0.903 ms  0.936 ms
 6  172.29.194.94  1.131 ms  2.890 ms  1.372 ms  1.941 ms  1.015 ms  1.064 ms
 7  172.29.255.197  2.567 ms  1.537 ms  1.179 ms  1.031 ms  1.103 ms  1.136 ms
 8  172.29.194.113  2.176 ms  1.925 ms  1.586 ms  1.224 ms  1.151 ms  1.352 ms
 9  172.29.194.37  5.130 ms  1.498 ms  0.911 ms  1.154 ms  1.973 ms  1.570 ms
10  vi-xx-0150.brc2.obit.ru (85.114.1.13)  1.974 ms  2.378 ms  3.236 ms  1.887 ms  1.976 ms  7.417 ms
11  * 109.239.136.155 15.394 ms * * * *
12  100ge0-42.core2.ams1.he.net (184.105.65.125)  33.066 ms * * 112.909 ms * *
13  100ge0-29.core1.ams7.he.net (184.105.213.197)  60.557 ms  58.424 ms  64.578 ms  67.975 ms  83.675 ms  35.377 ms
14  hivelocity-ventures-corp.e0-1.switch1.ams7.he.net (184.105.27.106)  34.104 ms  48.390 ms  57.888 ms  64.501 ms  84.650 ms  105.816 ms
15  * * * * * *
16  * * * * * *
17  * * * * * *
18  94-100-24-67.static.hvvc.us (94.100.24.67) [open]  34.051 ms  35.087 ms  34.208 ms  34.867 ms  35.691 ms  34.678 ms

Нерабочий исходящий порт:

% sudo tcptraceroute 94.100.24.67 80 -P 10009 -q 6
Selected device enp2s0, address 192.168.69.10, port 10009 for outgoing packets
Tracing the path to 94.100.24.67 on TCP port 80 (http), 30 hops max
 1  192.168.69.1  15.789 ms  2.220 ms  0.463 ms  0.513 ms  0.291 ms  0.852 ms
 2  95-161-156-121.obit.ru (95.161.156.121)  1.104 ms  1.795 ms  0.810 ms  2.062 ms  0.843 ms  0.860 ms
 3  172.29.194.72  1.588 ms  0.786 ms  0.857 ms  0.939 ms  0.791 ms  1.152 ms
 4  172.29.192.121  1.264 ms  4.696 ms  4.347 ms  8.515 ms  9.309 ms  5.530 ms
 5  172.29.194.77  1.583 ms  6.602 ms  0.909 ms  1.458 ms  2.957 ms  7.076 ms
 6  172.29.194.94  0.853 ms  1.583 ms  1.364 ms  2.193 ms  0.903 ms  0.938 ms
 7  172.29.255.197  2.833 ms  0.885 ms  1.057 ms  2.617 ms  1.297 ms  0.862 ms
 8  172.29.194.113  1.736 ms  0.908 ms  4.144 ms  5.369 ms  4.128 ms  1.041 ms
 9  172.29.194.37  1.459 ms  1.075 ms  2.203 ms  1.252 ms  1.298 ms  2.697 ms
10  vi-xx-0150.brc2.obit.ru (85.114.1.13)  1.359 ms  4.076 ms  3.059 ms  3.332 ms  1.875 ms  1.474 ms
11  * * * * * 109.239.136.155 70.226 ms
12  100ge0-42.core2.ams1.he.net (184.105.65.125)  39.163 ms * * 33.007 ms * 35.631 ms
13  100ge0-29.core1.ams7.he.net (184.105.213.197)  31.363 ms  33.058 ms  31.488 ms  32.445 ms  31.278 ms  31.369 ms
14  hivelocity-ventures-corp.e0-1.switch1.ams7.he.net (184.105.27.106)  35.329 ms  33.458 ms  33.483 ms  34.554 ms  39.352 ms  42.495 ms
15  * * * * * *
16  * * * * * *
17  * * * * * *
18  * * * * * *
19  * * * * * *
20  * * * * * *
21  * * * * * *
22  * * * * * *
23  * * * * * *
24  * * * * * *
25  * * * * * *
26  * * * * * *
27  * * * * * *

В трёх случах из трёх проблема появляется при транзите трафика через Мегафон Поволжье/Волга.
Последние хопы с трёх каналов: 83.169.204.90 и 85.26.205.119.

Не удается с помощью tcptraceroute что то толковое определить. Тут также то доходит, то не доходит, в зависимости от порта, но прямой связи с теми портами где потери по iperf - нет.

Но в ходе дальнейшего изучения также было выявлено что один хост, который сначала не давал потерь - вдруг стал давать небольшие потери на портах (0.2%), после нескольких тестов. Возможно спровоцировали это сканом.
А также в процессе скана iperf-ом, другой хост перешел из состояния малых потерь (0.2%) на некоторых портах в состояние больших потерь (3%), прямо посередине процесса скана.

Учитывая то, что iperf гонит случайный трафик, возможно мы идем по китайскому сценарию, когда в нераспознаваемый трафик вносятся потери с некоторой вероятностью.

График начался с малых потерь, а потом потери стали большими. Причем есть фаза когда есть и малые и большие одновременно. Конфиги на железо может распространяются так.

Серверы подконтрольны вам? Сделайте с них traceroute, вдруг там тоже Мегафон.

Да, подконтрольные. Но мегафона там никогда не было в трейсах, там либо ростелеком (в основном), либо трансттелеком (реже). По поводу наличия потерь на транстелекоме возможно я ошибся, проверил, там в трейсах почти везде ростелеком сейчас.

Возможно всё банально, и так проявляется криво работающий trunk.

Только вот проблема эта наблюдается с трафиком из разных частей света - гонконг, сша, европа, япония. Врядли везде транк криво работает.

У меня такое вышло :worried:, также проверял с 1300 до 65000 порта с шагом 65 по 3 секунды на UDP.
Использовал такой скрипт на питоне из под винды

import paramiko
import subprocess
import json

# Задайте здесь значения переменных
hostname = 'address'  # Имя или IP-адрес сервера
port = 22  # Порт для SSH
username = 'username'  # Имя пользователя
private_key_path = 'path-to-file'  # Путь к файлу с приватным ключом SSH
iperf3_duration = 3  # Длительность теста iperf3 в секундах
output_file = "data.txt"  # Файл для записи результатов
results_file = "results.txt"  # Файл для записи отфильтрованных результатов

# Создание SSH-соединения
private_key = paramiko.RSAKey(filename=private_key_path) 
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname, port=port, username=username, pkey=private_key)

for iperf3_port in range(1300, 65001, 65):  # Цикл по портам
    # Установка правил ufw и запуск iperf3 на сервере
    commands = [
        f'sudo ufw allow {iperf3_port}',
        f'iperf3 -s -p {iperf3_port} -D'
    ]
    for command in commands:
        stdin, stdout, stderr = client.exec_command(command)
        print(stdout.read().decode())
        print(stderr.read().decode())

    # Запуск iperf3 локально.  iperf3 должен быть в PATH
    subprocess.run(f'iperf3 -c {hostname} -p {iperf3_port} -u -t {iperf3_duration} -J > {output_file}', shell=True)

    # Чтение результатов из файла и запись нужных значений в другой файл
    with open(output_file, "r") as file:
        data = json.load(file)
        results = {
            "port": iperf3_port,
            "lost_percent": data["end"]["sum"]["lost_percent"]
        }
        with open(results_file, "a") as results_file_obj:  
            results_file_obj.write(json.dumps(results) + "\n")  # Запись результатов в новую строку файла

    # Остановка iperf3 на сервере и удаление правил ufw
    commands = [
        'pkill iperf3',
        f'sudo ufw delete allow {iperf3_port}',
    ]
    for command in commands:
        stdin, stdout, stderr = client.exec_command(command)
        print(stdout.read().decode())
        print(stderr.read().decode())

# Закрытие SSH-соединения
client.close()

Может я что-то не так проверяю :thinking:
А так живу за двумя натами, на дальнем востоке, проверял на серваке с нидерландов

За натом наверное будет проблематично задать source port, потому что NAT назначит свой.
Если же менять порт сервера, как в вашем случае, то source port все равно будет рандомным, и воспроизводимотси результатов не будет.

А так скрипт которым я проверял под линуксом, выглядит так: \

#!/usr/bin/bash

echo . > outfile.txt

for i in {20..1000} 
do 
  SPORT=$((i*65))
  echo $SPORT
  iperf3 -c X.X.X.X -u -b 10m -t 2 --bind src.white.ip.addr --cport $SPORT >> outfile.txt
done

А на стороне сервера - iperf3 -s
Далее вывод в outfile.txt анализировал grep-ом на предмет потерь.

Под винду там iperf3 потребует больше опций, иначе он работает просто неадекватно.
Для UDP корректно сработает такой вариант опций клиента iperf3: -u -b 10m -l 1350 -w 2m -t 15 (это при условии что сервер - на линуксе или freebsd).

Обязательно чтобы было -l 1350 -w 2m , иначе он просто меряет какую-то ерунду.

Для tcp iperf3 под винду обязательно чтобы было -w 2m, иначе результаты тоже будут неакдекватные.

P.S. я тоже мерял на Дальнем Востоке

виндовый iperf где брали? на iperf.fr старая версия, вот тут https://files.budman.pw/ самый последний файл ver3.13, вроде адекватно работает