This shows you the differences between two versions of the page.
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 ====== | ||
- | {{: | ||
- | |||
- | В 2009 году появился [[http:// | ||
- | Суть задумки в том, чтобы не полагаться на [[http:// | ||
- | Другая причина это возможность приёма входящего соединения через NAT.\\ | ||
- | [[http:// | ||
- | А некоторые пользователи обнаружили что торренты качаются на все 100 мегабит, | ||
- | |||
- | Вот какой бред написан в [[https:// | ||
- | " | ||
- | [[http:// | ||
- | |||
- | ===== Жизнь с uTP ===== | ||
- | В адрес авторов uTP звучала масса упрёков в изобретении TCP с нуля и хождении по всем граблям, | ||
- | С точки зрения разработчиков - выбора особо не было: TCP все провайдеры шейпят, | ||
- | Через NAT нормально ходят только TCP и UDP.\\ | ||
- | Потому просто взяли и сделали поверх UDP.\\ | ||
- | |||
- | Это решение подкосило многие домашние мыльницы и некоторых провайдеров: | ||
- | Для TCP - NAT знает когда соединение установлено и когда оно завершено, | ||
- | |||
- | Другим побочным эффектом явилось то, что uTorrent запрашивал больше трафика чем позволял тарифный план провайдера, | ||
- | Авторы uTorrent позже научились правильно подстраиваться под канал, но их эксперименты стоили нервов и денег.\\ | ||
- | |||
- | Ещё одним неприятным моментом в экспериментах с uTP на начальных этапах было то, что он генерировал большую пакетную нагрузку, | ||
- | Повышение пакетрейта губительно сказывается на WiFi и прочих радиолинках.\\ | ||
- | |||
- | В целом, протокол оказал ощутимое влияние как на провайдеров так и на производителей железа, | ||
- | |||
- | |||
- | ===== Протокол uTP ===== | ||
- | [[http:// | ||
- | |||
- | |||
- | ==== Версия 0 ==== | ||
- | Начиная с uTorrent 1.8 | ||
- | < | ||
- | uint32_t connid; | ||
- | uint32_t tv_sec; | ||
- | uint32_t tv_usec; | ||
- | uint32_t reply_micro; | ||
- | uint8_t windowsize; | ||
- | uint8_t ext; | ||
- | uint8_t flags; | ||
- | uint16_t seq_nr; | ||
- | uint16_t ack_nr; | ||
- | /* 23 ext/data */ | ||
- | } utp_pkt_t, *utp_pkt_p; /* 23 bytes */</ | ||
- | |||
- | |||
- | ==== Версия 1 ==== | ||
- | Начиная с uTorrent 2.0 | ||
- | < | ||
- | uint8_t version: | ||
- | uint8_t type: | ||
- | uint8_t ext; | ||
- | uint16_t connid; | ||
- | uint32_t tv_usec; | ||
- | uint32_t reply_micro; | ||
- | uint32_t windowsize; | ||
- | uint16_t seq_nr; | ||
- | uint16_t ack_nr; | ||
- | /* 20 ext/data */ | ||
- | } utp_pkt_v1_t, | ||
- | |||
- | |||
- | ==== Типы пакетов ==== | ||
- | < | ||
- | 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 / | ||
- | };</ | ||
- | |||
- | **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** - если есть дополнительные расширения/ | ||
- | |||
- | **tv_usec**, | ||
- | |||
- | |||
- | ==== Шифрование ==== | ||
- | Шифрования нет. Совсем.\\ | ||
- | Не потому что описание не полное а потому что оно реализовано несколькими уровнями выше и uTP никак не касается.\\ | ||
- | uTP заголовки никак не шифруются.\\ | ||
- | |||
- | |||
- | ===== Фильтрация / распознавание uTP ===== | ||
- | === Заход 1: uTPControl === | ||
- | Из спортивного интереса я решил попробовать написать нечто что сможет аккуратно выключать uTP у юзеров чей трафик проходит через роутер с моей программой.\\ | ||
- | В начале я пробовал слать RESET с виндовой машины, | ||
- | |||
- | [[ru: | ||
- | uTP протокол был любезно предоставлен в [[https:// | ||
- | |||
- | Работало uTPControl чрезвычайно просто: | ||
- | Те клиент пытался установить связь и сразу получал RESET - те его вроде как отключил тот к кому он подключался.\\ | ||
- | |||
- | Минусов у этого решения было два:\\ | ||
- | 1. Ложные срабатывания: | ||
- | |||
- | 2. Низкая производительность: | ||
- | В один поток на CoreDuo Е5300 под FreeBSD 7.3 выдавало до 100 тысяч пакетов RESET в секунду.\\ | ||
- | Один поток можно было обойти запустив несколько экземпляров и как то раскидав через ipfw пакеты между ними.\\ | ||
- | |||
- | В виду этих фатальных недостатков интерес со стороны сообщества пропал и я её забросил.\\ | ||
- | |||
- | Был человек который превратил это в netgraph ноду, но ложные срабатывания это не вылечило.\\ | ||
- | |||
- | |||
- | === Заход 2: ng_utp === | ||
- | [[ru: | ||
- | Прошло полтора года, я успел покопаться в ядре FreeBSD и netgraph, лучше узнать как работает сеть и пришла мысль: uTP имеет состояния аналогичные TCP, значит чтобы его 100% определять нужно эти состояния отслеживать.\\ | ||
- | Заодно я ещё раз заглянул в [[https:// | ||
- | За сигнатуры решил не цепляться, | ||
- | Идеальный вариант это свой " | ||
- | |||
- | В итоге получилась netgraph нода, которую можно подключать к L2 хукам типа ng_ether или L3 хукам, например ng_ipfw. В первом случае можно вообще сделать прозрачный эзернет мост из двух сетевух (не обязательно физических). Ещё можно просто поставить сервер и зеркалировать на него весь траф, но я сейчас не уверен в работоспособности такой схемы.\\ | ||
- | |||
- | Результатов замеров производительности не сохранил.\\ | ||
- | Однако нода без проблем параллелится по ядрам, может выполнятся как контексте ISR так и потоками netgraph, взаимные блокировки потоков сведены к минимуму.\\ | ||
- | |||
- | В случае L2: мультикаст и броадкаст пакеты пролетают сразу насквозь, | ||
- | Обрабатываются только IPv4 пакеты UDP, не адресованные и не отправленные с 127/8, не являющиеся броадкастом или мультикастом, | ||
- | |||
- | Далее проверяется что содержимое UDP пакета похоже на uTP.\\ | ||
- | Если содержимое похоже то ищем в таблице состояний запись для данной пары хостов: | ||
- | Теперь есть элемент который хранит все uTP соединения между двумя хостами или то что похоже на них. | ||
- | Ищем там connid, если не нашли то connid+-1, если опять нет - добавляем.\\ | ||
- | |||
- | Теперь у нас есть куда писать данные по конкретному uTP соединению.\\ | ||
- | Пишем: время последнего обновления, | ||
- | seq_nr и ack_nr можно было бы тоже запоминать и анализировать но и без них достаточно точно получается определять.\\ | ||
- | Старые записи удаляются автоматически.\\ | ||
- | |||
- | Теперь мы точно знаем что хосты установили uTP соединения, | ||
- | |||
- | Действовать имеет смысл только для DATA и STATE пакетов.\\ | ||
- | |||
- | Что можно сделать: | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | Чтобы сгенерировать RST пакет все данные есть: src ip:port + dst ip:port, pkt_ver, connid, ack_nr, seq_nr. | ||
- | Фактически у IP/UDP пакета заменяются данные, | ||
- | |||
- | Сейчас это всё ещё может быть актуальным для различных беспроводных сетей и офисных сетей, остальные уже обновились и расширились.\\ | ||
- | |||
- | |||
- | ===== Сигнатуры ===== | ||
- | Администраторы искали способ как быстро нормализовать работу сети и решили фильтровать uTP по сигнатурам пакетов, | ||
- | " | ||
- | Притом, | ||
- | |||
- | Спустя пару месяцев " | ||
- | Ещё через некоторое время авторы uTP поменяли пару незначительных для протокола начальных значений в SYN пакете и что то [[http:// | ||
- | |||
- | После того как ng_utp был написан стало понятно что проверять корректность работы с помощью tcpdump без правильных сигнатур мягко говоря не удобно - слишком много лишнего приходилось пробегать глазами.\\ | ||
- | Я ещё раз пробежался по коду [[https:// | ||
- | |||
- | |||
- | ==== Версия 0 ==== | ||
- | === SYN === | ||
- | syn - 14 bytes: | ||
- | < | ||
- | 41 = udp hdr len (8) + upd pkt data len\\ | ||
- | |||
- | upd header included: | ||
- | < | ||
- | - последнее это то что можно скармливать в tcpdump, отличается от первой смещениями и тем что константы объединены чтобы сравнений было меньше. Первая больше для самообразования.\\ | ||
- | |||
- | |||
- | === RESET === | ||
- | rst - 4 bytes: | ||
- | < | ||
- | upd header included: | ||
- | < | ||
- | |||
- | |||
- | ==== Версия 1 ==== | ||
- | === SYN === | ||
- | syn - 14 bytes: | ||
- | < | ||
- | " | ||
- | upd header included: | ||
- | < | ||
- | |||
- | |||
- | === RESET === | ||
- | rst - 4 bytes: | ||
- | < | ||
- | " | ||
- | upd header included: | ||
- | < | ||
- | |||
- | |||
- | ===== Обнаружение фильтрации ===== | ||
- | Проще всего, используя описание протокола, | ||
- | По сути нужно симулировать установление соединения, | ||
- | Дальше один клиент запускается в интернете, | ||
- | Сходным образом при использовании yota некоторые пакеты из l2tp на завершающем этапе согласования пропадают в 100% случаев. Так было ещё в сентябре 2014.\\ | ||
- | |||
- | |||
- | ===== Заключение ===== | ||
- | 1. uTP имеет достаточно чёткие сигнатуры и легко ловится DPI.\\ | ||
- | Более того, ловить сигнатуры в TCP ощутимо сложнее, | ||
- | Авторы uTP либо не ставили себе цель сделать протокол без сигнатур либо даже не приблизись к цели.\\ | ||
- | На мой взгляд в начале не ставили, | ||
- | |||
- | 2. Производители различных DPI уже давно добавили сигнатуры для uTP, вряд ли им это было трудно сделать.\\ | ||
- | |||
- | 3. В порядке слухов: | ||
- | |||
- | 4. Для IPv6 код не писал, на всякий случай ;)\\ | ||
- | |||
- | 5. uTP не лучше TCP для передачи данных, | ||
- | Говорить про оверхэд в 23/20 байт сейчас уже не актуально, | ||
- | Возможно с приходом кучи готовых либ для HTTP/2.0 торренты пустят и через него, скорее всего это вопрос времени. | ||