Один телефон, несколько узких мест: как раздать все 450 Мбит/с 5G

От 100-мегабитного порта роутера до ECMP: что ограничивало раздачу и как каналы сложились.
Дома интернет приходит только через телефон: на нём 5G давал около 450 Мбит/с, а компьютер через роутер получал 96–98. Я хотел забрать на компьютер всю доступную скорость и проверил по отдельности USB-модем, роутер и прямое подключение к точке доступа телефона. Затем объединил эти пути: в speedtest загрузка дошла до 456 Мбит/с. Ниже — замеры, ограничения каждого подключения и то, как схема работает, когда телефон уносят от компьютера.
С чего всё началось
Интернет дома только мобильный: 5G на телефоне Motorola moto g54, который я ношу с собой по дому. Работаю в виртуальной машине на Proxmox.
Сначала точку доступа телефона принимал недорогой USB Wi‑Fi адаптер, подключённый к VM. Антенна у него встроенная, приём слабый: стоило отойти с телефоном метров на пять, и связь обрывалась. Тогда я подключил к серверу кабелем роутер Tenda AC6 в режиме Wi‑Fi‑клиента (WISP). Он принимает интернет с телефона по Wi‑Fi и отдаёт серверу по кабелю. У роутера внешние антенны, и он держит телефон даже дальше 10 метров, выдавая там около 30 Мбит/с.
Но и когда телефон лежал рядом, скорость не поднималась выше 96–98 Мбит/с, хотя сам телефон показывал ~450. Сотовая сеть, выходит, не виновата: скорость теряется где-то между телефоном и компьютером. Этот участок я и разобрал по звеньям.
Стенд и каналы
Узел | Что это | Важное |
|---|---|---|
Телефон | Motorola moto g54 5G, Android 15 | USB 2.0; режим USB-модема только RNDIS; точка доступа 5 ГГц только на каналах 149–161 |
Роутер | Tenda AC6, режим WISP | один радиомодуль 5 ГГц; порты 10/100 Мбит/с |
Wi‑Fi адаптер | TP-Link Archer T2U Nano | 802.11ac, встроенная антенна, схема 1×1 |
Компьютер | VM на Proxmox (KVM, 12 vCPU) | USB-устройства проброшены через эмулированный контроллер xHCI |
Хост | сервер на чипсете Intel C620 | два USB-контроллера: чипсета и отдельный ASMedia ASM2142 |
От телефона к компьютеру можно провести три канала:
USB кабель: телефон в режиме USB-модема;
LAN: телефон → Wi‑Fi → роутер → кабель → компьютер;
Wi‑Fi адаптер: подключён к точке доступа телефона напрямую.
Как мерил. Скорость каждого канала по отдельности показывает замер curl: по 4 параллельные загрузки на канал по 8 секунд, с привязкой к интерфейсу, а скорость считается по счётчику байт на каждом интерфейсе. Итоговые цифры даёт speedtest на компьютере: он открывает десятки соединений к нескольким серверам и лучше показывает, сколько дают все каналы вместе. Сотовая сеть плавает, поэтому разброс повторных замеров около ±10 %.
Узкое место №1: роутер
Порты 10/100. По кабелю больше ~98 Мбит/с не пройдёт. Это и был потолок исходной схемы.
Один радиомодуль 5 ГГц. В режиме WISP он и принимает интернет от телефона, и раздаёт его клиентам, поэтому вынужден работать на канале телефона. Канал 36 из настроек в этом режиме игнорируется. Каждый пакет для Wi‑Fi‑клиента роутера проходит эфир дважды: Wi‑Fi адаптер через роутер получал 81–88 Мбит/с, а напрямую к телефону 137–149. Beamforming ничего не дал.
Процессор роутера ни при чём. Под нагрузкой он был занят на 1–48 %. Паспортные 867 Мбит/с — это скорость радио в одну сторону для клиента с двумя антеннами, когда сам роутер подключён к интернету кабелем.
Значит, нужен путь мимо роутера.
Узкое место №2: USB-модем в VM и одно ядро
Подключил телефон к VM по USB кабелю в режиме модема и получил 156 Мбит/с. При этом одно ядро VM было загружено на 92 %, а остальные 11 простаивали.
Причина в протоколе RNDIS, который Android использует для USB-модема: каждый сетевой пакет — отдельная USB-передача. За тест на 118 087 пакетов пришлось 132 420 прерываний USB-контроллера. В VM каждая такая передача проходит через эмулированный контроллер xHCI, и всю эту работу делает одно ядро.
Что не помогло:
RPS (раскидать обработку пакетов по ядрам): 169 вместо 156, в пределах погрешности. Дорогая часть — эмуляция USB, а она происходит ещё до сетевого стека.
NCM вместо RNDIS. NCM упаковывает много пакетов в одну передачу, но на
tether_force_usb_functionsпрошивка ответилаCould not set unsupported function for the GadgetHal. Без root никак. Попутные грабли: когда я включил отладку по USB, телефон сменил свой USB ID. Proxmox пробрасывал его в VM по ID, и телефон пропал из VM вместе с интернетом. После этого я перевёл проброс на привязку по порту.Второй эмулированный контроллер (EHCI), чтобы развести USB кабель и Wi‑Fi адаптер по разным ядрам. Адаптер я переключил на ходу через монитор QEMU (
device_del/device_add), без перезагрузки VM:
Конфигурация | USB кабель | Wi‑Fi адаптер | Сумма, Мбит/с |
|---|---|---|---|
Оба через xHCI | 122 | 95 | 218 |
USB кабель через xHCI, адаптер через EHCI | 141 | 8 | 150 |
Нагрузка разошлась по ядрам, но адаптер через EHCI почти перестал работать: 6–8 Мбит/с вместо ~140. Эмуляция EHCI в QEMU рассчитана на клавиатуры и мыши, а не на поток данных.
К этому моменту я перебрал 14 конфигураций трёх каналов и их сочетаний. Лучше всех оказалась USB кабель + LAN с балансировкой: 236 Мбит/с, в 2,4 раза больше исходного. Но одно ядро VM так и оставалось потолком, и средствами эмуляции его не убрать. Нужен настоящий контроллер.
Настоящий контроллер и датчик температуры
Сначала я проверил выигрыш временно, ничего не меняя в настройках VM. Скрипт на хосте отцеплял от хоста контроллер чипсета вместе с датчиком температуры (почему вместе, расскажу ниже) и подключал контроллер к работающей VM через монитор QEMU. Через 6 минут скрипт сам всё возвращал, а если бы подключение не удалось, откатил бы сразу. В VM заранее ждал наблюдатель, который при появлении контроллера сам прогнал тесты:
Замер curl | Эмуляция | Настоящий контроллер |
|---|---|---|
USB кабель | 156 (ядро 92 %) | 260 (ядро 18 %) |
USB кабель + Wi‑Fi адаптер | 218 | 348 |
USB кабель + LAN + Wi‑Fi адаптер | 246 | 368 |
Узкое место ушло. Но насовсем отдавать контроллер чипсета нельзя: он сидит в одной группе IOMMU с датчиком температуры чипсета (Intel PCH Thermal). VFIO отдаёт устройство в VM, только если вся его группа отвязана от хоста. Хост перестал бы видеть температуру чипсета: ни графиков, ни оповещений о перегреве. Вдобавок в VM ушли бы все порты чипсета. Менять наблюдаемость круглосуточного сервера на скорость интернета в одной VM — плохая сделка.
Выручил второй контроллер на плате, ASMedia ASM2142 (USB 3.1). Он сидит в своей группе IOMMU один, а два его порта на задней панели, Type‑A и Type‑C, были свободны. Его я отдал VM насовсем и переставил телефон в Type‑A. Wi‑Fi адаптер, клавиатура и мышь остались на портах чипсета.
Замер curl | Эмуляция | ASMedia |
|---|---|---|
USB кабель | 156 | 237 |
USB кабель + LAN | 229–236 | 347 |
USB кабель + LAN + Wi‑Fi адаптер | 246 | 445 |
Грабли на этом шаге:
Номер слота PCI. В VM уже была проброшена видеокарта в слоте
hostpci0, и первая команда добавила контроллер в тот же слот, то есть заменила видеокарту. Новое устройство нужно ставить в следующий свободный слот, сначала проверив занятые:qm config <VMID> | grep hostpci.Нужен именно порт ASMedia. Портов на задней панели много, и большинство принадлежит чипсету. К какому контроллеру подключён телефон, видно на хосте по пути устройства в
/sys/bus/usb/devices.
Узкое место №3: сам USB 2.0
На ASMedia ядро VM почти свободно, а USB кабель всё равно остановился на ~260–280. Чтобы исключить влияние Wi‑Fi‑раздачи, я выключил на телефоне точку доступа и померил один USB кабель:
Потоков | USB кабель, Мбит/с | Ядро VM |
|---|---|---|
4 | 263 | ~23 % |
8 | 275 | ~28 % |
16 | 279 | ~27 % |
Столько же. Это потолок USB 2.0 + RNDIS: на бумаге 480, реально ~300, и RNDIS теряет ещё на отдельной передаче для каждого пакета. USB 3 у телефона нет, NCM прошивка не поддерживает. Один кабель больше ~300 не даст.
Узкое место №4: Wi‑Fi‑раздача телефона
Wi‑Fi адаптер напрямую к телефону получал 137–149 Мбит/с при канальной скорости 390, процессор телефона был занят на ~4 %. Роутер и адаптер, подключённые одновременно, вместе получали ~145–160, почти столько же. Например, без USB кабеля вышло 144: LAN 94 + адаптер 50. Все клиенты делят эфир одной точки доступа по очереди.
Сам Wi‑Fi‑модуль телефона способен на чуть большее: адаптер, переставленный для проверки на ASMedia, получил 190. Но в рабочей схеме этот порт занят телефоном. Поднять потолок настройками нельзя: телефон раздаёт только в одном диапазоне за раз (2,4 или 5 ГГц), а на 5 ГГц ему доступен лишь один блок 80 МГц, каналы 149–161.
Складываем потолки
Канал | Потолок, Мбит/с | Во что упирается |
|---|---|---|
USB кабель | ~280–300 | USB 2.0 + RNDIS |
LAN | до ~98 | порты роутера 10/100 |
Wi‑Fi адаптер | ~50 (с LAN вместе ~145) | общая с роутером Wi‑Fi‑раздача телефона |
Сотовая сеть | ~450 |
Пока сумма потолков меньше сотовой сети, каналы складываются. Оператор этому не мешает: раздача идёт через ту же точку доступа оператора (APN), что и интернет самого телефона, отдельного «модемного» APN нет.
Распределяю я по соединениям: общий маршрут по умолчанию с несколькими направлениями (ECMP) и хешем по адресам и портам. Каждое соединение целиком идёт по одному каналу, а многопоточная загрузка — сразу по всем.
sysctl -w net.ipv4.fib_multipath_hash_policy=1 # хеш с учётом портов
sysctl -w net.ipv4.fib_multipath_use_neigh=1 # не слать на мёртвый шлюз
ip route replace default proto 99 metric 50 \
nexthop via <шлюз USB> dev <usb-интерфейс> weight 2 \
nexthop via <шлюз LAN> dev <lan-интерфейс> weight 1 \
nexthop via <шлюз Wi-Fi> dev <wifi-интерфейс> weight 1
Все каналы выходят в интернет через один телефон, поэтому внешний IP у всех соединений один, и сайты не видят «прыжков» между адресами.
Служба, чтобы работало без присмотра
Маршрут ставит и поддерживает небольшая служба на bash, multiwan-balance:
Ставит маршрут поверх маршрутов NetworkManager (metric 50) с весами: USB кабель 2, LAN и Wi‑Fi адаптер по 1.
Раз в 5 секунд проверяет интернет через каждый канал. После трёх неудач подряд канал исключается, после первой удачи возвращается. Пока по каналу идёт активный приём, он считается живым без пинга. Под нагрузкой пинг через телефон доходит до секунд (до внешнего узла — 2,9 с, максимум 6,8 с) и давал бы ложные срабатывания.
Если не работает ни один канал, маршрут службы снимается, и остаются штатные маршруты NetworkManager. При остановке службы происходит то же самое.
Возвращает Wi‑Fi адаптеру скорость после того, как телефон уносили. Когда телефон возвращается, адаптер переподключается ещё на краю зоны, на низкой скорости, и остаётся на ней, даже когда телефон уже рядом. Ручное переподключение сразу помогало. Служба делает это сама: если связь пропадала, а сигнал три проверки подряд не хуже −60 dBm, она один раз переподключает адаптер.
Роутер остаётся опорным каналом. Полная скорость получается, когда телефон лежит у компьютера на USB. Когда я ухожу с телефоном, USB кабель отключается, и интернет держится через роутер.
Ещё одна деталь: в NetworkManager я выключил IPv6 на USB кабеле и Wi‑Fi адаптере. Когда точка доступа выключена, телефон начинает раздавать по USB ещё и IPv6, и такой трафик шёл бы только по USB кабелю, мимо балансировки.
Результат
Схема | Speedtest, загрузка |
|---|---|
Роутер (исходно) | 96–98 |
USB кабель + LAN | 396 |
USB кабель + LAN + Wi‑Fi адаптер | 456 (отдача 48) |
Без Wi‑Fi адаптера speedtest упирается ровно в сумму двух потолков: ~300 + ~96 ≈ 396. Адаптер добирает остаток до потолка оператора. 456 — столько же, сколько показывал сам телефон (сеть колеблется примерно на ±10 %): выжато всё, что даёт сотовая сеть.
Что не складывается
Отдача. Каждый канал по отдельности отдаёт примерно одинаково: USB кабель 32, LAN 32, Wi‑Fi адаптер 29 Мбит/с. Все три вместе дают только 41. Значит, предел — отдача самой сотовой сети, ~30–57 Мбит/с, и она сильно плавает. С адаптером в распределении отдача даже немного ниже (в замере curl 44–47 против 55–57). Смена алгоритма TCP на BBR не помогла. Если отдача важнее, адаптер можно перевести в резерв (
WIFI_MODE=reserve): загрузка тогда ~396.Одиночная загрузка идёт по одному каналу: до ~300 Мбит/с по USB кабелю, ~98 по LAN, ~140 по Wi‑Fi адаптеру. При весах 2:1:1 она попадает на USB кабель в половине случаев. По той же причине 16 потоков к одному серверу дают ~380, а не 456: соединения ложатся на каналы неравномернее, чем десятки соединений speedtest.
Потеря роутера. Если роутер включён, но потерял связь с телефоном, служба замечает это за ~15–40 секунд. В это время часть новых соединений может зависать.
Порты ASMedia теперь принадлежат VM. Подключённые к ним устройства видны только в ней.
Итог: во что упирались
Узкое место | Потолок, Мбит/с | Как обошли |
|---|---|---|
Порты роутера 10/100 | ~98 | добавил каналы мимо роутера |
Роутер с одним радиомодулем в режиме WISP | ~88 через его Wi‑Fi | Wi‑Fi роутера для компьютера не используется, только LAN |
Эмуляция USB-контроллера в VM (всё на одном ядре) | ~156 | проброс отдельного контроллера ASMedia |
USB 2.0 + RNDIS в телефоне | ~280–300 | не обойти, добавлены другие каналы |
Wi‑Fi‑раздача телефона | ~145–160 на всех клиентов | используется двумя путями: роутер и Wi‑Fi адаптер |
Сотовая сеть оператора | ~450 (±10 %) | это и есть итоговый предел: через раздачу 456 |
Выводы
USB кабель — отдельный путь, а роутер и Wi‑Fi адаптер делили один эфир телефона. Поэтому скорость адаптера в одиночном тесте нельзя целиком прибавить к скорости LAN.
ECMP складывает скорость при множестве соединений. Каждое соединение целиком идёт по одному пути, так что одно соединение от добавления каналов быстрее не становится.
С отдачей вышло иначе: её ограничивала сотовая сеть. Участие Wi‑Fi адаптера немного снижало результат, поэтому при приоритете отдачи ему полезнее оставаться резервным каналом.
В VM эмуляция USB упёрлась в одно ядро раньше, чем телефон — в предел своего USB 2.0. Проброс настоящего контроллера снял это ограничение, но для постоянной работы подошёл контроллер, который не делил группу IOMMU с нужным хосту датчиком температуры.
Скрипт службы и установщик: https://github.com/byha78/phone-multiwan
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.