Site Tools


software:article:utp_dpi

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revisionBoth sides next revision
software:article:utp_dpi [2015/05/27 23:28]
– [Torrent/uTP — о протоколе и самодельных DPI] root
software:article:utp_dpi [2022/02/04 18:34]
– removed - external edit (Unknown date) 127.0.0.1
Line 1: Line 1:
-====== Torrent/uTP — о протоколе и самодельных DPI ====== 
-{{:ru:software:article:utorrent_logo.png|}} 
- 
-В 2009 году появился [[http://en.wikipedia.org/wiki/Micro_Transport_Protocol|Micro Transport Protocol]], сокращённо - uTP, можно ознакомится [[http://habrahabr.ru/post/68332/|тут]].\\ 
-Суть задумки в том, чтобы не полагаться на [[http://en.wikipedia.org/wiki/TCP_congestion-avoidance_algorithm|TCP Congestion Control]], которым под windows управлять весьма проблематично, а самим управлять загрузкой канала.\\ 
-Другая причина это возможность приёма входящего соединения через NAT.\\ 
-[[http://habrahabr.ru/post/85443/|uTP выявил много узких мест]] как у [[http://forum.nag.ru/forum/index.php?showtopic=55025|провайдеров]] так и у пользователей: ещё вчера прекрасно работавшие роутеры превратились в тыкву.\\ 
-А некоторые пользователи обнаружили что торренты качаются на все 100 мегабит, не зависимо от тарифа.\\ 
- 
-Вот какой бред написан в [[https://ru.wikipedia.org/wiki/ΜTorrent|русской википедии]] про uTP:\\ 
-"Также провайдерам намного сложнее блокировать передачу данных через μTP благодаря отсутствию строгих, формализованных отличий UDP пакетов обычного трафика (формируемого, к примеру, сетевыми играми) от трафика, формируемого протоколом μTP, в отличие от TCP пакетов, по содержанию полей которых можно делать вывод об их принадлежности к p2p-трафику."\\ 
-[[http://en.wikipedia.org/wiki/Micro_Transport_Protocol|Вики на английском]] более адекватна.\\ 
- 
-===== Жизнь с uTP ===== 
-В адрес авторов uTP звучала масса упрёков в изобретении TCP с нуля и хождении по всем граблям, в том что они не взяли уже готовые протоколы, и в том что теперь придётся обновляться и расширятся.\\ 
-С точки зрения разработчиков - выбора особо не было: TCP все провайдеры шейпят, для управления всеми аспектами работы tcp протокола в винде нужны права администратора и скорее всего свой драйвер, многие другие протоколы которые ходят поверх IP (tcp/udp/gre/udplite/...) вообще провайдерами фильтруются и в винде их так просто не реализовать.\\ 
-Через NAT нормально ходят только TCP и UDP.\\ 
-Потому просто взяли и сделали поверх UDP.\\ 
- 
-Это решение подкосило многие домашние мыльницы и некоторых провайдеров: количество трансляций в NAT роутеров стало очень быстро расти.\\ 
-Для TCP - NAT знает когда соединение установлено и когда оно завершено, а для UDP понятие соединений отсутствует в принципе, поэтому обычно применяются таймеры для удаления старых сессий.\\ 
- 
-Другим побочным эффектом явилось то, что uTorrent запрашивал больше трафика чем позволял тарифный план провайдера, и от этого страдали даже те провайдеры у которых шейпер был настроен правильно: на хомячка из интернета прилетало ощутимо больше его тарифного плана и этот излишек дропался шейпером. Провайдеры несли финансовые потери от такого DDoS хомяка на самого себя.\\ 
-Авторы uTorrent позже научились правильно подстраиваться под канал, но их эксперименты стоили нервов и денег.\\ 
- 
-Ещё одним неприятным моментом в экспериментах с uTP на начальных этапах было то, что он генерировал большую пакетную нагрузку, [[http://torrentblog.ru/protokol-utp-ne-sderzhal-svoix-obeshhanij/|отправляя множество мелких UDP пакетов]]. Позднее авторы научились заполнять пакеты с данными целиком.\\ 
-Повышение пакетрейта губительно сказывается на WiFi и прочих радиолинках.\\ 
- 
-В целом, протокол оказал ощутимое влияние как на провайдеров так и на производителей железа, я бы даже сказал что он подготовил почву для HD в ютубе.\\ 
- 
- 
-===== Протокол uTP ===== 
-[[http://www.bittorrent.org/beps/bep_0029.html|Документация на английском]]\\ 
- 
- 
-==== Версия 0 ==== 
-Начиная с uTorrent 1.8 
-<code>typedef struct utp_pkt_s { /* offset - PacketFormat */ 
- uint32_t connid; /* 00 connection ID */ 
- uint32_t tv_sec; /* 04  */ 
- uint32_t tv_usec; /* 08  */ 
- uint32_t reply_micro; /* 12  */ 
- uint8_t windowsize; /* 16 receive window size in PACKET_SIZE chunks */ 
- uint8_t ext; /* 17 Type of the first extension header */ 
- uint8_t flags; /* 18 Flags */ 
- uint16_t seq_nr; /* 19 Sequence number */ 
- uint16_t ack_nr; /* 21 Acknowledgment number */ 
- /* 23 ext/data */ 
-} utp_pkt_t, *utp_pkt_p; /* 23 bytes */</code> 
- 
- 
-==== Версия 1 ==== 
-Начиная с uTorrent 2.0 
-<code>typedef struct utp_pkt_v1_s { /* offset - PacketFormatV1 */ 
- uint8_t version:4; /* 00 protocol version */ 
- uint8_t type:4; /* 00 type (formerly flags) */ 
- uint8_t ext; /* 01 Type of the first extension header */ 
- uint16_t connid; /* 02 connection ID */ 
- uint32_t tv_usec; /* 04  */ 
- uint32_t reply_micro; /* 08  */ 
- uint32_t windowsize; /* 12 receive window size in bytes */ 
- uint16_t seq_nr; /* 16 Sequence number */ 
- uint16_t ack_nr; /* 18 Acknowledgment number */ 
- /* 20 ext/data */ 
-} utp_pkt_v1_t, *utp_pkt_v1_p; /* 20 bytes */</code> 
- 
- 
-==== Типы пакетов ==== 
-<code>enum { 
- ST_DATA = 0, /* Data packet. */ 
- ST_FIN = 1, /* Finalize the connection. This is the last packet. */ 
- ST_STATE = 2, /* State packet. Used to transmit an ACK with no data. */ 
- ST_RESET = 3, /* Terminate connection forcefully. */ 
- ST_SYN = 4, /* Connect SYN. */ 
- ST_NUM_STATES /* Used for bounds checking. */ 
-};</code> 
- 
-**flags** из версии 0 превратился в type в версии 1, типы пакетов перечислены выше.\\ 
-Сначала отправляется SYN на него приходит ответ STATE или RESET.\\ 
-Завершается соединение на FIN или RESET.\\ 
-DATA и STATE используются при передаче данных.\\ 
- 
-**connid** - идентификатор соединения. В TCP его роль выполняет номер порта (вернее их пара). Номер соединения у двух хостов всегда различается на единицу.\\ 
- 
-Вообще довольно запутанная и странная схема установления соединения:\\ 
- > SYN: connid=34 - запрос на установление соединения\\ 
- < STATE: connid=34 - подтверждение\\ 
- > DATA: connid=35 - передача данных\\ 
- < STATE: connid=34 - подтверждение передачи данных\\ 
-Те инициатор соединения задаёт номер соединения в первом пакете а в дальнейшем использует номер на единицу больше.\\ 
- 
-**seq_nr** и **ack_nr** - используется чтобы ориентироваться в потоке в случае потери или реордеринга (когда первый отправленный пакет приходит после второго).\\ 
- 
-Остальные поля меня интересовали мало, хотя для **ext** опций валидацию написал.\\ 
- 
-**ext** - если есть дополнительные расширения/данные в пакете после заголовка, аналог IP otions.\\ 
- 
-**tv_usec**, **reply_micro**, **windowsize** - относятся к информации необходимой для управления скоростью передачи.\\ 
- 
- 
-==== Шифрование ==== 
-Шифрования нет. Совсем.\\ 
-Не потому что описание не полное а потому что оно реализовано несколькими уровнями выше и uTP никак не касается.\\ 
-uTP заголовки никак не шифруются.\\ 
- 
- 
-===== Фильтрация / распознавание uTP ===== 
-=== Заход 1: uTPControl === 
-Из спортивного интереса я решил попробовать написать нечто что сможет аккуратно выключать uTP у юзеров чей трафик проходит через роутер с моей программой.\\ 
-В начале я пробовал слать RESET с виндовой машины, перебирая connid, но это явно не работало, не совпадали адреса отправителя пакета и seq_nr, ack_nr. Это был первый неудачный опыт.\\ 
- 
-[[ru:software:freebsd:utp_ctrl_daemon|uTPControl - block uTP torrent proto]] - была первая более менее программа которую я написал под FreeBSD.\\ 
-uTP протокол был любезно предоставлен в [[https://github.com/bittorrent/libutp|libuTP]] всему интернету :)\\ 
- 
-Работало uTPControl чрезвычайно просто: программа создавала divert сокет и бесконечно читала из него пакеты в цикле. Если это был UDP пакет, в котором ВОЗМОЖНО uTP версии 0 или 1 и тип пакета не FIN и не RESET то генерируем UDP-uTP RESET пакет и отправляем обратно.\\ 
-Те клиент пытался установить связь и сразу получал RESET - те его вроде как отключил тот к кому он подключался.\\ 
- 
-Минусов у этого решения было два:\\ 
-1. Ложные срабатывания: иногда пакеты от некоторых онлайн игр были прямо как uTP и им улетал ответ, видимо у игр от этого срывало крышу и юзеры жаловались.\\ 
- 
-2. Низкая производительность: все пакеты из ядра копировались в юзерспейс и там в один поток обрабатывались. 
-В один поток на CoreDuo Е5300 под FreeBSD 7.3 выдавало до 100 тысяч пакетов RESET в секунду.\\ 
-Один поток можно было обойти запустив несколько экземпляров и как то раскидав через ipfw пакеты между ними.\\ 
- 
-В виду этих фатальных недостатков интерес со стороны сообщества пропал и я её забросил.\\ 
- 
-Был человек который превратил это в netgraph ноду, но ложные срабатывания это не вылечило.\\ 
- 
- 
-=== Заход 2: ng_utp === 
-[[ru:software:freebsd:ng_utp|FreeBSD uTP (udp torrent) netgraph node]]\\ 
-Прошло полтора года, я успел покопаться в ядре FreeBSD и netgraph, лучше узнать как работает сеть и пришла мысль: uTP имеет состояния аналогичные TCP, значит чтобы его 100% определять нужно эти состояния отслеживать.\\ 
-Заодно я ещё раз заглянул в [[https://github.com/bittorrent/libutp|libuTP]] и получше посмотрел за что можно зацепится.\\ 
-За сигнатуры решил не цепляться, это плохой путь с массой ложных срабатываний и мучениями по их поддержанию - авторы уже несколько раз меняли начальные константы и сигнатуры "протухали" у тех кто их использовал.\\ 
-Идеальный вариант это свой "клиент" с референсной реализацией uTP который будет выстраивать таблицу соединений на основе пролетающих через него пакетов и уже по данным этой таблицы что то можно делать.\\ 
- 
-В итоге получилась netgraph нода, которую можно подключать к L2 хукам типа ng_ether или L3 хукам, например ng_ipfw. В первом случае можно вообще сделать прозрачный эзернет мост из двух сетевух (не обязательно физических). Ещё можно просто поставить сервер и зеркалировать на него весь траф, но я сейчас не уверен в работоспособности такой схемы.\\ 
- 
-Результатов замеров производительности не сохранил.\\ 
-Однако нода без проблем параллелится по ядрам, может выполнятся как контексте ISR так и потоками netgraph, взаимные блокировки потоков сведены к минимуму.\\ 
- 
-В случае L2: мультикаст и броадкаст пакеты пролетают сразу насквозь, тэгированный трафик обрабатывается как обычный. QinQ не делал, добавить не сложно.\\ 
-Обрабатываются только IPv4 пакеты UDP, не адресованные и не отправленные с 127/8, не являющиеся броадкастом или мультикастом, и ещё немного проверок что это не мусор а то что нужно, включая опциональную проверку контрольной суммы IP и UDP.\\ 
- 
-Далее проверяется что содержимое UDP пакета похоже на uTP.\\ 
-Если содержимое похоже то ищем в таблице состояний запись для данной пары хостов: src ip:port / dst ip:port, если не находим то меняем местами и ищем ещё раз. Не нашли и пакет не FIN или RESET - добавляем. (подозреваю что на линухе с conntrack было бы чуть проще, а тут пришлось самому писать)\\ 
-Теперь есть элемент который хранит все uTP соединения между двумя хостами или то что похоже на них. 
-Ищем там connid, если не нашли то connid+-1, если опять нет - добавляем.\\ 
- 
-Теперь у нас есть куда писать данные по конкретному uTP соединению.\\ 
-Пишем: время последнего обновления, время последней отправки запроса и получения ответа, считаем запросы и ответы, запоминаем какие типы пакетов встречались.\\ 
-seq_nr и ack_nr можно было бы тоже запоминать и анализировать но и без них достаточно точно получается определять.\\ 
-Старые записи удаляются автоматически.\\ 
- 
-Теперь мы точно знаем что хосты установили uTP соединения, знаем сколько каждый отправил пакетов и получил ответов, знаем их идентификаторы, можно действовать.\\ 
- 
-Действовать имеет смысл только для DATA и STATE пакетов.\\ 
- 
-Что можно сделать: 
-<ul><li>ничего, просто мониторинг: есть счётчики по хостам, соединениями, сами таблицы с хостами...</li> 
-<li>дропать пакеты с uTP: src ip:port + dst ip:port есть</li> 
-<li>помечать пакеты: на L2 в VLAN заголовке PCP для тегированных пакетов 802.1P</li> 
-<li>помечать пакеты: на L3 в IP  заголовке - ip_tos - DSCP</li> 
-<li>отправлять uTP - RST пакеты</li></ul> 
-Чтобы сгенерировать RST пакет все данные есть: src ip:port + dst ip:port, pkt_ver, connid, ack_nr, seq_nr. 
-Фактически у IP/UDP пакета заменяются данные, пересчитывается контрольная сумма и от отправляется дальше.\\ 
- 
-Сейчас это всё ещё может быть актуальным для различных беспроводных сетей и офисных сетей, остальные уже обновились и расширились.\\ 
- 
- 
-===== Сигнатуры ===== 
-Администраторы искали способ как быстро нормализовать работу сети и решили фильтровать uTP по сигнатурам пакетов, добавляя их то в ACL коммутаторов то в фаервол BSD/Linux роутера.\\ 
-"Странность" ситуации в том, что сигнатуры искали анализируя пакеты.\\ 
-Притом, что код [[https://github.com/bittorrent/libutp|libuTP]] был открыт 16 мая 2010 года - через 4 месяца после выхода uTorrent 2.0 где uTP был включён.\\ 
- 
-Спустя пару месяцев "живительные" сигнатуры путём нечеловеческих усилий по анализу пакетов были [[http://www.opennet.ru/tips/info/2304.shtml|получены]].\\ 
-Ещё через некоторое время авторы uTP поменяли пару незначительных для протокола начальных значений в SYN пакете и что то [[http://www.linux.org.ru/forum/admin/6357142|рандомизировали]]: connid и seq_nr - больше не смогли.\\ 
- 
-После того как ng_utp был написан стало понятно что проверять корректность работы с помощью tcpdump без правильных сигнатур мягко говоря не удобно - слишком много лишнего приходилось пробегать глазами.\\ 
-Я ещё раз пробежался по коду [[https://github.com/bittorrent/libutp|libuTP]] и получились такие сигнатуры, сейчас может быть они уже устарели.\\ 
- 
- 
-==== Версия 0 ==== 
-=== SYN === 
-syn - 14 bytes: 
-<code>udp[17] = 2 and udp[18] = 4 and udp[21:2] = 0 and udp[23] = 0 and udp[24] = 8 and udp[25:4] = 0 and udp[29:4] = 0</code> 
-41 = udp hdr len (8) + upd pkt data len\\ 
- 
-upd header included: 
-<code>(udp[4:2] = 41 and udp[25:2] = 0x0204 and udp[29:4] = 0x00000008 and udp[33:4] = 0 and udp[37:4] = 0)</code> 
-- последнее это то что можно скармливать в tcpdump, отличается от первой смещениями и тем что константы объединены чтобы сравнений было меньше. Первая больше для самообразования.\\ 
- 
- 
-=== RESET === 
-rst - 4 bytes: 
-<code>udp[17] = 0 and udp[18] = 3</code> 
-upd header included: 
-<code>(udp[4:2] = 31 and udp[25:2] = 0x0003)</code> 
- 
- 
-==== Версия 1 ==== 
-=== SYN === 
-syn - 14 bytes: 
-<code>udp[0] & 0x0f = 1 and udp[0] & 0xf0 = 0x40 and udp[1] = 2 and udp[18:2] = 0 and udp[20] = 0 and udp[21] = 8 and udp[22:4] = 0 and udp[26:4] = 0</code> 
-"(udp[0] & 0x0f = 1 and udp[0] & 0xf0 = 0x40)" можно преобразовать в: "udp[0] = 0x41"\\ 
-upd header included: 
-<code>(udp[4:2] = 38 and udp[8:2] = 0x4102 and udp[26:4] = 0x00000008 and udp[30:4] = 0 and udp[34:4] = 0)</code> 
- 
- 
-=== RESET === 
-rst - 4 bytes: 
-<code>udp[0] & 0x0f = 1 and udp[0] & 0xf0 = 0x30 and udp[1] = 0</code> 
-"udp[0] & 0x0f = 1 and udp[0] & 0xf0 = 0x30" можно преобразовать в: "udp[0] = 0x31"\\ 
-upd header included: 
-<code>(udp[4:2] = 28 and udp[8:2] = 0x3100)</code> 
- 
- 
-===== Обнаружение фильтрации ===== 
-Проще всего, используя описание протокола, реализовать простенький клиент, который будет устанавливать соединение и пытаться отправлять данные.\\ 
-По сути нужно симулировать установление соединения, и дальше пытаться слать DATA и STATE пакеты в ответ с ext типа ACK.\\ 
-Дальше один клиент запускается в интернете, другой у себя и смотрим теряются ли пакеты в 100% случаев или может RESET приходят.\\ 
-Сходным образом при использовании yota некоторые пакеты из l2tp на завершающем этапе согласования пропадают в 100% случаев. Так было ещё в сентябре 2014.\\ 
- 
- 
-===== Заключение ===== 
-1. uTP имеет достаточно чёткие сигнатуры и легко ловится DPI.\\ 
-Более того, ловить сигнатуры в TCP ощутимо сложнее, поскольку для гарантированного обнаружения нужно уметь собирать несколько пакетов вместе и уже потом проверять содержимое: клиент может передавать данные по одному байту.\\ 
-Авторы uTP либо не ставили себе цель сделать протокол без сигнатур либо даже не приблизись к цели.\\ 
-На мой взгляд в начале не ставили, а потом было уже поздно и рандомизация отдельных полей не помогает.\\ 
- 
-2. Производители различных DPI уже давно добавили сигнатуры для uTP, вряд ли им это было трудно сделать.\\ 
- 
-3. В порядке слухов: для линукса вроде бы тоже есть ядерная версия для работы с uTP протоколом на базе ipp2p а может уже отдельно. Но в паблик её не выкладывали. С середины 2012 года.\\ 
- 
-4. Для IPv6 код не писал, на всякий случай ;)\\ 
- 
-5. uTP не лучше TCP для передачи данных, вся проблема в том, что TCP можно хоть как то управлять из приложения только на BSD/Linux - setsockopt(..., IPPROTO_TCP, TCP_CONGESTION,...) - основное что требуется, хотя и там более тонкие параметры congestion control для отдельных сокетов не настраиваются. 
-Говорить про оверхэд в 23/20 байт сейчас уже не актуально, HTTP/2.0 не сильно лучше. 
-Возможно с приходом кучи готовых либ для HTTP/2.0 торренты пустят и через него, скорее всего это вопрос времени. 
  
software/article/utp_dpi.txt · Last modified: 2022/02/05 04:27 by root