Some checks failed
Published content check / validate (push) Failing after 7s
Полный текст выпусков с анимированными SVG-схемами: «Фейк и его условия». Список видео в Zapret2/Zapret2.md обновлён. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1082 lines
198 KiB
Markdown
Executable file
1082 lines
198 KiB
Markdown
Executable file
---
|
||
title: "Видео 7. Фейк и его условия"
|
||
date: 2026-10-25
|
||
tags:
|
||
- zapret2
|
||
- видео
|
||
- сценарий
|
||
- fake
|
||
- fooling
|
||
- ttl
|
||
- md5
|
||
aliases:
|
||
- "Видео 7"
|
||
description: "Видео 7 серии о Zapret 2: поддельные пакеты и условия, при которых они доходят до DPI, но не до сервера, с анимированными схемами."
|
||
---
|
||
|
||
> [!info] О чём заметка
|
||
> Это полный текст видео номер 7 из серии о том, как работает [[Zapret2/Zapret2|Zapret 2]], разбитый по сценам, с анимированными схемами. Каждую сцену можно читать отдельно.
|
||
|
||
> [!tip] Как читать пометки
|
||
> **«В нашем тесте»** значит настоящий запуск в лаборатории проекта: клиент, роутер, сервер. **«По коду»** значит, что мы прочитали исходники, но не запускали. **«По общим сведениям»** значит, что это вне нашего кода и лаборатории. О настоящем оборудовании ТСПУ мы ничего не утверждаем: наш «инспектор» это модель.
|
||
|
||
|
||
## Подложное письмо: повтор и вопрос выпуска
|
||
|
||
Добро пожаловать в выпуск о подделках. Сначала короткий повтор, чтобы эту часть можно было смотреть отдельно от остальных.
|
||
|
||
В выпуске «Письмо в сортировочном центре» мы собрали модель наблюдателя: игрушечный инспектор стоит в роутере и читает пакеты. Это игрушка нашего проекта, а не настоящее оборудование. Слово «сработало» в нашей серии значит: игрушку обошли, и сервер получил настоящие байты.
|
||
|
||
В выпусках «multisplit: резать на части» и «Перемешать: disorder» программа работала с байтами самого запроса: резала их на куски и отправляла по порядку и не по порядку. В сети были настоящие байты запроса. Исключение одно: накладка seqovl, лишние нулевые байты перед первой частью. А в конце «Перемешать: disorder» был намёк: добавочный сегмент со сроком жизни один исчезал на роутере. Но и в нём лежали настоящие байты запроса.
|
||
|
||
Сегодня другой класс приёмов. Программа ничего не режет и не переставляет: настоящий пакет остаётся таким, как был, а рядом она кладёт лишний, с чужими данными. По-английски он называется fake, по-русски скажем «фейк».
|
||
|
||
![[wiki-anim-video07-s01-cut-or-fake.svg|Резка или фейк]]
|
||
|
||
Так же называется и стратегия, которая его отправляет: функция программы вместе с её аргументами, как в выпуске «Читаем командную строку zapret2».
|
||
|
||
Теперь слово из названия выпуска. Настройки, которые делают подделку непригодной для получателя, в программе называются fooling. Мы зовём их «условиями подделки», а коротко «условиями»: это намеренный дефект, без которого подделка получателю вредит. Не путайте их с условиями профиля из выпуска «Читаем командную строку zapret2»: те решают, какие пакеты достаются профилю, а эти решают, чем подделка отличается от настоящего пакета. В выпусках «multisplit: резать на части» и «Перемешать: disorder» то же самое называлось подменой полей, а в одном месте просто подделками. Это один механизм под тремя именами, и дальше мы говорим только «условие подделки», а коротко «условие».
|
||
|
||
Игрушка, пример выдуманный. На ленте идут письма. Сортировщик смотрит в окошко только первое письмо каждого адресата и по нему решает, пропускать ли всю переписку. Мы кладём на ленту перед настоящим письмом подложное, с безобидным адресом. Сортировщик читает его, успокаивается и дальше не смотрит.
|
||
|
||
Но у подложного письма два читателя. Если адресат его не получит или выбросит, всё хорошо. Если прочтёт как настоящее, он ответит на чужие слова, и переписка сломается. Весь выпуск о том, как сделать подделку, которая обманывает первого читателя и не обманывает второго.
|
||
|
||
План части. Сначала подделка без условий: она ломает соединение. Потом первое условие, срок жизни: подделка умирает на дороге, и мы посмотрим, на каком роутере. В конце разберём, как программа сама узнаёт длину пути. Остальные условия пойдут в следующих частях.
|
||
|
||
Договорённости, они повторятся ещё не раз. «Работает» значит: работает против нашего игрушечного инспектора и нашего получателя, сервера на Linux. О настоящем оборудовании мы не знаем ничего. «В нашем тесте» значит настоящий запуск программы в нашей лаборатории. «По общим сведениям» значит знание не из нашего кода и не из лаборатории. «По счёту» значит арифметика на бумаге. «Прогон функций» значит, что мы запустили настоящие функции программы без сети и подали им числа.
|
||
|
||
![[wiki-anim-video07-s01-fooling-one-term.svg|Три имени, один термин]]
|
||
|
||
Итак. Фейк это лишний пакет рядом с настоящим, а вопрос выпуска такой: чем подложный пакет должен отличаться от настоящего, чтобы его принял один читатель и отверг другой, и как программа этого добивается? Начнём с картинки на проводе, а потом разберём, что от такого пакета вообще требуется.
|
||
|
||
|
||
## Картинка заранее: что уходит на провод и какой у нас инспектор
|
||
|
||
Вопрос сцены: что именно увидит сервер, когда рядом с настоящим приветствием встанет подделка? Покажем это одной картинкой, ещё без подробностей, а в следующих сценах разберём каждую часть.
|
||
|
||
Клиент собирается отправить приветствие в двести пятьдесят байт, с именем www.example.com. Программа со стратегией fake ставит перед ним второй пакет: шестьсот восемьдесят байт, а имя в нём другое, www.microsoft.com. Номера у двух пакетов одинаковые: оба претендуют на первый байт потока. Значит, на провод уходят два пакета подряд, сначала подделка, за ней настоящий. В нашем тесте так и было.
|
||
|
||
![[wiki-anim-video07-s01b-two-packets-one-number.svg|Два пакета, один номер]]
|
||
|
||
На входе в роутер стоит инспектор, и он здесь другой, чем в прошлых выпусках. Тот игрушечный инспектор смотрел каждый пакет отдельно и ничего не помнил. Этот ждёт первый пакет с данными, решает по нему один раз, и решение действует на всё соединение. Первым ему попадается подделка: имя в ней разрешённое, и настоящее приветствие он уже не читает. Как мы его построили, покажем в сцене про лабораторию.
|
||
|
||
Остаётся вопрос, ради которого весь выпуск: что сервер сделает с двумя пакетами на одном месте. Если примет подделку за данные, соединение сломано. Поэтому подделке нужны условия, о которых мы только что говорили: пусть она до сервера не доедет, или пусть сервер сам её выбросит.
|
||
|
||
![[wiki-anim-video07-s01b-inspector-decides-once.svg|Инспектор решает один раз]]
|
||
|
||
И одна договорённость о слове «игрушка». Фразы вроде «Игрушка, выдуманная» или «Игрушка, пример выдуманный» открывают картинку для понимания, вроде письма на ленте: к лаборатории они отношения не имеют. А когда мы говорим просто «игрушка» про инспектора в роутере, это сокращение от «игрушечный инспектор», нашей модели наблюдателя.
|
||
|
||
Итак. С фейком на провод уходят два пакета, подделка и настоящий. Инспектор этого выпуска судит по первому пакету с данными, поэтому подделка встаёт первой. Дальше выясним, чем подделка должна отличаться от настоящего пакета.
|
||
|
||
|
||
## Три задачи подделки: два читателя одного пакета
|
||
|
||
Вопрос сцены: что должно быть правдой о подложном пакете, чтобы он помог? Три вещи.
|
||
|
||
Игрушка, выдуманная. Подложное письмо летит по ленте и проходит двух читателей. Первый, сортировщик с окошком, должен принять его за настоящее. Второй, адресат, не должен.
|
||
|
||
Первая задача: правдоподобие. Подложный пакет должен выглядеть как первый пакет с данными соединения, то есть как начало защищённого соединения, приветствие. Наш игрушечный инспектор, мы это увидим на опытах, судит именно так.
|
||
|
||
Насколько глубоко должно быть это правдоподобие? Для нашей игрушки совсем неглубоко. В одном нашем опыте подделкой служил блоб, готовый набор байтов с именем, всего из десяти байт: заголовок записи TLS и пять произвольных. Наш инспектор, о нём в сцене про лабораторию, ищет в таком пакете запрещённое имя, не находит и смотрит только на первые два байта, и ему этого хватило: он разрешил пакет. В том опыте у подделки было ещё условие, о нём позже. Это свойство нашей игрушки, а не настоящего оборудования.
|
||
|
||
Вторая задача: отказ получателя. Получатель не должен принять подделку за настоящие данные. Способов два. Первый: пакет не доходит, он умирает по дороге, как письмо с просроченной маркой. Второй: пакет доходит, но получатель отвергает его по формальной причине, как письмо с неправильной печатью. Сегодня первый способ, второй пойдёт в следующих частях.
|
||
|
||
Третья задача: безвредность. Подделка не должна запутать соединение: настоящий пакет, идущий следом, должен дойти как обычно, и сервер должен ответить клиенту.
|
||
|
||
![[wiki-anim-video07-s02-two-readers-three-jobs.svg|Два читателя, три задачи]]
|
||
|
||
На игрушке. Если адресат вскроет подложное письмо вместе с настоящим, он перепутает страницы и ответит невпопад. Безвредность значит, что страницы не путаются, и переписка идёт дальше.
|
||
|
||
Заметим и порядок. Инспектор нашей игрушки судит по первому пакету с данными, поэтому подделка должна встать перед настоящим пакетом, а не после: если настоящий пойдёт первым, решение будет вынесено уже по нему. Как письмо, которое кладут на ленту первым, потому что сортировщик смотрит только на первое. Что программа так и делает, мы увидим на пакетах.
|
||
|
||
Откуда берётся содержимое подделки? Из блоба. Блоб, как мы разбирали в выпуске «Читаем командную строку zapret2», это именованный набор байтов: из файла, из текста или встроенный в программу. В программе уже есть встроенный образец приветствия, в коде он называется fake_default_tls. Это запись приветствия на шестьсот восемьдесят байт, и имя сайта в ней другое: www.microsoft.com.
|
||
|
||
Наше настоящее приветствие короче: двести пятьдесят байт, имя www.example.com. Значит, подделка длиннее настоящего пакета на четыреста тридцать байт. Откуда взялись байты образца и что в них лежит, разберём ближе к концу выпуска.
|
||
|
||
![[wiki-anim-video07-s02-ten-bytes-and-sizes.svg|Десять байт, два размера]]
|
||
|
||
Проверь себя. Допустим, подделка умерла слишком рано: раньше, чем дошла до инспектора. Какая из трёх задач провалена?
|
||
|
||
Первая. Инспектор подделку не увидел и судит по настоящему пакету, как будто её не было: обмануть ему нечего. Вот почему важно, где стоит инспектор: в нашей лаборатории он на входе в первый роутер. Посмотрим на лабораторию.
|
||
|
||
Итак. У подделки три задачи: быть правдоподобной для инспектора, не быть принятой получателем и не навредить соединению. Дальше мы проверяем это не на словах, а на пакетах, и для этого нужна лаборатория.
|
||
|
||
|
||
## Лаборатория и две точки отсчёта: игрушка запрещает настоящее приветствие, без игрушки всё проходит
|
||
|
||
Вопрос сцены: где и чем мы смотрим на подделки, и что бывает с настоящим приветствием, когда подделки нет совсем? Лабораторию опишем один раз, все опыты выпуска идут в ней.
|
||
|
||
Игрушка, выдуманная. Шлагбаум на въезде в город. Охранник смотрит на первую машину колонны и решает один раз: пропустить всю колонну или закрыть въезд.
|
||
|
||
Лаборатория знакома по выпуску «Письмо в сортировочном центре»: клиент, роутер, сервер и четыре точки, где мы записываем пакеты. Цэ-ноль это сетевая карта клиента после работы программы, эр-ноль вход в роутер, эр-один выход из него, эс-ноль сетевая карта сервера. Цэ-ноль стоит после программы, поэтому на ней видно всё, что программа отправила, в том числе подделки, которые потом не доедут до сервера. В этом выпуске новое одно: роутеров может быть два или три, и тогда добавляются точки эр-два и эр-три.
|
||
|
||
Срок жизни, TTL, мы помним. В выпуске «Что видит наблюдатель» мы называли его временем жизни, а в этом выпуске говорим «срок жизни». Клиент ставит шестьдесят четыре, каждый роутер вычитает единицу, а пакет с нулём роутер выбрасывает. В нашем тесте пакет с данными записан на четырёх точках со сроками шестьдесят четыре, шестьдесят четыре, шестьдесят три и шестьдесят три. Вход в роутер ещё не вычел единицу, выход уже вычел.
|
||
|
||
Теперь игрушечный инспектор этого выпуска. Это вторая копия программы nfqws2, мы поставили её на входе в первый роутер, до того как роутер проверит срок жизни. Подделок эта копия не делает: она запускает только наш короткий скрипт на Lua, который читает пакеты, записывает своё решение в журнал и выбрасывает запрещённые.
|
||
|
||
Она следит за одним соединением, от клиента к серверу, и решает один раз, по первому пакету с данными. Прежний игрушечный инспектор судил каждый пакет отдельно, а этот запоминает своё решение.
|
||
|
||
Внутри имя www.example.com: соединение запрещено, этот пакет и все следующие данные клиента выбрасываются. Запись приветствия TLS или строка запроса HTTP без имени: разрешено, дальше она пакеты не смотрит. Это наше допущение, а не знание о настоящем оборудовании.
|
||
|
||
Получатель один: сервер на Linux шестой серии. У него два слоя. Ядро принимает пакеты и может отвергнуть какие-то, записав это в свои счётчики. Приложение получает только то, что ядро приняло, и записывает, сколько байт пришло.
|
||
|
||
О счётчиках ядра, чтобы дальше не возвращаться. Ядро Linux, по общим сведениям, ведёт числовые счётчики: сколько пакетов оно приняло, сколько отвергло и по какой причине. Это не тот счётчик правила, что был в игрушечном инспекторе прошлых выпусков, а счётчики самого ядра. Мы снимали их у клиента и у сервера до каждого опыта и после, и разница показывает, какой из них сдвинулся. Имена счётчиков будут на экране, а по-русски мы читаем их как причину. Это наше чтение названий: прямо ядро причину не сообщает, доказано лишь, какой счётчик сдвинулся в каждом опыте.
|
||
|
||
![[wiki-anim-video07-s03-lab-chain-and-toy.svg|Лаборатория и инспектор]]
|
||
|
||
Строка запуска, как в выпуске «Читаем командную строку zapret2»: фильтр вида данных, имя стратегии и её аргументы. Условий подделки в этой части поначалу нет.
|
||
|
||
Каждый опыт читаем одной таблицей: что ушло, где это видно, что получил сервер, что видит клиент и что решила игрушка.
|
||
|
||
Первая точка отсчёта. Стратегия «пустышка», по-латыни pass, из выпуска «Читаем командную строку zapret2»: она ничего не делает, только пишет в журнал, что её вызвали. Игрушка включена. Три пакета рукопожатия проходят: данных в них нет, и игрушка их не судит.
|
||
|
||
Потом клиент отправляет настоящее приветствие: двести пятьдесят байт, имя www.example.com. Игрушка читает этот первый пакет с данными, находит имя и решает: соединение запрещено. На выходе из роутера пакета нет.
|
||
|
||
Подтверждения клиент не получает и отправляет приветствие снова. В нашем тесте шесть отправок, паузы между ними растут, и все шесть есть на точках цэ-ноль и эр-ноль, а на точках эр-один и эс-ноль ни одной. Журнал игрушки отмечает первый пакет строкой «имя найдено», остальные пять строкой «соединение запрещено, данные выбрасываю». Счётчик повторов в ядре клиента вырос на пять.
|
||
|
||
Сервер не получил данных вовсе и закрывает молчащее соединение примерно через две секунды после первого приветствия. Шестое приветствие клиент отправил уже после закрытия. Итог: сервер получил ноль байт.
|
||
|
||
У нашей игрушки всё решает первый пакет с данными, что бы ни пришло дальше. Поэтому подделку нужно поставить перед настоящим приветствием, на первое место.
|
||
|
||
Проверь себя. Выключим игрушку и оставим пустышку. Что получит сервер?
|
||
|
||
Приветствие идёт одно, без повторов, и срок жизни у него падает с шестидесяти четырёх до шестидесяти трёх. Сервер отвечает подтверждением: жду двести пятьдесят первый байт. Следом идёт его ответ в четырнадцать байт. Сервер получил ровно двести пятьдесят байт, с именем www.example.com.
|
||
|
||
Назовём такой итог «чистым потоком»: сервер получил ровно настоящие байты клиента. Это наш эталон успеха. В таблицах будем писать «чисто», когда так, и «испорчен», когда сервер получил что-то другое. Подделка должна пройти две проверки сразу: игрушка не запрещает соединение, и поток остаётся чистым.
|
||
|
||
![[wiki-anim-video07-s03-two-baselines.svg|Две точки отсчёта]]
|
||
|
||
Итак. В лаборатории от одного до трёх роутеров, игрушка на входе в первый и один получатель на Linux. С игрушкой и без подделки настоящее приветствие запрещено, сервер получает ноль байт. Без игрушки оно проходит, и поток чистый. Между этими двумя опытами и будет работать подделка.
|
||
|
||
|
||
## Подделка без условий: пакет за пакетом
|
||
|
||
Вопрос сцены: что будет, если отправить подделку без единого условия? Это самый важный опыт части: он показывает, зачем нужны условия.
|
||
|
||
Игрушка, выдуманная. Адресат получает два письма с надписью «страница один». Первое кладёт в папку как первую страницу, второе считает дубликатом. Какое пришло первым, то и стало страницей.
|
||
|
||
Строка опыта: стратегия fake с аргументом blob, образец fake_default_tls, условий подделки нет, игрушка включена. Номера здесь относительные, как в выпусках «Что видит наблюдатель» и «multisplit: резать на части»: начало соединения принято за ноль, а первый байт данных клиента получает номер один. Идём по точке цэ-ноль пакет за пакетом. Первый пакет с данными несёт шестьсот восемьдесят байт, номер один. Это подделка, срок жизни у неё обычный.
|
||
|
||
Сразу за ней второй пакет: настоящее приветствие, двести пятьдесят байт, и номер тоже один. Два разных пакета встали на одно место потока.
|
||
|
||
И идентификатор IP у них одинаковый. Откуда он берётся, скажем в следующей сцене. Оба пакета видны на входе в роутер, на выходе и на сервере.
|
||
|
||
Игрушка на входе читает первый пакет с данными, то есть подделку. Заблокированного имени там нет, это запись приветствия TLS: разрешено, дальше не смотрим. Настоящее приветствие игрушка уже не читает. В нашем тесте первую задачу подделка выполнила: игрушка обманута.
|
||
|
||
![[wiki-anim-video07-s04-wire-ladder-no-condition.svg|Подделка без условий]]
|
||
|
||
Теперь сервер. Он принимает первый из двух пакетов, подделку, как данные с первого байта по шестьсот восьмидесятый. Подтверждение сервера: жду шестьсот восемьдесят первый байт. Следом он отправляет свой ответ в четырнадцать байт. Приложение сервера получило один кусок, и в нём имя www.microsoft.com.
|
||
|
||
Настоящее приветствие приходит после и целиком лежит в уже принятом месте. Сервер отвечает блоком SACK, и этот блок лежит ниже самого подтверждения. Так, мы помним по выпуску «Перемешать: disorder», получатель сообщает о дубликате.
|
||
|
||
А что видит клиент? Он отправил двести пятьдесят байт, и его следующий номер двести пятьдесят первый. Сервер же подтверждает шестьсот восемьдесят один: данные, которых клиент не отправлял.
|
||
|
||
В нашем тесте клиент отвечает пустыми пакетами со своим номером двести пятьдесят один, а подтверждение в них остаётся на единице: ни одного байта ответа сервера он не принял. Сервер повторяет свой ответ в четырнадцать байт, клиент ответа не получает, четыре секунды тишины. Два счётчика ядра клиента, по названиям про недопустимые подтверждения, тоже выросли; так мы читаем названия, прямо ядро этого не говорит.
|
||
|
||
И это не разовая беда. Клиент повторяет приветствие, и перед каждой из шести отправок встаёт своя подделка: стратегия не помнит, что фейк уже был, она вызывается на каждом подходящем пакете.
|
||
|
||
Виновата ли игрушка? Нет. В таком же опыте без игрушки сервер получил те же шестьсот восемьдесят байт, а клиент так же молчал. Беда не в инспекторе, а в том, что получатель принял подделку за данные.
|
||
|
||
Откуда настоящий пакет после подделки уходит как обычно? Функция fake отправляет подделку тем же отправщиком части, что и в выпуске «multisplit: резать на части», только вместо данных подставлен блоб. Функция кончается без возврата, программа читает «ничего» как «пропустить», и настоящий пакет идёт следом. Multisplit, напомним, возвращала «выбросить».
|
||
|
||
![[wiki-anim-video07-s04-server-client-and-code.svg|Взгляд сервера и клиента]]
|
||
|
||
Проверь себя. Сервер ответил: жду шестьсот восемьдесят первый байт. Сколько байт он принял и чьи они?
|
||
|
||
Шестьсот восемьдесят. Настоящих байт у клиента всего двести пятьдесят, значит, все принятые байты это подделка, и цифра в подтверждении её выдаёт. Без подделки сервер ждал бы двести пятьдесят первый.
|
||
|
||
Итак. Подделка без условий обманула игрушку и сломала соединение: сервер принял её за данные, клиент это подтверждение не принял, переписка встала. Значит, нужно условие, при котором получатель подделку не примет. Начнём с самой простой: пусть она умрёт по дороге.
|
||
|
||
|
||
## Анатомия подделки: что у неё общее с настоящим пакетом, в каком порядке она уходит и почему очередь её не видит
|
||
|
||
Вопрос сцены: из чего сделана подделка и чем она отличается от настоящего пакета? Ответ даст проба: положить два пакета рядом.
|
||
|
||
Берём пакеты из опыта прошлой сцены. Первая группа одинаковых полей: адреса и порты, идентификатор IP, порядковый номер и номер подтверждения. Значения совпадают до последней цифры. Вторая группа: флаги, окно, параметры TCP с отметкой времени и срок жизни. Даже отметка времени у подделки та же, что у настоящего пакета.
|
||
|
||
Различаются четыре вещи, и все они связаны с тем, что данные другие. Данные: у подделки шестьсот восемьдесят байт. У настоящего пакета двести пятьдесят. Длина в заголовке IP, потому что данные другие. И обе контрольные суммы, IP и TCP: программа пересчитала их, обе верны, это просто суммы разных пакетов.
|
||
|
||
![[wiki-anim-video07-s05-fake-vs-real-table.svg|Подделка и настоящее приветствие]]
|
||
|
||
Откуда эта копия? Отправщик части, знакомый нам по выпуску «multisplit: резать на части», копирует таблицу текущего пакета и вместо данных ставит блоб. Потом применялка условий, по-латыни apply_fooling, делает ещё одну копию и применяет к ней условия. Итого две операции: копия с блобом вместо данных и условия на этой копии. У fake условия касаются только отправляемой копии: настоящий пакет они не трогают.
|
||
|
||
Отсюда вывод. Подделка без условия это правильный пакет на том же номере, и получатель вправе её принять, мы это видели. Любое условие должно сделать подделку неприемлемой для получателя и оставить нетронутым то, что читает инспектор.
|
||
|
||
Идентификатор IP у подделки и у настоящего пакета один: функция берёт его из текущего пакета, а следующий свой пакет нумерует на единицу больше.
|
||
|
||
Шесть отправок приветствия на точке цэ-ноль дают шесть пар. В каждой паре подделка стоит перед настоящим пакетом, и у пары один идентификатор.
|
||
|
||
Порядок. В наших запусках, где подделка шла на пакете с данными, первым с данными на точке цэ-ноль стоял фейк, настоящее приветствие следом. Подделка уходит в ядро прямо внутри вызова функции, а настоящий пакет программа отпускает только после того, как вернулись все стратегии. Устройство главного цикла разбирает выпуск «Внутри программы».
|
||
|
||
Почему программа не ловит свою подделку снова? Ядро отдаёт ей пакеты через очередь. Свои пакеты программа помечает особой отметкой, по общим сведениям это число, которое ядро хранит рядом с пакетом, и правило ядра помеченные пакеты в очередь не отдаёт; подробности в выпуске «Внутри программы». Нам достаточно журнала: среди пакетов, которые получила программа, ни одной подделки нет.
|
||
|
||
![[wiki-anim-video07-s05-copy-order-queue.svg|Копия, порядок и очередь]]
|
||
|
||
Проверь себя. Клиент отправил приветствие один раз, и стратегия fake сработала. Сколько пакетов с данными получит программа из очереди и сколько уйдёт в сеть?
|
||
|
||
Из очереди один: настоящее приветствие клиента, ведь каждая подделка рождается внутри программы и уходит с отметкой. В сеть уйдут два: подделка и настоящее. В нашем тесте на шесть приветствий клиента так и вышло: шесть пакетов в очереди и двенадцать на проводе.
|
||
|
||
Итак. Подделка это копия заголовков настоящего пакета с другими данными и пересчитанными длиной и суммами. Она уходит первой, с тем же идентификатором, и в очередь не возвращается. Всё, что мы захотим изменить в заголовках, делают условия.
|
||
|
||
|
||
## Письмо с просроченной маркой: срок жизни один на одном роутере
|
||
|
||
Вопрос сцены: как заставить подделку исчезнуть по дороге, и что при этом видят все участники?
|
||
|
||
Игрушка, выдуманная. На письме марка с числом штрихов. Сортировочный центр, получив письмо, смотрит на марку. Остался один штрих: письмо выбрасывают, а отправителю шлют записку «срок вышел». Штрихов больше: центр стирает один и отправляет письмо дальше. Марка на два штриха и три центра подряд: первый стирает штрих, второй видит последний и выбрасывает письмо. До третьего оно не доедет.
|
||
|
||
В сети роль марки играет срок жизни, TTL: каждый роутер вычитает единицу, и пакет, у которого срок вышел, роутер выбрасывает. Условие, которым мы его задаём, называется «срок жизни подделки», по-латыни ip_ttl. Число записывается в заголовок IP подделки, и только подделки; настоящий пакет уходит со своим сроком.
|
||
|
||
Опыт. Один роутер, ip_ttl равно один. На точке цэ-ноль сначала подделка: шестьсот восемьдесят байт, срок жизни один. Следом настоящее приветствие со сроком шестьдесят четыре.
|
||
|
||
На входе в роутер видны оба пакета. Игрушка читает первый пакет с данными, то есть подделку, и в её журнале срок жизни пакета один. Заблокированного имени там нет: разрешено.
|
||
|
||
На выходе из роутера только настоящее приветствие, и срок у него уже шестьдесят три. Подделку роутер выбросил: срок у неё вышел, и дальше она не пошла. Это видно по точкам: на эр-один и на эс-ноль её нет.
|
||
|
||
![[wiki-anim-video07-s06-toy-stamp-and-ladder.svg|Марка со штрихами и опыт]]
|
||
|
||
Но роутер не молчит. По общим сведениям, так сети сообщают об ошибках: он отправляет обратно отправителю пакета служебное сообщение «время истекло», ICMP, с адреса самого роутера. В нём роутер приводит начало выброшенного пакета, поэтому клиент получает цитату собственной подделки. Размеры покажем на экране.
|
||
|
||
Итог опыта. Сервер подтвердил двести пятьдесят первый байт, ответил четырнадцатью байтами и закрыл соединение. Он получил чистые двести пятьдесят байт, и клиент получил ответ. Повторов не было: на точке цэ-ноль ровно два пакета с данными, подделка и настоящий. В нашем тесте, против нашей игрушки и нашего получателя, сработали все три задачи сразу.
|
||
|
||
![[wiki-anim-video07-s06-icmp-quote-and-ttl2.svg|Цитата ICMP и срок 2]]
|
||
|
||
И важная оговорка о нашей модели. Игрушка стоит на входе в первый роутер, до того как роутер вычтет единицу. Поэтому она видит подделку со сроком один. Если бы инспектор стоял за роутером, он такую подделку не увидел бы. Где стоит настоящее оборудование относительно места, где умирает пакет, мы не знаем.
|
||
|
||
Проверь себя. Та же схема, один роутер, но срок жизни подделки два. Дойдёт ли она до сервера?
|
||
|
||
Дойдёт. Роутер сотрёт один штрих, на выходе срок один, и по общим сведениям получатель такой пакет принимает: выбрасывают роутеры, а не получатель. В нашем тесте сервер ответил подтверждением с номером шестьсот восемьдесят один, то есть принял шестьсот восемьдесят байт. Приложение получило подделку, а клиент молчал: те же симптомы, что без условия.
|
||
|
||
Итак. Срок один на одном роутере: подделка доходит до игрушки на входе, на выходе её нет, клиент получает цитату, а сервер чистый поток. Срок два на одном роутере уже не работает: подделка доходит до сервера.
|
||
|
||
|
||
## Какое число ставить: два и три роутера, и где умирает каждая подделка
|
||
|
||
Вопрос сцены: какое число ставить, если между клиентом и сервером несколько роутеров, и где умрёт подделка при каждом числе?
|
||
|
||
Игрушка со штрихами из прошлой сцены. Марка на два штриха и три центра подряд: письмо умирает на втором. Где оно пропало, зависит только от числа штрихов.
|
||
|
||
Теперь настоящие пакеты. Два роутера, срок один. Подделка видна на точках цэ-ноль и эр-ноль, дальше её нет, а служебное сообщение «время истекло» пришло с адреса первого роутера. Сервер получил чистые двести пятьдесят байт.
|
||
|
||
Срок два на тех же двух роутерах. Подделка проходит первый, на его выходе срок уже один. На втором роутере она умирает, и ответ «время истекло» приходит с адреса второго. Настоящее приветствие доходит до сервера со сроком шестьдесят два, и поток чистый.
|
||
|
||
Срок три на двух роутерах. Подделка проходит оба и достигает сервера: на выходе из второго у неё срок один. Приложение сервера получило подделку, шестьсот восемьдесят байт, клиент молчит. Подделка не умерла, значит, три слишком много.
|
||
|
||
Теперь три роутера. Срок три: подделка видна на всех точках до выхода из второго роутера, умирает на третьем, ответ приходит с адреса третьего, сервер получает чистый поток. Срок четыре доходит до сервера и портит поток.
|
||
|
||
Правило по счёту. Подделка достигает сервера, если срок не меньше числа роутеров плюс один. Срок, равный числу роутеров, убивает её на последнем роутере, перед самым сервером. Срок меньше умирает раньше. Наши запуски на трёх путях согласны с правилом.
|
||
|
||
![[wiki-anim-video07-s07-where-it-dies.svg|Где умирает подделка]]
|
||
|
||
Крайнее значение: ноль. Оно допустимо, подделка отправляется и умирает на первом роутере.
|
||
|
||
Теперь заметим, что в цитате внутри служебного сообщения срок такой, с каким подделка пришла на этот роутер. У нас это единица, а при исходном нуле в цитате ноль. По общим сведениям, роутер выбрасывает такой пакет и цитирует его в том виде, как получил.
|
||
|
||
Зачем целиться в последний роутер, если хватило бы и единицы? В нашей лаборатории игрушка стоит на входе, и ей достаточно единицы. Если бы инспектор стоял дальше по пути, подделка с единицей до него не дошла бы, и срок пришлось бы подбирать ближе к концу пути. Это рассуждение, мы его не проверяли.
|
||
|
||
Другое крайнее значение: двести пятьдесят шесть. Программа его отвергает. В нашем тесте это ошибка внутри Lua, подделка не отправляется вовсе, игрушка запрещает настоящее приветствие, и сервер получает ноль байт.
|
||
|
||
Прогон функций, напомним, это запуск настоящих функций без сети. Двести пятьдесят пять допустимо и записывается как есть. Минус один и слово вместо числа дают ту же ошибку. Допустимо число от нуля до двухсот пятидесяти пяти.
|
||
|
||
Проверь себя. Три роутера, срок жизни два. Где умрёт подделка?
|
||
|
||
На втором роутере. На выходе из первого срок один, и второй роутер её выбрасывает. Это не запуск, а арифметика по нашему правилу: запуска со сроком два на трёх роутерах у нас нет. Два меньше трёх плюс один, значит, до сервера она не дойдёт.
|
||
|
||
![[wiki-anim-video07-s07-edges-zero-256-check.svg|Ноль, 256 и счёт на бумаге]]
|
||
|
||
Итак. Подделка умирает на том роутере, где срок кончается, и до сервера доходит, только если срок не меньше числа роутеров плюс один. Срок ноль допустим, а число вне границ ломает стратегию. А откуда программа знает число роутеров? Об этом в конце части.
|
||
|
||
|
||
## IPv6: предельное число переходов, и условие не той версии, которое молча не делает ничего
|
||
|
||
Вопрос сцены: что меняется для IPv6, и что будет, если написать условие не той версии?
|
||
|
||
Лаборатория умеет ходить и по IPv6: это шестая версия протокола IP, мы упоминали её в выпуске «Что видит наблюдатель». По общим сведениям, адреса там другие и заголовок устроен немного иначе, но клиент, роутер, сервер и те же приветствия остаются. До сих пор мы работали по IPv4, четвёртой версии, как в прошлых выпусках.
|
||
|
||
Игрушка, выдуманная. Фабрика с двумя конвейерами, у каждого свой переключатель скорости, на каждом подпись. Если повернуть переключатель второго конвейера, первый не заметит ничего, и никто об этом не скажет. Нас будет интересовать, какой переключатель мы повернули.
|
||
|
||
В IPv6 нет поля «срок жизни» под этим названием. Поле того же назначения называется предельным числом переходов, по-английски hop limit, и по общим сведениям роутер вычитает из него единицу так же, как из срока жизни. Условие, которым мы его задаём, это «предельное число переходов подделки», по-латыни ip6_ttl. Это то же правило под другим именем.
|
||
|
||
Опыт на одном роутере, ip6_ttl равно один. Подделка видна на точках цэ-ноль и эр-ноль и дальше не идёт. Роутер отвечает служебным сообщением «время истекло» для IPv6, с адреса самого роутера, и в цитате значение один. Сервер получил чистые двести пятьдесят байт.
|
||
|
||
Два роутера, ip6_ttl равно два. Подделка проходит первый роутер и умирает на втором, ответ приходит с адреса второго, поток чистый. Правило то же, что для IPv4: умирает там, где значение кончается.
|
||
|
||
Есть разница в цитате: в IPv6 она в нашем тесте длиннее, подделка процитирована целиком. В IPv4, помните, цитата была обрезана.
|
||
|
||
![[wiki-anim-video07-s08-ipv6-hoplimit-runs.svg|IPv6: то же правило]]
|
||
|
||
Теперь ошибка, которую легко сделать. Клиент идёт по IPv6, а в строке стоит условие ip_ttl, для IPv4. В нашем тесте на проводе у подделки предельное число переходов шестьдесят четыре, как у любого обычного пакета: условие ничего не изменило. Подделка дошла до сервера, приложение приняло шестьсот восемьдесят байт, клиент молчит. И ни одной строки об ошибке.
|
||
|
||
![[wiki-anim-video07-s08-wrong-version-silent.svg|Условие не той версии молчит]]
|
||
|
||
Почему так, видно в коде. Одно место, две ветви. Если у пакета заголовок IPv4, функция читает ip_ttl и пишет в срок жизни. Если у пакета заголовок IPv6, она читает ip6_ttl и пишет в предельное число переходов. Чужую пару условий ветвь не читает вообще. Программа не считает это ошибкой: условие записано верно, просто для другой версии.
|
||
|
||
Прогон функций показывает все четыре сочетания. Для IPv6 условие ip6_ttl равно один даёт значение один, а ip_ttl равно один оставляет шестьдесят четыре. Для IPv4 наоборот.
|
||
|
||
Следствие, по коду и по этим запускам: если стратегия должна работать на обеих версиях, нужны оба условия. Версия без своего условия остаётся подделкой без условия, а чем это кончается, мы видели. Практический вывод: сначала смотрим, по какой версии идёт соединение, и только потом выбираем имя условия.
|
||
|
||
Проверь себя. В строке только ip_ttl равно один, а клиент идёт по IPv6. Что получит сервер?
|
||
|
||
Подделку: шестьсот восемьдесят байт. Условие не той версии молча пропущено, и стратегия ведёт себя, как без условия. Именно это показал наш запуск.
|
||
|
||
Итак. В IPv6 то же правило с другим названием поля: ip6_ttl задаёт предельное число переходов, и подделка умирает там, где оно кончается. Условие чужой версии не делает ничего и не жалуется, поэтому про обе версии надо помнить отдельно.
|
||
|
||
|
||
## Автоматический срок жизни: как программа узнаёт длину пути из пакета, который пришёл обратно
|
||
|
||
Вопрос сцены: программа должна сама узнать длину пути. Откуда она знает, сколько роутеров между клиентом и сервером? Ответ приходит из пакета, который идёт в обратную сторону.
|
||
|
||
Игрушка, выдуманная. Ответное письмо приходит с остатком марки. Допустим, наш сервер всегда ставит марку шестьдесят четыре, а пришло письмо с отметкой шестьдесят два. Шестьдесят четыре минус шестьдесят два, два центра по дороге. Это верно, если обратный путь такой же, как прямой.
|
||
|
||
Какой пакет от сервера нужен? В нашей лаборатории первый, ответ на открытие соединения, SYN-ACK. На точке цэ-ноль его срок жизни в трёх наших запусках: один роутер, шестьдесят три. Два роутера, шестьдесят два. Три роутера, шестьдесят один.
|
||
|
||
Программа записывает это число в журнал: «incoming TTL», входящий срок. По счёту: шестьдесят четыре минус шестьдесят три, один роутер. Минус шестьдесят два, два роутера. Минус шестьдесят один, три. Столько, сколько мы и поставили.
|
||
|
||
![[wiki-anim-video07-s09-hops-from-synack.svg|Длина пути из SYN-ACK]]
|
||
|
||
Где число запоминается? В записи соединения: так называется память программы о каждом соединении, её устройство разбирает выпуск «Память о соединении». Программа, обработав пакет от сервера, смотрит, есть ли в записи входящий срок. Если нет, записывает срок этого пакета.
|
||
|
||
В нашем тесте число берётся один раз за соединение, из первого пакета от сервера, который очередь отдала программе. По коду программа ещё запоминает его по адресу сервера и может взять оттуда в следующем соединении; в лаборатории это не проверяли.
|
||
|
||
Если правила ядра не отдают программе входящие пакеты, числа не будет. В нашей лаборатории отдают.
|
||
|
||
Но число шестьдесят два само не длина пути. Программа должна угадать, с какого числа начинал ответ. Она считает, что сервер начинает с одного из трёх круглых чисел: двести пятьдесят пять, сто двадцать восемь или шестьдесят четыре, и что путь короче тридцати двух переходов. Тогда полосы не пересекаются.
|
||
|
||
Больше двухсот двадцати трёх: начало двести пятьдесят пять.
|
||
|
||
От девяносто седьмого до ста двадцать восьмого: начало сто двадцать восемь.
|
||
|
||
От тридцать третьего до шестьдесят четвёртого: начало шестьдесят четыре. Всё, что попало между полосами, это «не знаю», и числа нет. Если догадка есть, переходов столько, сколько начальное значение минус пришедшее.
|
||
|
||
![[wiki-anim-video07-s09-guess-bands-tape.svg|Три полосы и счёт переходов]]
|
||
|
||
Проверим на числах. Прогон функций: шестьдесят два дают два перехода, сто двадцать шесть тоже два, двести пятьдесят три тоже два. Для проверенных чисел между полосами догадки нет; границы самих полос взяты из кода.
|
||
|
||
Проверь себя. Входящий срок сто семнадцать. Сколько переходов?
|
||
|
||
Сто семнадцать попадает в полосу от девяносто седьмого до ста двадцать восьмого, начало сто двадцать восемь, переходов одиннадцать. Это по счёту, правило из кода, запуска с таким числом у нас нет.
|
||
|
||
Два допущения стоят за всем этим, руководство говорит о них прямо. Первое: сервер начинал с одного из трёх значений. Второе: обратный путь по длине такой же, как прямой. В нашей лаборатории путь симметричный по построению, и это не доказательство для настоящей сети.
|
||
|
||
Итак. Программа читает срок первого пакета от сервера, угадывает начальное значение из трёх возможных и вычитает: так она узнаёт длину пути. Если догадки нет, числа нет. Но длина пути ещё не срок подделки: как из неё получается «на один роутер раньше» или «ровно перед сервером», разберём в следующей части.
|
||
|
||
|
||
## Что мы знаем к этому месту: подделка, срок жизни и длина пути
|
||
|
||
Подведём итог первой части.
|
||
|
||
Первое: у подделки три задачи. Она должна быть правдоподобной для инспектора, не быть принятой получателем и не навредить соединению. Всё это мы проверяли только против нашей игрушки на входе в первый роутер и нашего получателя, сервера на Linux.
|
||
|
||
Второе: подделка без условия обманывает игрушку, но ломает соединение. Сервер принимает её за данные и отвечает подтверждением с номером шестьсот восемьдесят один, то есть принял шестьсот восемьдесят байт. Клиент отправил только двести пятьдесят и этого подтверждения не принимает, ответа нет. Перед каждым повтором приветствия стоит своя подделка, потому что стратегия не помнит прошлых.
|
||
|
||
Третье: подделка это копия заголовков настоящего пакета с другими данными. Тот же идентификатор IP, тот же номер, те же флаги и окно. Она уходит первой и помечена как своя, поэтому очередь её не видит. У fake условия применяются к этой копии и не трогают настоящий пакет.
|
||
|
||
Четвёртое: срок жизни. Подделка умирает на том роутере, где срок кончается, и доходит до сервера, только если срок не меньше числа роутеров плюс один. Роутер сообщает о смерти служебным сообщением «время истекло» с цитатой.
|
||
|
||
В IPv6 то же делает ip6_ttl, а условие чужой версии молча ничего не делает. Число вне границ от нуля до двухсот пятидесяти пяти программа отвергает: подделка не отправляется, и игрушка запрещает настоящее приветствие.
|
||
|
||
Пятое: длину пути программа узнаёт из пакета, который пришёл обратно. Она читает срок первого пакета от сервера, угадывает начальное значение из трёх и вычитает. Если догадки нет, числа нет.
|
||
|
||
Проверь себя. Два роутера. Какой срок поставить, чтобы подделка умерла на последнем роутере?
|
||
|
||
Два. В нашем тесте подделка со сроком два была видна на точках цэ-ноль, эр-ноль и эр-один, на втором роутере умерла, и ответ «время истекло» пришёл с его адреса. Сервер получил чистый поток.
|
||
|
||
![[wiki-anim-video07-s10-two-routers-answer.svg|Два роутера: срок 2]]
|
||
|
||
Как читать любой опыт выпуска, мы теперь знаем: что видно на точках записи, что получил сервер, что видит клиент и что решила игрушка. Так мы будем смотреть и на следующие условия.
|
||
|
||
Теперь о границах. Мы проверяли одного получателя, Linux шестой серии. Мы не проверяли другие системы, роутер, который не отвечает служебными сообщениями или меняет срок, и пути, у которых обратная дорога не равна прямой. А настоящий инспектор нам неизвестен: наша игрушка просто стоит там, где ей удобно.
|
||
|
||
![[wiki-anim-video07-s10-five-points.svg|Итог первой части: пять пунктов]]
|
||
|
||
Что дальше. В следующей части мы превратим длину пути в срок подделки: дельта, диапазон, правило знака, тихие отказы и ошибки. А потом перейдём к условиям другого рода, при которых подделку отвергает уже сам получатель: подпись MD5, неверная сумма, неверные номера и отметка времени. Лаборатория и способ чтения опытов останутся те же.
|
||
|
||
Итак. Подделка это лишний пакет рядом с настоящим. Без условия он ломает соединение, со сроком жизни умирает по дороге, а длину пути программа узнаёт сама, из пакета, пришедшего обратно. И всё это проверено против нашей игрушки и нашего сервера, а не против настоящего оборудования.
|
||
|
||
|
||
## Повтор, а затем первая цифра подбора: срок жизни равен числу переходов плюс дельта
|
||
|
||
Это вторая часть выпуска «Фейк и его условия», её можно смотреть отдельно, поэтому начнём с короткого повтора. Фейк, он же подделка, это один лишний пакет: программа копирует заголовки настоящего пакета с данными, кладёт в копию чужую нагрузку и отправляет её перед настоящим. Нагрузка в наших опытах готовая: поддельное приветствие на шестьсот восемьдесят байт.
|
||
|
||
В прошлых выпусках наблюдатель был правилом в роутере, которое смотрело каждый пакет отдельно. Здесь он другой: вторая копия нашей программы в роутере ждёт первый пакет с данными и раз и навсегда решает судьбу соединения. Это игрушечный инспектор, модель, а не настоящее оборудование. О настоящем ТСПУ мы ничего не знаем, и слово «работает» дальше значит: работает против этой модели и против нашего получателя, сервера под Linux.
|
||
|
||
Без условий подделка вредит: в нашем тесте получатель принимает её за данные, и соединение встаёт. Поэтому подделке нужны условия подделки, по-английски fooling; в выпусках про резку мы называли это подменой полей. В этой части две темы: подбор срока жизни, у которого много тихих отказов, и шесть условий, после которых получатель сам выбрасывает подделку. Причину мы читаем по счётчикам ядра получателя, их значения мы снимали до и после каждого опыта.
|
||
|
||
Срок жизни, TTL, падает на единицу на каждом роутере. Подделка, у которой срок равен числу роутеров, умирает на последнем, и оттуда приходит сообщение «время истекло». Получатель её не видит, а инспектор нашей модели стоит раньше и видит. Число роутеров программа выводит из срока первого ответного пакета сервера: у нас он пришёл со значением шестьдесят два, и если сервер стартовал с шестидесяти четырёх, то переходов, они же хопы, два.
|
||
|
||
Какой срок поставить подделке, если до сервера два перехода?
|
||
|
||
Игрушка, выдуманная. На конверте стоит счётчик, каждое отделение связи вычитает единицу, а кто получил ноль, возвращает письмо отправителю. Отделений на пути два. Поставим два: первое вычтет единицу, второе получит ноль и вернёт письмо. Поставим один: письмо вернётся уже с первого. Поставим три: оно проедет оба и попадёт в город.
|
||
|
||
Этот аргумент по-русски называется «автоматический срок жизни», коротко «автоподбор», в строке стратегии он записан ip_autottl. Его значение читается так: ноль, запятая, один, дефис, двадцать. Первое число называется дельтой, остальное диапазоном, его разберём в следующей сцене.
|
||
|
||
Дельта, по-английски delta, это число со знаком. Программа берёт число переходов и прибавляет дельту. Ноль значит «ровно по числу переходов», минус один «на переход короче», плюс один «на переход длиннее». Ошибиться в сторону «короче» безопасно: подделка умрёт раньше. Ошибиться в сторону «длиннее» опасно: она дойдёт до сервера.
|
||
|
||
Первый опыт, дельта ноль. В журнале входящий срок шестьдесят два и «найден автоподбор, два». Подделка ушла со сроком два, на втором роутере срок кончился, и клиенту пришло «время истекло» с адреса второго роутера. Инспектор стоит в первом роутере, до проверки срока: в его журнале пакет в шестьсот восемьдесят байт со сроком два, соединение разрешено. Получатель получил чистые двести пятьдесят байт, и это результат нашего теста.
|
||
|
||
Дельта минус один даёт срок один: подделка умирает на первом роутере, инспектор всё равно её видит, получатель получает чистые двести пятьдесят байт.
|
||
|
||
Дельта плюс один даёт три: подделка проходит оба роутера и доезжает до сервера, получатель принимает её за данные, клиент напрасно ждёт ответа. Это не ошибка подбора, мы сами попросили на переход дальше.
|
||
|
||
![[wiki-anim-video07-s11-delta-three-runs.svg|Дельта: три опыта]]
|
||
|
||
Теперь код. Расчёт делает маленькая функция, назовём её оценщиком, в библиотеке это часть функции autottl. Её первая строка: d равно hop_count плюс attl точка delta, то есть переходы плюс дельта. Остальные строки про диапазон и знак, они в следующей сцене.
|
||
|
||
![[wiki-anim-video07-s11-autottl-first-run.svg|Автоподбор: первый опыт]]
|
||
|
||
Проверь себя. Дорога из трёх роутеров, ответ сервера пришёл со сроком шестьдесят один, дельта ноль. Какой срок у подделки и где она умрёт?
|
||
|
||
Переходов три: шестьдесят четыре минус шестьдесят один. Срок три, подделка умирает на третьем, последнем роутере. В нашем тесте сообщение «время истекло» пришло с адреса третьего роутера, а получатель получил чистые двести пятьдесят байт.
|
||
|
||
![[wiki-anim-video07-s11-three-routers-check.svg|Три роутера: срок 3]]
|
||
|
||
Итак. Срок подделки равен числу переходов плюс дельта. На дороге из двух роутеров минус один убивает подделку на первом, ноль на последнем, плюс один пропускает до сервера. В нашем тесте это работает против нашей модели и нашего получателя, и только если число переходов угадано верно.
|
||
|
||
|
||
## Диапазон и проверка знака: когда подбор отказывается
|
||
|
||
Остались два числа в аргументе: диапазон. Что он делает и когда подбор отказывается отвечать?
|
||
|
||
Игрушка, выдуманная. Вы просите у термостата двадцать пять градусов, а в паспорте написано, что прибор держит от десяти до двадцати двух. Он поставит двадцать два. Диапазон делает со сроком жизни то же: что ниже нижней границы, поднимает до неё, что выше верхней, опускает до неё.
|
||
|
||
Опыт, два роутера: дельта пять, диапазон от одного до трёх. Два плюс пять дают семь, это выше верхней границы, берётся три. Подделка со сроком три проезжает оба роутера, получатель принимает её за данные, клиент ждёт ответа. Подбор сработал как написано, просто результат нам не годится.
|
||
|
||
Теперь отказ. Два роутера, дельта минус три, диапазон от двух до девяти. Два минус три дают минус один, диапазон поднимает до двух. Мы просили срок короче пути, а вышло два, и это не короче: путь тоже два. Подбор отказывает. Журнал пишет «не удалось найти автоподбор» на каждую отправку приветствия, шесть раз за опыт: клиент завис и повторяет приветствие, а перед каждым повтором программа делает свою подделку.
|
||
|
||
Что тогда с подделкой? В нашем тесте её срок остался шестьдесят четыре, она доехала до сервера, получатель принял шестьсот восемьдесят чужих байт, клиент завис. Отказ не отключает подделку, он лишает её условия.
|
||
|
||
Обратный случай: дельта ноль, диапазон от пяти до девяти. Два плюс ноль дают два, диапазон поднимает до пяти. Просили не короче пути, вышло пять, проверка довольна. Срок пять, и подделка доезжает до сервера. Проверка смотрит на направление, а не на цель.
|
||
|
||
Чтобы не строить дорогу из пяти роутеров, мы запустили настоящие функции библиотеки на выдуманных числах, без сети. Мы называем это «прогоном функций». Для пяти переходов дельта пять с диапазоном от одного до трёх даёт отказ: три меньше пяти, а дельта неотрицательная. А дельта минус три с диапазоном от одного до двадцати даёт два: пять минус три, и знак сохранён.
|
||
|
||
![[wiki-anim-video07-s12-clamp-and-sign-runs.svg|Диапазон и знак: три опыта]]
|
||
|
||
Теперь код оценщика. Первая строка знакомая: d равно числу переходов плюс дельта. Дальше диапазон: если d меньше нижней границы, результат равен ей, если больше верхней, то верхней, иначе просто d.
|
||
|
||
И последняя строка, проверка знака, два условия через «или». Просили короче пути, а вышло не короче. Или просили не короче, а вышло короче. Если верно хоть одно, функция возвращает nil, то есть «ничего».
|
||
|
||
Проверь себя. Дорога из одного роутера, дельта минус один, диапазон от одного до двадцати. Что вернёт подбор?
|
||
|
||
Один минус один дают ноль, диапазон поднимает до одного. Просили короче пути, а вышло один, и путь тоже один: отказ. Опыт показал то же: срок остался шестьдесят четыре. На дороге из одного роутера «на переход раньше» просто некуда.
|
||
|
||
![[wiki-anim-video07-s12-evaluator-code-walk.svg|Оценщик по строкам]]
|
||
|
||
Итак. Диапазон подтягивает результат к границам, проверка знака отказывает, если результат противоречит знаку дельты. В нашем тесте отказ значит срок шестьдесят четыре и подделку, которая доезжает до сервера. А верный знак цель ещё не гарантирует.
|
||
|
||
|
||
## Когда подбор молчит или ошибается: запасные пути, неверные догадки, память
|
||
|
||
Что получает подделка, если подбор не нашёл числа и что будет, если догадка неверна?
|
||
|
||
Если подбор ничего не вернул, у подделки остаётся срок жизни настоящего пакета, шестьдесят четыре. Но к подбору можно дописать запасное число, аргумент ip_ttl: оно включается только тогда, когда подбор молчит. Руководство описывает это так же.
|
||
|
||
Если число найдено, оно главнее. Мы записали автоподбор «ноль, от одного до двадцати» и запасное число один: подбор нашёл два, и подделка ушла со сроком два.
|
||
|
||
Опыт без ответов. Правила ядра у клиента мы настроили так, чтобы программа получала только исходящие пакеты: ядро отдаёт ей лишь то, что выбрали правила, это разбирает выпуск «Внутри программы». Ответа сервера программа не видит, и в первом соединении срок ответа взять неоткуда. Без запасного числа подделка уходит со сроком шестьдесят четыре, и получатель принимает её за данные. С запасным числом один она умирает на первом роутере, а получатель видит чистые двести пятьдесят байт.
|
||
|
||
Откуда программа берёт срок ответа? Из записи соединения: в неё она кладёт срок первого ответного пакета, подробности в выпуске «Память о соединении». Если записи нет, например когда запись соединений выключена отдельным ключом, ветка автоподбора пропускается молча, и берётся запасное число. В нашем тесте с этим ключом подделка ушла со сроком один.
|
||
|
||
По коду программа запоминает этот срок ещё и по адресу сервера, по умолчанию на два часа, и может взять его из прошлого соединения. У нас в каждом опыте одно соединение, поэтому это мы не проверяли.
|
||
|
||
Теперь опечатка: вместо дельты и диапазона написано просто abc. Если срок ответа известен, разбор значения падает с ошибкой. Ошибка в функции значит, что стратегия в этот раз не сработала и пакет идёт как есть, мы видели это в выпуске «multisplit: резать на части». Так же падает разбор, если нижняя граница больше верхней.
|
||
|
||
Подделки нет совсем, и игрушечный инспектор читает настоящее приветствие с запрещённым именем и блокирует соединение.
|
||
|
||
А если срока ответа нет, то же abc не вызывает ошибки: разбор до него не доходит. Подделка уходит со сроком шестьдесят четыре, получатель принимает её за данные. Одна опечатка, два разных тихих отказа.
|
||
|
||
Проверь себя. Ответов сервера программа не видит, в строке автоподбор «ноль, от одного до двадцати» и запасной ip_ttl, равный одному. Какой срок будет у подделки?
|
||
|
||
Один: автоподбор числа не нашёл, берётся запасное. В нашем тесте подделка умерла на первом роутере, получатель получил чистые двести пятьдесят байт.
|
||
|
||
![[wiki-anim-video07-s13-what-fake-gets.svg|Что получает подделка: семь опытов]]
|
||
|
||
Теперь про догадку. Напомним из начала выпуска: число переходов это стартовое число сервера минус пришедшее, а стартовое программа угадывает по трём полосам, до шестидесяти четырёх, до ста двадцати восьми и до двухсот пятидесяти пяти. Со стартами сто двадцать восемь и двести пятьдесят пять подбор в нашем тесте посчитал верно, два перехода.
|
||
|
||
Стартовые числа тридцать два и восемьдесят мы выдумали, чтобы потрогать границы, и заставили сервер ставить их в ответы. Пришли тридцать и семьдесят восемь, ни в одну полосу они не попали, догадки нет, подбор отказал, подделка доехала до сервера.
|
||
|
||
Хуже неверная догадка. Старт сто, пришло девяносто восемь: это вторая полоса, и подбор уверенно считает сто двадцать восемь минус девяносто восемь, тридцать переходов, а на самом деле их два. В нашем тесте при дельте ноль и диапазоне до двадцати результат срезан до двадцати, проверка знака отказала, и срок остался шестьдесят четыре.
|
||
|
||
При диапазоне пошире вышел бы срок тридцать, подделка дошла бы до сервера и стала данными: это по счёту, такого запуска не было. Неверная догадка даёт или тихий отказ, или неверный срок, и оба молчат.
|
||
|
||
Какие стартовые числа у настоящих серверов, мы не знаем. И руководство называет второе допущение: подбор считает путь обратно таким же длинным, как туда. Наша лаборатория симметрична по построению, настоящая сеть не обязана.
|
||
|
||
Теперь память. Удачное число запоминается на время соединения, отдельно для каждой записи lua-desync в строке: два фейка в строке помнят каждый своё. В опыте журнал пишет «найдено три», а потом пять раз «использую запомненное»: клиент завис и повторяет приветствие.
|
||
|
||
Отказ не запоминается: подбор пробует снова на каждом пакете, это показал прогон функций.
|
||
|
||
![[wiki-anim-video07-s13-guess-bands-and-memory.svg|Догадка о старте и память]]
|
||
|
||
Итак. Если подбор молчит и запасного числа нет, подделка остаётся со сроком шестьдесят четыре, а найденное число главнее запасного. Опечатка даёт два разных тихих отказа. Неверная догадка о старте сервера даёт тихий отказ или неверный срок. Удача запоминается на соединение, отказ нет. Всё проверено только на нашей симметричной лаборатории.
|
||
|
||
|
||
## Опция MD5: подпись, ключа от которой у получателя нет
|
||
|
||
Начинаем шесть условий для получателя. Первое: опция эм-дэ-пять. Как заставить наш получатель выбросить пакет, который ни один роутер на пути не заметит? Дальше опыты идут на дороге из одного роутера.
|
||
|
||
Игрушка, выдуманная. Письмо запечатано печатью, которую получатель не признаёт. Сортировочный центр печатей не проверяет, получатель видит незнакомую печать и выбрасывает письмо.
|
||
|
||
В TCP есть опция для подписи пакетов между сторонами, которые заранее договорились о ключе; по общим сведениям, её используют, например, между роутерами. Если подпись пришла, а ключа нет, наш получатель пакет отбрасывает.
|
||
|
||
Причину мы видим по счётчику ядра получателя. Его имя, оно на экране, мы читаем как «подпись, которой не ждали». Это наше чтение: доказано лишь, что в этом опыте сдвинулся именно он.
|
||
|
||
Опыт: аргумент эм-дэ-пять без значения, в записи tcp_md5. Подделка уходит со сроком шестьдесят четыре, с теми же флагами и номером, что у настоящего пакета. Заголовок TCP вырос с тридцати двух байт до пятидесяти двух. Тридцать два это двадцать обязательных и двенадцать опций: две пустые опции NOP и метка времени. Добавилась опция: вид девятнадцать, длина восемнадцать, шестнадцать случайных байт подписи и два нулевых байта выравнивания.
|
||
|
||
Подделку видно в четырёх точках записи: у клиента, на входе и на выходе первого роутера и у сервера. Роутер опцию не проверяет, инспектор принимает подделку за первый пакет. А получатель её выбросил: счётчик вырос на единицу, прикладная программа получила чистые двести пятьдесят байт, клиент получил ответ.
|
||
|
||
В нашем тесте содержимое подписи не важно: с шестнадцатью байтами, заданными вручную, результат тот же. Важно, что опция правильного вида и длины есть.
|
||
|
||
![[wiki-anim-video07-s14-md5-road-counter.svg|Опция MD5: дорога и счётчик]]
|
||
|
||
Теперь код. Если аргумент задан и такой опции в пакете ещё нет, в список опций добавляется новая: вид эм-дэ-пять и данные. Данные это шестнадцатеричная строка из аргумента или, если значение пустое, шестнадцать случайных байт.
|
||
|
||
И ловушка: код не проверяет длину. Мы написали ноль-ноль-один-один, это два байта. Опция получилась длиной четыре, заголовок вырос до тридцати шести, счётчик не сдвинулся, получатель принял подделку, клиент завис. Нам кажется, что это для получателя уже не подпись, но проверено лишь то, что он подделку принял.
|
||
|
||
И про место: на опции в заголовке TCP отведено не больше сорока байт. В прогоне функций мы взяли пакет, у которого опции уже занимали почти всё это место: подпись не поместилась, сборка пакета выдала ошибку, и стратегия в этот раз не сработала.
|
||
|
||
Проверь себя. Есть ключ «убрать пустые опции», в записи tcp_nop_del. Метка времени вместе с двумя пустыми опциями занимает двенадцать байт, ключ убирает эти две. Какой тогда будет длина заголовка с подписью?
|
||
|
||
Метка времени без пустых опций занимает десять байт. Десять плюс восемнадцать дают двадцать восемь, плюс двадцать байт основного заголовка, всего сорок восемь. Так и было на проводе: двадцать восемь уже делится на четыре, и нулей выравнивания нет. Получатель подделку отверг.
|
||
|
||
![[wiki-anim-video07-s14-md5-header-strips.svg|Заголовок TCP с опцией MD5]]
|
||
|
||
Итак. Опция эм-дэ-пять это подпись без ключа: роутеры её пропускают, наш получатель отбрасывает пакет, и счётчик «подпись, которой не ждали» растёт. Содержимое неважно, длина обязана быть настоящей, места должно хватить. Всё это проверено только на нашем получателе и нашей модели.
|
||
|
||
|
||
## Испорченная контрольная сумма
|
||
|
||
Второе условие для получателя: испорченная контрольная сумма. Что это за число и почему порчу не замечает никто на пути?
|
||
|
||
Игрушка, выдуманная. На посылке написан вес. Сортировочный центр её не взвешивает, а получатель взвешивает при вскрытии. Вес не сошёлся, посылка возвращается.
|
||
|
||
Контрольная сумма TCP это число в заголовке. Отправитель считает его по адресам, заголовку и данным, а получатель пересчитывает и сверяет. Не сошлось, значит пакет испорчен в пути, и получатель его выбрасывает.
|
||
|
||
Роутерам на пути эту сумму проверять незачем: по общим сведениям, им хватает заголовка IP. В нашем тесте так и вышло: подделка с неверной суммой прошла роутер и дошла до сервера.
|
||
|
||
Опыт: аргумент «испортить сумму», в записи badsum, без значения. Мы разобрали подделку собственным разборщиком и пересчитали сумму сами: в заголовке стоит одно число, пересчёт даёт другое, и так во всех четырёх точках записи. Сами числа на экране. У настоящего приветствия следом число сходится.
|
||
|
||
Получатель подделку не принял. В счётчиках ядра сдвинулись два, по единице: ошибки TCP вообще и ошибки контрольной суммы. Прикладная программа получила чистые двести пятьдесят байт, клиент получил ответ. Игрушечный инспектор суммы не проверяет и принял подделку за первый пакет.
|
||
|
||
![[wiki-anim-video07-s15-sum-wire-and-effect.svg|Испорченная контрольная сумма на проводе и у получателя]]
|
||
|
||
Где эта порча делается? Не в подмене полей: там badsum нет. Подмена меняет копию пакета, а настоящие байты из копии собирает упаковщик пакета, в коде он называется reconstruct; как он устроен, рассказывает выпуск «От байтов к таблице». Сначала упаковщик считает правильную сумму для нового заголовка, поэтому у подделок из прошлых сцен суммы верные. И только потом, если задан badsum, портит её.
|
||
|
||
Порча это одна строка на языке Си. Исключающее «или» двух чисел переворачивает те биты первого, где у второго стоят единицы, а остальные оставляет как есть. Контрольная сумма меняется исключающим «или» с числом: единица плюс остаток от деления случайного числа на шестьдесят пять тысяч пятьсот тридцать пять. Это число от единицы и выше, нулём оно не бывает. А исключающее «или» с ненулевым числом всегда меняет значение.
|
||
|
||
Само значение аргумента не важно. Мы записали badsum равно ноль, и журнал отправки всё равно показывает «badsum равно один»: код включает порчу для любого заданного значения. Счётчик ошибок суммы сдвинулся так же.
|
||
|
||
Можно ждать, что аппаратный подсчёт сумм на сетевой карте скроет порчу. Мы оставили настройки виртуальных карт по умолчанию, и получатель подделку всё равно выбросил: счётчик ошибок суммы вырос на единицу. Почему, мы не выясняли.
|
||
|
||
![[wiki-anim-video07-s15-where-sum-is-spoiled.svg|Где портится контрольная сумма]]
|
||
|
||
Проверь себя. У подделок из прошлых сцен, с коротким сроком жизни и с опцией эм-дэ-пять, какая контрольная сумма?
|
||
|
||
Верная. Подмена полей сумму пересчитывает, портит её только отдельный аргумент. Так и было в наших прогонах для этой части: везде, кроме опыта с badsum, сумма у подделки сходится, мы проверили это разбором пакетов.
|
||
|
||
Итак. Неверную контрольную сумму видит наш получатель, а в нашем тесте роутеры не видят. Её портит упаковщик пакета после расчёта верной суммы, и маска порчи не бывает нулём. Счётчик получателя называет причину: ошибка суммы. Всё это проверено только на нашем получателе и нашей модели.
|
||
|
||
|
||
## Сдвиг порядкового номера: на сколько, чтобы подделку отбросили
|
||
|
||
Третье условие для получателя: сдвинуть порядковый номер подделки. На сколько, чтобы получатель её отбросил, а не принял за данные?
|
||
|
||
Игрушка, выдуманная. Театр, места пронумерованы. Билет на место, которое уже пройдено контролёром, не впустит. А билет на соседнее место может сработать, и зритель сядет не туда.
|
||
|
||
Напомним расчёт из выпуска «multisplit: резать на части»: номера считаются от начала данных клиента. Настоящее приветствие занимает номера с первого по двести пятидесятый, получатель ждёт номер один. Подделка в шестьсот восемьдесят байт копирует номер настоящего пакета и тоже начинается с единицы. Аргумент «сдвиг номера», в записи tcp_seq, прибавляет к нему своё число.
|
||
|
||
Минус шестьсот восемьдесят. Подделка занимает номера с минус шестьсот семьдесят девятого по нулевой и кончается ровно перед байтом один. Все её байты старые. Получатель их выбросил и ответил блоком SACK на шестьсот восемьдесят байт, дублирующим SACK, как в выпуске «Перемешать: disorder». У получателя вырос счётчик, который мы читаем как «дубликат о старых данных». Прикладная программа получила чистые двести пятьдесят байт.
|
||
|
||
Минус шестьсот семьдесят девять, на единицу меньше. Теперь подделка занимает номера с минус шестьсот семьдесят восьмого по первый, и её последний байт попадает на байт один, который получатель как раз ждёт. Он его принимает. Прикладная программа получила двести пятьдесят байт, но первый из них последний байт подделки, а остальные двести сорок девять настоящие. Первый байт приветствия подменён, и в потоке имя сайта уже не найти.
|
||
|
||
Минус один: подделка занимает номера от нулевого до шестьсот семьдесят девятого. Нулевой старый, остальные шестьсот семьдесят девять новые, и получатель принял их все. Клиент завис.
|
||
|
||
Значит, подделка должна лежать в прошлом целиком: сдвиг не больше минус её длины. Для наших шестисот восьмидесяти байт это минус шестьсот восемьдесят и меньше. Минус десять тысяч лежит с запасом и тоже работает.
|
||
|
||
![[wiki-anim-video07-s16-backward-shifts-tape.svg|Сдвиг номера назад на ленте номеров]]
|
||
|
||
Теперь вперёд. Плюс один оставляет дыру в один байт, настоящее приветствие её закрывает, и к нему дописывается хвост подделки: двести пятьдесят настоящих байт и ещё четыреста тридцать один чужой, всего шестьсот восемьдесят один. Клиент завис. Как очередь собирает такой поток, показывал выпуск «Перемешать: disorder».
|
||
|
||
Плюс десять тысяч попадает в окно приёма, то есть в то, сколько байтов вперёд получатель готов принять. Подделка ложится в очередь вне порядка, у получателя вырос счётчик этой очереди, а прикладная программа её не видит.
|
||
|
||
Плюс сто тысяч уже за окном приёма. Получатель её не принял: вырос счётчик, который мы читаем как «пакет за окном», а клиенту ушло пустое подтверждение «жду номер один», без блока SACK. Приложение получило чистые двести пятьдесят байт.
|
||
|
||
В коде это сложение: новый номер равен старому плюс заданное число по модулю два в тридцать второй степени. Отрицательное число переходит через ноль на другой край счёта, как в выпуске «multisplit: резать на части». Если вместо числа написана ерунда, будет ошибка, и стратегия в этот раз не сработает.
|
||
|
||
![[wiki-anim-video07-s16-forward-shifts-and-code.svg|Сдвиг номера вперёд и строка кода]]
|
||
|
||
Проверь себя. Подделка длиной в триста байт. Какой сдвиг нужен, чтобы она лежала в прошлом целиком?
|
||
|
||
Минус триста и меньше: последний байт подделки, то есть единица плюс сдвиг плюс двести девяносто девять, должен быть не больше нуля. Минус двести девяносто девять оставит один новый байт. Это счёт по правилу, запуска с такой подделкой у нас не было.
|
||
|
||
Итак. В нашем тесте подделка должна целиком лежать в прошлом: сдвиг не больше минус её длины. Назад на единицу меньше длины, и получатель принимает один байт подделки и портит поток. Вперёд в окне подделка ждёт в очереди, за окном получатель её не принимает.
|
||
|
||
|
||
## Сдвиг подтверждения: ответ-вызов и граница в пять тысяч байт
|
||
|
||
Четвёртое условие: подтверждение. В каждом пакете есть число, которым отправитель говорит, до какого байта он всё получил. Что будет, если подделка это число соврёт?
|
||
|
||
Игрушка, выдуманная. Две стороны переписываются. Чем больше писем собеседник вам прислал и вы подтвердили, тем более старую расписку он стерпит. Если вы ему ничего не слали, терпеть ему нечего.
|
||
|
||
Аргумент называется «сдвиг подтверждения», в записи tcp_ack, и работает так же, как сдвиг номера из прошлой сцены, только на другом поле. Опыт: восемь значений сдвига подтверждения, от минус шестидесяти шести тысяч до плюс шестидесяти шести тысяч, среди них минус один и плюс один. Результат один: подделка отвергнута, у сервера сдвинулся один и тот же счётчик, прикладная программа получила чистые двести пятьдесят байт.
|
||
|
||
Возьмём минус один. Подделка ушла с подтверждением ноль, а настоящее приветствие за ней с подтверждением один. Сервер сразу ответил пустым пакетом, повторяющим старое подтверждение. Такой ответ называют ответом-вызовом, по-английски challenge ACK, так называется и счётчик. По общим сведениям, это способ не верить подозрительному подтверждению и переспросить; мы видели только ответ и счётчик.
|
||
|
||
Код тот же, что у номера: к подтверждению прибавляется заданное число по модулю, отрицательное уходит через ноль. Меняется только поле.
|
||
|
||
![[wiki-anim-video07-s17-challenge-ack-eight-runs.svg|Ответ-вызов и восемь значений сдвига подтверждения]]
|
||
|
||
Но почему отвергнут даже минус один? Возможно, потому что сервер к этому моменту ничего не отправлял. Проверим на сервере, который говорит первым: сразу после соединения он отправляет клиенту пять тысяч байт. Клиент их читает, ждёт три десятых секунды, чтобы подтверждения ушли, и только потом отправляет приветствие.
|
||
|
||
Минус один, минус тысяча и минус пять тысяч: подделка принята, получатель получил шестьсот восемьдесят чужих байт, клиент завис.
|
||
|
||
Минус пять тысяч один отвергнут: счётчик ответа-вызова вырос, поток чистый.
|
||
|
||
Дальше отвергнуты все: минус шесть тысяч, минус шестьдесят шесть тысяч и даже плюс тысяча. Граница проходит ровно между минус пятью тысячами и минус пятью тысячами одним.
|
||
|
||
Сервер отправил байты с первого по пятитысячный, клиент подтвердил всё: подтверждение пять тысяч один. Подделка со сдвигом минус пять тысяч несёт подтверждение один, то есть «из потока сервера не получено ничего». Со сдвигом минус пять тысяч один она несёт ноль, и это уже до начала потока сервера. Это счёт по пакетам, он показывает границу, но не объясняет правило ядра.
|
||
|
||
В нашем тесте терпимая «старость» подтверждения равна тому, что сервер сам отправил и получил подтверждённым. Когда клиент говорит первым, как с приветствием TLS, сервер ничего не отправлял, терпеть нечего, и отвергается даже минус один. По общим сведениям, правило ядра берёт меньшее из двух чисел: окна клиента, примерно шестидесяти четырёх тысяч байт, и подтверждённых байтов. Мы проверили только пять тысяч, сервер, отправивший больше окна, не пробовали.
|
||
|
||
Теперь спор. Руководство говорит: Linux отбрасывает пакет с верным номером и неверным подтверждением, только когда метка времени идёт в списке опций первой. Для этого есть аргумент «метка первой», в записи tcp_ts_up. Мы повторили с ним четыре опыта из восьми: результат тот же, без него подделку отвергали тоже. В нашем тесте аргумент не понадобился, почему, мы не выясняли.
|
||
|
||
![[wiki-anim-video07-s17-first-speaking-server-boundary.svg|Сервер говорит первым: граница сдвига подтверждения]]
|
||
|
||
Проверь себя. Тот же сервер с пятью тысячами байт, сдвиг минус четыре тысячи: принята подделка или нет?
|
||
|
||
По границе принята: минус четыре тысячи ближе к нулю, чем минус пять тысяч, а минус пять тысяч принимался. Такого запуска у нас не было, это вывод из границы, а не измерение.
|
||
|
||
Итак. В нашем тесте сдвиг подтверждения отвергался с ответом-вызовом, пока сервер ничего не отправил; по общим сведениям, так бывает, когда первым говорит клиент, как в TLS и HTTP. Если сервер уже отправлял байты, малый сдвиг принимался. Условие «метка первой» мы не воспроизвели. Проверено на одном получателе и на пяти тысячах байт.
|
||
|
||
|
||
## Снятый флаг ACK, и почему PSH условием не является
|
||
|
||
Пятое условие для получателя: флаги. В заголовке TCP у каждого пакета есть набор флагов. Какие из них можно снять с подделки, чтобы получатель её выбросил?
|
||
|
||
Игрушка, выдуманная. В начатой переписке каждое письмо обязано нести отметку «я прочитал ваше предыдущее». Письмо без отметки получатель выбрасывает. В TCP такая отметка есть: это флаг ACK.
|
||
|
||
У приветствия клиента два флага. ACK значит «в пакете есть подтверждение», PSH значит «передайте данные приложению сразу». Каждому флагу соответствует свой разряд: у PSH он равен восьми, у ACK шестнадцати, вместе двадцать четыре. Остальные разряды: у FIN один, у SYN два, у RST четыре.
|
||
|
||
Аргумент «снять флаги», в записи tcp_flags_unset, принимает список имён. Код превращает имена в число-маску, переворачивает её биты и накладывает на флаги подделки логическое «и»: выбранные разряды гаснут. Например, ACK это шестнадцать; перевернули, наложили на двадцать четыре, осталось восемь, это PSH. Неизвестное имя флага вызывает ошибку.
|
||
|
||
![[wiki-anim-video07-s18-flag-bits-and-mask.svg|Разряды флагов и маска снятия флага]]
|
||
|
||
Опыт: снимаем ACK. Остаётся один PSH. Подделка уходит с флагом PSH без ACK, видна во всех четырёх точках записи, и ни один из нужных нам счётчиков получателя не двигается, лишнего ответа тоже нет: пакет выброшен молча. Прикладная программа получила чистые двести пятьдесят байт, а игрушечный инспектор на флаги не смотрит и принял подделку за первый пакет.
|
||
|
||
Где подделку выбросили? Она видна у клиента, на входе и выходе роутера и у самого сервера, значит, роутеры флаги не проверяют, и выбросил её именно получатель.
|
||
|
||
Почему получатель молчит? По общим сведениям, пакет без флага ACK в уже установленном соединении ядро отбрасывает без ответа. Мы видели только, что счётчики не двинулись, а правило ядра сами не проверяли.
|
||
|
||
Опыт: снимаем PSH. Двадцать четыре минус восемь, остаётся шестнадцать: один ACK. Подделка уходит с флагом ACK без PSH, и в нашем тесте получатель её принимает: шестьсот восемьдесят чужих байт, клиент завис. Пакет с данными без PSH это обычный пакет с данными, PSH лишь просит передать их приложению быстрее. По общим сведениям, ACK несёт каждый пакет установленного соединения, а PSH нужен не всякому, поэтому снятие PSH условием не является.
|
||
|
||
Проверь себя. Снимаем сразу и ACK, и PSH. Чему равны флаги и что сделает получатель?
|
||
|
||
Двадцать четыре минус двадцать четыре, ноль: флагов нет совсем, поле флагов у подделки пустое. В нашем опыте получатель выбросил такой пакет молча, сервер получил чистые двести пятьдесят байт.
|
||
|
||
![[wiki-anim-video07-s18-three-flag-runs.svg|Три опыта со снятием флагов]]
|
||
|
||
Есть и обратный аргумент, «поднять флаги», в записи tcp_flags_set: он включает флаги. На проводе нашей лаборатории мы его не запускали: по общим сведениям, пакет с флагом сброса мог бы оборвать наше соединение. Прогон функций показал лишь, что в байтах пакета флаги меняются.
|
||
|
||
Итак. Подделка без флага ACK в нашем тесте выбрасывается получателем молча. Подделка без PSH принимается: PSH условием не является. Расчёт простой: у каждого флага свой разряд, а аргумент гасит выбранные. Это проверено только на нашем получателе и нашей модели.
|
||
|
||
|
||
## Старая метка времени: минус один терпится, минус два нет, будущее вредит
|
||
|
||
Шестое условие для получателя: старая метка времени. Что это за метка и почему получатель сравнивает её с прошлыми?
|
||
|
||
В пакетах TCP бывает опция метки времени с двумя числами. Первое, «часы отправителя», это показание часов в момент отправки. Второе, «эхо», это последнее значение часов собеседника, которое отправитель видел. Их латинские имена на экране. Опция есть, только если обе стороны договорились о ней при установлении соединения.
|
||
|
||
По общим сведениям, получатель запоминает последнюю метку собеседника и отбрасывает пакеты с более старой меткой: это защита от запоздавших копий. Игрушка, выдуманная: пришло письмо от четвёртого числа, а следом письмо от третьего, и третье выбрасывается как устаревшее.
|
||
|
||
Аргумент «сдвиг метки времени», в записи tcp_ts, прибавляет число только к первым четырём байтам данных опции, то есть к часам отправителя. Вторая половина остаётся как была. Если опции нет или данные не восемь байт, код пишет в журнал, что метки нет, и ничего не меняет.
|
||
|
||
Опыт, минус один. Перед подделкой клиент отправил пакет с некоторым значением метки, настоящее приветствие несёт то же. Подделка на единицу меньше: у предыдущего пакета и у приветствия метка кончается на восемьдесят семь, у подделки на восемьдесят шесть. Получатель её принял: шестьсот восемьдесят чужих байт, клиент завис.
|
||
|
||
Минус два: подделка отстаёт на две единицы, и получатель её отбросил. Сдвинулся счётчик, имя которого мы читаем как «старая метка в установленном соединении», поток чистый. Минус шестьсот тысяч тоже отброшена.
|
||
|
||
Что такое «единица» метки? В нашем тесте метка тикает раз в миллисекунду: между двумя повторами приветствия с разницей в двести миллисекунд она выросла примерно на двести единиц. Значит, «минус два» это отставание на две миллисекунды. Предыдущий пакет клиента ушёл в ту же миллисекунду, что и приветствие, поэтому граница оказалась здесь. Если между ними пройдёт больше времени, граница сдвинется: мы этого не мерили, и надёжнее брать большой сдвиг, например минус шестьсот тысяч.
|
||
|
||
![[wiki-anim-video07-s19-ts-option-bytes-and-unit.svg|Опция метки времени: байты, код и единица]]
|
||
|
||
Теперь метка из будущего, плюс шестьсот тысяч. Получатель подделку принял, и это только половина беды. Похоже, он запомнил метку из будущего, и собственные пакеты клиента с обычной меткой стали казаться ему старыми: по счётчикам сервера, правило отбросило пакеты клиента ещё семь раз. Метка из будущего портит само соединение.
|
||
|
||
А если метки времени вообще не договорены? Сервер с выключенными метками: заголовок клиента двадцать байт, без опций. Код пишет «метки нет» и подделку не меняет, получатель её принимает, шестьсот восемьдесят байт. А опция эм-дэ-пять и сдвиг подтверждения при выключенных метках по-прежнему отвергаются: эти условия на метках не держатся.
|
||
|
||
![[wiki-anim-video07-s19-ts-shift-outcomes.svg|Четыре сдвига метки времени и сервер без меток]]
|
||
|
||
Проверь себя. Метки времени в соединении не договорены, мы задали сдвиг метки времени минус пять. Что случится?
|
||
|
||
Ничего не изменится: код не найдёт опцию и напишет об этом в журнал. Подделка уйдёт как есть, и получатель её примет. Так и было в опыте с выключенными метками.
|
||
|
||
Итак. В нашем тесте получатель отбрасывает подделку с меткой, отстающей не меньше чем на две единицы, при нашем темпе часов в одну миллисекунду. Минус один терпится, метка из будущего портит соединение, а без договорённости о метках условие ничего не делает. Всё это на одном получателе и нашей модели.
|
||
|
||
|
||
## Что мы знаем к этому месту: шесть условий и подбор срока жизни
|
||
|
||
Остановимся и сложим то, что узнали: один способ работает на дороге, шесть на получателе.
|
||
|
||
![[wiki-anim-video07-s20-seven-conditions.svg|Семь условий: где остаётся подделка]]
|
||
|
||
Одно условие работает на дороге: срок жизни. Подделка умирает на роутере, до получателя не доходит, а у клиента приходит «время истекло». Число берётся как переходы плюс дельта и держится на двух догадках: о стартовом сроке сервера и о длине обратного пути. Если подбор отказывает и запасного числа нет, подделка остаётся со сроком шестьдесят четыре и доезжает.
|
||
|
||
Шесть условий работают на получателе: подделка доходит до него, а он выбрасывает её сам. Опция эм-дэ-пять: счётчик «подпись, которой не ждали». Испорченная сумма: счётчик ошибок суммы. Сдвиг номера в прошлое на длину подделки и больше: счётчик старых данных. Сдвиг подтверждения: ответ-вызов. Снятый ACK: молчаливый выброс, счётчиков нет. Метка, отстающая на две единицы и больше: счётчик старой метки.
|
||
|
||
Счётчики это следы, а не объяснения: мы знаем, что в опыте сдвинулся именно этот счётчик, а название читаем как причину. У номера вперёд свои следы: в окне приёма растёт счётчик очереди, за окном счётчик выхода за окно и приходит пустое подтверждение.
|
||
|
||
Заметим, чем различаются два вида условий. Срок жизни не пускает подделку даже до сервера, и получатель её не видит. Шесть условий пускают её до самого получателя, и там она отвергается. Инспектор нашей модели видит подделку в обоих случаях: он стоит в первом роутере, до проверки срока.
|
||
|
||
![[wiki-anim-video07-s20-where-accepted.svg|Где подделку принимают]]
|
||
|
||
Есть и места, где условие не срабатывает и подделка становится данными. Номер, оставляющий хоть один новый байт. Опция неверной длины. Малый сдвиг подтверждения, когда сервер уже говорил. Снятый PSH. Метка на единицу старее и метка из будущего. И отказ подбора срока. В большинстве этих случаев соединение виснет, а при сдвиге номера минус шестьсот семьдесят девять ответ приходит, но поток испорчен.
|
||
|
||
Проверь себя. Подделка ушла со сдвигом подтверждения минус тысяча, а сервер до этого ничего не отправлял. Дойдёт ли подделка до сервера и какой счётчик двинется?
|
||
|
||
Дойдёт: в нашем тесте роутеры сдвиг подтверждения не проверяют. А получатель её отвергнет, и двинется счётчик ответа-вызова. Это условие из таблицы, мы видели его в опыте: приложение получило чистые двести пятьдесят байт.
|
||
|
||
Слово «работает» в этой таблице значит одно: получатель отбросил подделку, а простой игрушечный инспектор принял её за первый пакет. Всё измерено на одном получателе, Linux, и на нашем инспекторе. О настоящем ТСПУ и других получателях мы не знаем ничего.
|
||
|
||
Дальше в выпуске два вопроса: что будет, если игрушечный инспектор строже и проверяет то же, что получатель, и что будет с другим получателем. Там же увидим всю таблицу на одном экране.
|
||
|
||
Итак. Подбор срока останавливает подделку на дороге, шесть условий заставляют получателя выбросить её самому. У каждого условия своя причина, видная в счётчиках, и свои ловушки, где подделка превращается в данные. Всё это относится к нашему получателю и нашей модели.
|
||
|
||
|
||
## Повтор и вопрос: чьё поведение мы измеряли
|
||
|
||
Коротко повторим, что было раньше в этом выпуске, чтобы эту часть можно было смотреть отдельно.
|
||
|
||
Подделка, в коде fake, это подложное письмо: программа отправляет рядом с настоящим пакетом ещё один, с теми же заголовками и другими байтами. У подделки нет вердикта, поэтому настоящий пакет идёт следом без изменений.
|
||
|
||
Подделка должна показаться нашей игрушечной модели инспектора настоящим первым пакетом с данными, быть отброшенной получателем и ничего больше не испортить. В нашем тесте подделка без условий принимается сервером вместо настоящих данных, и клиент зависает. Для этого есть условия: одни убивают подделку в пути, это срок жизни, другие портят пакет, чтобы получатель его отбросил: сумма, MD5, номера, ACK, метка времени.
|
||
|
||
Слово «сработало» у нас значит только «обошло нашу игрушку и нашего получателя». Настоящий ТСПУ мы не видели.
|
||
|
||
Вопрос сцены: чьё поведение мы на самом деле измеряли, и что изменится, если получатель будет другим?
|
||
|
||
Игрушка, пример выдуманный. Два почтовых отделения и одна и та же посылка с неверно написанным весом. Первое отделение такие посылки возвращает, второе доставляет не глядя.
|
||
|
||
Наше отделение это сервер на Linux версии шесть точка восемнадцать с настройками по умолчанию. Кроме одной: расчёт контрольной суммы силами интерфейса, по общим сведениям он называется offload, мы отключили. Что сервер сделал с подделкой, видно по счётчикам его ядра до и после опыта.
|
||
|
||
![[wiki-anim-video07-s21-kernel-counters.svg|Счётчики ядра получателя]]
|
||
|
||
Первый опыт, подделка с опцией MD5. В нашем тесте у сервера вырос на единицу счётчик, по названию он про опцию MD5, которой получатель не ждал.
|
||
|
||
Второй, подделка с испорченной контрольной суммой: вырос счётчик ошибок суммы, снова на единицу.
|
||
|
||
Третий, со старой меткой времени, на две единицы меньше положенной: вырос счётчик с именем PAWS. По общим сведениям, это проверка меток времени, которая отбрасывает старые пакеты. Во всех трёх опытах приложение сервера получило чистые двести пятьдесят байт настоящего приветствия. Названия счётчиков мы читаем как причины только по названию: проверено лишь то, что они сдвигаются в этих опытах.
|
||
|
||
![[wiki-anim-video07-s21-two-variations.svg|Две вариации получателя]]
|
||
|
||
Теперь две вариации получателя, которые мы проверили нарочно. Первая: на сервере выключены метки времени. Клиентские пакеты тогда не несут эту опцию, и условию «старая метка» не за что зацепиться. Программа пишет в журнал, что опции нет, и ничего не меняет. Подделка принята: приложение получило шестьсот восемьдесят байт, а клиент завис.
|
||
|
||
Проверь себя. Метки времени у сервера всё ещё выключены, а условие другое: опция MD5. Что будет с подделкой?
|
||
|
||
Её отбросят: MD5 с метками времени не связана. В нашем тесте счётчик MD5 снова вырос на единицу, а приложение получило чистые двести пятьдесят байт.
|
||
|
||
Вторая вариация: в одном опыте мы оставили offload включённым. Мы ожидали, что плохая сумма тогда не подействует. В нашем тесте подействовала: счётчик ошибок суммы вырос на единицу, приложение получило чистое приветствие. Почему, мы не выясняли.
|
||
|
||
Всё остальное мы не проверяли: Windows, BSD, macOS, встроенные системы, серверы за балансировщиками и промежуточные устройства. По общим сведениям, другой стек может не отбросить опцию MD5, для которой у него нет ключа, или не проверять метки времени; это предположения, а не результаты. Заданный вручную срок жизни от стека получателя не зависит, он зависит от пути, но на других стеках мы это не проверяли. А автоподбор срока опирается на начальный срок жизни сервера, и у разных систем он, по общим сведениям, разный.
|
||
|
||
Итак. Всё измеренное относится к одной связке: игрушечный инспектор и Linux шесть точка восемнадцать. У большинства условий в счётчиках получателя остаётся след, но не у всех: подделка со сроком жизни один до сервера не доходит, а подделка без флага ACK выбрасывается молча. Выключенные метки времени отключают одно условие, но не остальные.
|
||
|
||
|
||
## Игрушки построже: условие, причину которого видно, не работает
|
||
|
||
Сначала честная плашка. Инспектор в этой сцене игрушка нашего проекта, мы построили её сами, и слово «сработало» значит «обошло игрушку».
|
||
|
||
Вопрос сцены: что будет с условиями, если игрушка станет внимательнее?
|
||
|
||
Игрушка, пример выдуманный. Сортировщик видит на подложной посылке неверно написанный вес. Он знает, что адресат такую посылку вернёт, поэтому откладывает её, не читая, и берёт следующую, настоящую. Подделка не сработала: причина, по которой посылку не примут, видна и сортировщику.
|
||
|
||
![[wiki-anim-video07-s22-toy-first-packet.svg|Игрушка: первый пакет решает]]
|
||
|
||
Теперь наша игрушка. Она стоит на входе в первый роутер, раньше проверки срока жизни, и видит даже пакеты, которым осталось жить один шаг. Она следит за потоком клиента к серверу. Первый пакет с данными, который она приняла во внимание, решает за всё соединение. Если внутри блокируемое имя, этот пакет и все следующие данные клиента выбрасываются. Если приветствие без имени, соединение разрешено, и дальше игрушка не смотрит.
|
||
|
||
Опорные опыты. Без подделки игрушка находит имя в первом пакете и выбрасывает его вместе с повторами: сервер получил ноль байт. Без игрушки тот же запрос доходит: двести пятьдесят байт и ответ.
|
||
|
||
![[wiki-anim-video07-s22-strict-toys-matrix.svg|Матрица условий и игрушек]]
|
||
|
||
У игрушки есть ещё четыре строгих варианта. Каждый не принимает во внимание пакеты определённого вида. Первый, «проверяющий сумму», игнорирует пакеты с неверной контрольной суммой. Против подделки с плохой суммой в его журнале запись «пакет с неверной суммой не учтён». Первым пакетом с данными для него стало настоящее приветствие, он нашёл в нём имя и выбросил. Сервер получил ноль байт.
|
||
|
||
Второй, «отсеивающий MD5», игнорирует пакеты с опцией MD5. Против подделки с MD5 исход тот же: подделка не учтена, имя найдено, ноль байт.
|
||
|
||
Третий, «следящий за номером», ждёт определённый номер первого байта. У подделки номер на десять тысяч меньше, игрушка её игнорирует, исход тот же.
|
||
|
||
Четвёртый, «требующий ACK», игнорирует пакеты без флага ACK. Подделка без этого флага игнорирована, сервер получил ноль байт.
|
||
|
||
Проверим, что дело в совпадении причины. MD5 против проверяющего сумму: игрушка принимает подделку за первый пакет и разрешает соединение, сервер получил чистые двести пятьдесят байт. Плохая сумма против отсеивающего MD5: то же. Старая метка времени против следящего за номером: то же. И срок жизни один против всех четырёх: все четыре приняли такую подделку за первый пакет.
|
||
|
||
Игрушки построены нами, каждую мы научили видеть ровно одну причину. Поэтому правило верно по построению: это не открытие о настоящих инспекторах, а проверка простой мысли, что условие помогает там, где причину не видят. Старая метка времени прошла следящего за номером; остальных трёх мы с ней не пробовали, по устройству игрушек она должна пройти и их, но это счёт, а не запуск. Чтобы видеть срок жизни или метку времени, игрушке нужно знать длину пути или состояние меток у получателя.
|
||
|
||
Проверь себя. Игрушка требует ACK, а у подделки испорчена контрольная сумма. Что решит игрушка?
|
||
|
||
Флаг ACK у подделки на месте, игрушка примет её за первый пакет, имени в нём нет, и она разрешит соединение. А плохую сумму отбросит сервер. Это счёт по устройству игрушки: такую пару мы не запускали.
|
||
|
||
Итак. Условие, причину которого видит игрушка, против неё не работает: сумму видит проверяющий сумму, MD5 отсеивающий MD5, номер следящий за номером, ACK требующий ACK. Срок жизни приняли все четыре. Каков настоящий инспектор, мы не знаем.
|
||
|
||
|
||
## Матрица условий: семь строк, четыре графы
|
||
|
||
Соберём всё в одну таблицу, по строке на условие.
|
||
|
||
Вопрос сцены: что мы знаем про каждое условие? Для каждого четыре ответа. Что меняется в пакете. Где подделка останавливается. Что получил сервер и что показали его счётчики. И какая из наших игрушек умеет такую подделку отсеять.
|
||
|
||
Игрушка, пример выдуманный: анкета с четырьмя графами. Заполняем её по строке.
|
||
|
||
![[wiki-anim-video07-s23-where-fake-stops.svg|Где подделка остановилась]]
|
||
|
||
Строка первая, срок жизни. В пакете меняется одно число в заголовке IP: срок жизни равен единице. В нашем тесте подделка видна на клиенте и при входе в роутер, и больше нигде: роутер вернул сообщение об ошибке. Сервер получил чистые двести пятьдесят байт. Игрушки подделку видят, она доходит до роутера раньше, чем тот её убьёт, но ни одна из четырёх не находит в ней причины отбросить и принимает её за первый пакет с данными.
|
||
|
||
Строка вторая, опция MD5. Заголовок TCP вырос с тридцати двух до пятидесяти двух байт: добавилась опция с шестнадцатью случайными байтами и выравнивание нулями. Подделка доехала до сервера и там отброшена, вырос счётчик MD5. Отсеять её умеет игрушка, отсеивающая MD5.
|
||
|
||
Строка третья, плохая контрольная сумма. Испорчено одно поле, остальное в порядке. Подделка доехала, сервер отбросил, вырос счётчик ошибок суммы. Отсеять её умеет проверяющий сумму.
|
||
|
||
Строка четвёртая, плохой номер. Первый байт подделки на десять тысяч раньше ожидаемого, она целиком лежит в прошлом. Сервер ответил пометкой «дубликат», это по названию счётчика, а приложение получило чистое приветствие. Отсеять умеет следящий за номером. Условие в том, что сдвиг назад не меньше длины подделки, то есть шестисот восьмидесяти: при минус шестьсот семьдесят девять последний байт подделки ложится на первый ожидаемый байт и портит поток.
|
||
|
||
Строка пятая, плохое подтверждение. Номер ACK сдвинут на тысячу назад. В нашем тесте сервер отбросил подделку, вырос счётчик проверочных подтверждений. Игрушки для этого условия нет: ни одна не проверяет номер подтверждения. И оговорка: так, пока сервер сам ничего не отправил. Когда сервер отправил клиенту пять тысяч байт, а клиент их подтвердил, сдвиг на тысячу подделку уже не отбрасывал.
|
||
|
||
Строка шестая, снятый флаг ACK. Остался один PSH. Сервер отбросил молча, счётчик не вырос, приложение чисто. Отсеять умеет требующий ACK. А снять только PSH не помогает: такая подделка принята.
|
||
|
||
Строка седьмая, старая метка времени. От значения отнято шестьсот тысяч. Вырос счётчик PAWS, приложение чисто. Из игрушек проверена одна, следящий за номером, и подделку она не отсеяла. Две оговорки: минус один сервер принял, а метка из будущего принимается и портит соединение.
|
||
|
||
![[wiki-anim-video07-s23-trace-toy-caveat.svg|След, игрушка и оговорка]]
|
||
|
||
Как читать таблицу. Подделка либо умирает в пути, это срок жизни, либо доезжает и умирает у получателя, это остальные шесть. Первой группе нужно знать расстояние, второй нужен определённый получатель.
|
||
|
||
Проверь себя. Допустим, сдвиг номера минус сто. Сколько байт подделки ляжет на номера настоящего приветствия?
|
||
|
||
Пятьсот восемьдесят: шестьсот восемьдесят минус сто. Мы видели два края: при минус шестьсот семьдесят девять на настоящие номера ложится один байт, при минус один шестьсот семьдесят девять. Середина это счёт по этим краям, такого запуска мы не делали.
|
||
|
||
Итак. Семь условий, две группы: одна подделка умирает на роутере, шесть умирают у получателя. Пять из шести у получателя оставляют след в счётчиках сервера, снятый ACK не оставляет никакого. Против четырёх условий у нас есть игрушка, которая их отсеивает, а для срока жизни, подтверждения и метки времени такой игрушки нет.
|
||
|
||
|
||
## Ловушки: условие бьёт не туда, не по той версии или не срабатывает вовсе
|
||
|
||
Условия подделки могут и навредить. Вопрос сцены: где условие бьёт не по подделке, а по настоящему пакету, или вообще ничего не делает?
|
||
|
||
Вспомним игрушку из выпуска «multisplit: резать на части»: курьер, которому велели ставить на каждую посылку штамп «доставить до первого поста». Он поставил, и ни одна посылка не дошла.
|
||
|
||
![[wiki-anim-video07-s24-multisplit-pktmod-ttl1.svg|Ловушки: срок 1 бьёт по настоящим]]
|
||
|
||
Первая ловушка, та же самая. У multisplit нет своих поддельных аргументов, и всё, что вы пишете, получают настоящие части. В нашем тесте разрез midsld и срок жизни один: на клиенте двенадцать пакетов, шесть пар из ста четырёх и ста сорока шести байт, и у всех срок жизни один. За роутером не появилось ничего, приложение получило ноль байт.
|
||
|
||
Новое здесь одно. Игрушка приняла первую часть, в ней нет имени, и разрешила соединение. Игрушку обошли, а соединение всё равно мертво. Журнал чистый, ошибок нет.
|
||
|
||
Вторая ловушка. В программе есть простая стратегия, которая правит не копию, а сам проходящий пакет; назовём её правкой на месте, в коде это pktmod. Для неё срок жизни один значит, что настоящее приветствие уходит со сроком один. В нашем тесте на клиенте шесть пакетов по двести пятьдесят байт, оригинал и пять повторов, у всех срок жизни один, ни один не прошёл роутер, и без игрушки не прошёл бы. Игрушка стоит до проверки срока жизни, увидела имя и заблокировала. Приложение получило ноль байт.
|
||
|
||
У самой fake такой беды нет: она отправляет только копию, и условия ложатся только на неё.
|
||
|
||
![[wiki-anim-video07-s24-v6-and-error.svg|Ловушки: IPv6 и неверное значение]]
|
||
|
||
Третью ловушку мы разбирали раньше в этом выпуске: условие не той версии протокола молча не применяется. В нашем тесте срок жизни один на потоке IPv6 оставил предел пересылок шестьдесят четыре, подделка доехала, приложение приняло шестьсот восемьдесят байт, а клиент завис. Для IPv6 аргумент другой, ip6_ttl.
|
||
|
||
Четвёртая тоже знакома: неверное значение. Срок жизни двести пятьдесят шесть не помещается в байт. В нашем тесте программа написала ошибку, потом написала, что пакет пропускается без изменений. Подделки нет, настоящее приветствие ушло целым, игрушка его заблокировала, сервер получил ноль байт. Ошибка повторилась шесть раз, по разу на каждую отправку приветствия клиентом: оригинал и повторы.
|
||
|
||
Заметьте разницу. Ошибка в значении отключает стратегию для этого пакета: подделка не отправляется, настоящий пакет идёт как есть. Тихо пропускается только один случай: имя заготовки неизвестно, а у вызова стоит флаг optional; об этом в сцене про заготовки. И проверить, что условие вообще применилось, можно только по журналу или по захвату пакетов.
|
||
|
||
Проверь себя. Строка fake с заготовкой по умолчанию и сроком жизни минус один. Что будет с подделкой и с настоящим пакетом?
|
||
|
||
Допустимые значения от нуля до двухсот пятидесяти пяти, так что минус один это ошибка, как с двумястами пятьюдесятью шестью: подделки нет, настоящий пакет уходит целым, игрушка блокирует. Это по коду, живьём минус единицу мы не запускали.
|
||
|
||
Итак. Условие в multisplit и в pktmod бьёт по настоящим пакетам, условие не для той версии протокола молчит, а неверное значение отключает стратегию, и обычно мы узнаём об этом только по журналу.
|
||
|
||
|
||
## Фейк по умолчанию: шестьсот восемьдесят байт на бланке приветствия
|
||
|
||
Вопрос сцены: что лежит в шестистах восьмидесяти байтах подделки по умолчанию и чем она отличается от настоящего приветствия? Это тот самый образец из первой части, он называется fake_default_tls: блоб, готовый набор байтов с именем, и он всегда есть в программе. Слово «заготовка» в этом выпуске значит то же, что «образец»: блоб, из которого берутся байты подделки.
|
||
|
||
Игрушка, пример выдуманный. Чтобы подложное письмо не вызвало вопросов, оно должно быть на таком же бланке, с теми же графами. Графы бланка приветствия мы проходили в выпуске «Что видит наблюдатель», а как по ним ходит программа, в выпуске «Где резать: метки»: по длинам. Пройдём так же. Позиции считаем, как в том выпуске: от нуля, то есть байт номер сорок три это сорок четвёртый по счёту.
|
||
|
||
Первые пять байт, заголовок записи, говорят: дальше шестьсот семьдесят пять. Пять плюс шестьсот семьдесят пять и есть вся подделка, шестьсот восемьдесят. Дальше по длинам до идентификатора сессии: он на байте номер сорок три, длина тридцать два, ровно как у настоящего.
|
||
|
||
Первое отличие здесь: список наборов шифров. У настоящего приветствия он занимает шесть байт, это три набора. У подделки тридцать четыре байта, семнадцать наборов. Поэтому всё дальше сдвинуто на двадцать восемь байт.
|
||
|
||
На байте номер сто четырнадцать длина всех расширений: пятьсот шестьдесят четыре. Это метка extlen. Сто шестнадцать плюс пятьсот шестьдесят четыре: шестьсот восемьдесят. Длины сошлись до последнего байта.
|
||
|
||
Первое расширение, как у настоящего, имя сервера. Его данные начинаются на байте сто двадцать, это метка sniext. Внутри длина списка двадцать, тип имени ноль и длина имени семнадцать. Само имя занимает байты со сто двадцать пятого по сто сорок первый: www.microsoft.com. Это метка host.
|
||
|
||
Сравним с настоящим приветствием из наших опытов. У него метки восемьдесят шесть, девяносто два и девяносто семь, а имя www.example.com. У подделки сто четырнадцать, сто двадцать и сто двадцать пять: на двадцать восемь больше, и это целиком из-за списка шифров. А вся подделка длиннее настоящего приветствия на четыреста тридцать байт: двадцать восемь из них в шифрах, остальные четыреста два в расширениях. Расширения подделки занимают пятьсот шестьдесят четыре байта, у настоящего приветствия сто шестьдесят два.
|
||
|
||
![[wiki-anim-video07-s25-form-and-lengths.svg|Бланк подделки по длинам]]
|
||
|
||
После имени идут ещё шестнадцать расширений. Самое длинное, двести восемьдесят один байт, названо по типу encrypted client hello. В подделке это просто байты на своём месте, а что они значили бы для настоящего оборудования, мы не знаем.
|
||
|
||
Сама заготовка всегда одна и та же. Она записана в программе готовым массивом и создаётся раньше, чем программа читает вашу командную строку. В нашем тесте в двух отдельных запусках содержимое совпало до байта. Руководство называет её приветствием Firefox без Kyber, то есть, по общим сведениям, без постквантового ключа; с настоящим захватом Firefox мы её не сверяли.
|
||
|
||
Проверь себя. Допустим, имя в подделке длиннее на два байта: девятнадцать вместо семнадцати. Сколько байт во всей подделке?
|
||
|
||
Шестьсот восемьдесят два: поле длины каждого вложенного куска вырастет на два, и вся подделка тоже. В нашем тесте опция, которая подставляет своё имя, дала именно такую подделку; что ещё она умеет, разберёт выпуск «Другие слои».
|
||
|
||
![[wiki-anim-video07-s25-extensions-and-constant.svg|Расширения подделки и сама заготовка]]
|
||
|
||
Итак. Подделка по умолчанию это один аккуратный бланк приветствия на шестьсот восемьдесят байт: длины сходятся до последнего байта, имя www.microsoft.com лежит на байте сто двадцать пять. Годится ли она против настоящего инспектора, мы не знаем.
|
||
|
||
|
||
## Другие заготовки и откуда берутся байты: HTTP, QUIC, файл, запись в строке
|
||
|
||
Вопрос сцены: какие ещё заготовки есть и как подставить свою?
|
||
|
||
Игрушка, пример выдуманный. У каждого учреждения свой бланк: подложное письмо должно быть на бланке того, куда оно идёт. Так и здесь: для HTTP нужна заготовка-запрос, для QUIC заготовка-датаграмма.
|
||
|
||
Вторая заготовка, fake_default_http: запрос GET к корню сайта, с заголовком Host www.iana.org и строкой браузера Firefox, всего двести шестьдесят три байта.
|
||
|
||
В нашем тесте порт восемьдесят и настоящий запрос на восемьдесят один байт. С опцией MD5 подделка уходит первой с тем же номером и тем же идентификатором пакета, игрушка принимает её за первый пакет, а сервер получает чистые восемьдесят один байт. Без условия сервер принял двести шестьдесят три байта подделки, и клиент завис.
|
||
|
||
Третья заготовка, fake_default_quic: шестьсот двадцать байт, первый байт четыре-ноль в шестнадцатеричной записи, остальные нули. Настоящим начальным пакетом QUIC это не является, а что о нём подумает настоящий инспектор, мы не знаем.
|
||
|
||
QUIC идёт по UDP, а в UDP нет ни номеров, ни подтверждений. Условия уровня TCP к нему не применяются: код проверяет, есть ли TCP, и без него молчит. В нашем тесте MD5 на QUIC ничего не сделал, подделка дошла до приложения. Без условий, с двумя повторами подделки, сервер получил две по шестьсот двадцать байт и настоящий пакет в тысячу двести: у UDP нет потока, доставляется каждая датаграмма.
|
||
|
||
Подействовали два условия. Срок жизни один убил обе подделки на роутере, и до сервера дошёл только настоящий пакет. Плохая контрольная сумма: сервер подделку до приложения не донёс. Условия не про TCP, например заголовки расширения IPv6, по коду к UDP применяются; мы их здесь не проверяли, это выпуск «Другие слои».
|
||
|
||
![[wiki-anim-video07-s26-http-and-quic-blobs.svg|Заготовки HTTP и QUIC и их опыты]]
|
||
|
||
Теперь откуда берутся байты. Второй путь, свой файл: при запуске программа читает его в блоб. В нашем тесте файл с приветствием для iana.org на пятьсот семнадцать байт: подделка ушла в пятьсот семнадцать байт, сервер получил чистое приветствие.
|
||
|
||
Третий путь, байты прямо в строке: запись, которая начинается с нуля и буквы икс. В нашем тесте десять байт: начало записи TLS и ещё пять произвольных. Игрушка по журналу приняла их за приветствие и разрешила соединение. Четвёртый путь, строка, объявленная в файле Lua: в нашем тесте тринадцать байт, и исход тот же, сервер получил чистые двести пятьдесят байт.
|
||
|
||
Порядок поиска такой. Сначала запись с нулём и иксом, потом поле таблицы с данными о пакете, потом глобальная переменная Lua, и если нигде нет, то ошибка. Запись с нулём и иксом программа считает существующей всегда, даже с ошибкой в цифрах, поэтому флаг optional на неё не действует. Встроенное имя занять нельзя: если объявить fake_default_tls заново, программа не запустится и напишет, что имя повторяется. В нашем тесте игрушка после этого заблокировала настоящее приветствие.
|
||
|
||
Теперь про ошибки. Без аргумента blob и с неизвестным именем это ошибка, пакет уходит целым, игрушка блокирует, сервер получил ноль байт. С флагом optional и неизвестным именем программа пишет «блоб не найден, пропускаю» и тоже ничего не отправляет: в нашем тесте тот же исход. Флаг optional прячет ошибку, но блоб не заменяет.
|
||
|
||
Проверь себя. Блоб записан с нуля и икса и трёх цифр, да ещё стоит флаг optional. Что будет?
|
||
|
||
Ошибка про неверную шестнадцатеричную строку. Такая запись считается существующей всегда, поэтому optional не сработает, а три цифры нечётны. Это по коду и по прогону функций, с флагом мы не запускали.
|
||
|
||
![[wiki-anim-video07-s26-blob-sources-and-errors.svg|Источники блоба и ошибки]]
|
||
|
||
Итак. Заготовки три: TLS, HTTP и QUIC; на QUIC в нашем тесте подействовали срок жизни и плохая сумма. Свою заготовку можно взять из файла, из записи в строке или из строки Lua.
|
||
|
||
|
||
## Большой фейк, повторы и длинное приветствие: куски, счёт и первый кусок
|
||
|
||
Вопрос сцены: что будет, если заготовка не помещается в один пакет, если попросить повторы, и если настоящее приветствие длиннее пакета?
|
||
|
||
Игрушка, пример выдуманный. Длинное письмо не лезет в один конверт, его раскладывают по нескольким, и на каждый ставят одну и ту же печать. Так будет и с условиями.
|
||
|
||
В нашем тесте заготовка на три тысячи байт, загруженная из файла, и условие MD5. Подделка ушла тремя пакетами: тысяча четыреста двадцать восемь байт, ещё столько же и сто сорок четыре. Проверим счёт. Два куска по тысяче четыреста двадцать восемь вместе дают две тысячи восемьсот пятьдесят шесть. Всего было три тысячи. Остаток сто сорок четыре, это третий кусок.
|
||
|
||
Откуда тысяча четыреста двадцать восемь. Размер сегмента в нашей лаборатории тысяча четыреста шестьдесят: это меньшее из двух чисел, которые стороны назвали друг другу в рукопожатии, как в выпуске «multisplit: резать на части». Из него вычитаются байты опций в заголовке: двенадцать на время и двадцать на MD5 с выравниванием, всего тридцать два. Тысяча четыреста шестьдесят минус тридцать два: тысяча четыреста двадцать восемь.
|
||
|
||
Номера идут подряд. Первый кусок с единицы. Второй с номера тысяча четыреста двадцать девять: единица плюс длина первого куска. Третий с номера две тысячи восемьсот пятьдесят семь: тысяча четыреста двадцать девять плюс тысяча четыреста двадцать восемь. У трёх пакетов одна и та же случайная печать MD5, потому что она выбирается один раз на вызов. Идентификаторы пакетов идут подряд, а у настоящего приветствия идентификатор первого куска подделки.
|
||
|
||
Код нарезки знаком по выпуску «multisplit: резать на части»: условия применяются один раз, до нарезки. Без условий, по прогону функций, те же три тысячи байт ложатся иначе: два куска по тысяче четыреста сорок восемь и остаток сто четыре.
|
||
|
||
![[wiki-anim-video07-s27-big-blob-slices.svg|Большая заготовка режется на куски]]
|
||
|
||
Теперь повторы. Если записать repeats равно трём, в журнале одна строка об отправке, а на проводе три побайтно одинаковых пакета: тот же идентификатор, та же печать, и за ними настоящее приветствие.
|
||
|
||
И наконец настоящее приветствие длиннее пакета. В нашем тесте тысяча четыреста восемьдесят четыре байта: ядро клиента отдаёт их двумя сегментами, а программа задерживает оба, собирает и проигрывает заново. Подробно это выпуск «Сборка и повторный проигрыш», нам важно одно: функция fake вызывается на каждый проигрываемый кусок, но действует только на первом. На втором в журнале запись «не действую на дальнейшие куски».
|
||
|
||
На проводе сначала подделка, шестьсот восемьдесят байт, потом первый кусок настоящего приветствия, тысяча четыреста сорок восемь, потом второй, тридцать шесть. У подделки только флаг ACK, без PSH: она копирует флаги первого куска, а у того PSH нет. Сервер получил тысячу четыреста восемьдесят четыре чистых байта, кусками тысяча четыреста сорок восемь и тридцать шесть. Подделка по размеру не подгоняется под то, что изображает.
|
||
|
||
Проверь себя. Заготовка в тысячу пятьсот байт, из условий только срок жизни один. Сколько пакетов и какого размера?
|
||
|
||
Два: тысяча четыреста сорок восемь и пятьдесят два. Срок жизни опций TCP не добавляет, значит, в заголовке только время, двенадцать байт. Это счёт по коду и по прогону функций, живой запуск мы не делали.
|
||
|
||
![[wiki-anim-video07-s27-repeats-and-replay.svg|Повторы и проигранное заново приветствие]]
|
||
|
||
Итак. Большая заготовка режется по размеру сегмента минус опции, условия лежат на каждом куске, повторы дают одинаковые копии, а перед проигранным заново приветствием подделка стоит только перед первым куском.
|
||
|
||
|
||
## В коде: функция `fake` строка за строкой
|
||
|
||
Мы видели, что fake делает на проводе. Теперь прочитаем код: около двадцати пяти строк в файле стратегий. Из выпуска «Читаем командную строку zapret2» мы помним, что стратегия это функция на Lua, найденная по имени. Она получает два аргумента: контекст, ручку для разговора с программой, и таблицу desync, где лежит всё о текущем пакете и аргументы вашего вызова. Устройство таблицы разбирает выпуск «Lua внутри программы».
|
||
|
||
Вопрос сцены: какие проверки проходит пакет, прежде чем fake отправит подделку?
|
||
|
||
Игрушка, пример выдуманный. Охранник на проходной с бланком из четырёх клеток: пропускает, только если отмечены все.
|
||
|
||
![[wiki-anim-video07-s28-fake-code-lines.svg|Функция fake строка за строкой]]
|
||
|
||
Первая строка тела. Назовём её выключателем встречного направления, в коде это direction_cutoff_opposite. По умолчанию fake работает только с исходящими пакетами: это задаёт аргумент dir, его значения out, in или any. Поэтому для этого соединения функция сразу отключает свой экземпляр для входящих. В журнале нашего теста это строка «экземпляр отключён для входящих».
|
||
|
||
Дальше одно условие из трёх клеток. Протокол TCP или UDP. Исходящий пакет. И вид данных известный, то есть не «пусто» и не «неизвестно»: так мы говорили в выпуске «Читаем командную строку zapret2», а в самой программе это тип нагрузки. Как программа его определяет, разбирает выпуск «Тип нагрузки и имя сайта». Чистый ACK и SYN под это не подходят, так что перед ними подделки нет.
|
||
|
||
Внутри четвёртая клетка: первый ли это проигрываемый кусок. У длинного приветствия, проигранного заново, да только у первого, а у остальных функция пишет в журнал и ничего не делает.
|
||
|
||
Следующие три строки: аргумент blob обязателен, без него ошибка. В нашем тесте настоящий пакет ушёл целым, и игрушка заблокировала его.
|
||
|
||
Потом флаг optional. Если он стоит и блоба с таким именем нет, функция пишет в журнал и выходит без ошибки. Порядок строк важен: проверка обязательного blob идёт раньше, поэтому optional без blob всё равно ошибка.
|
||
|
||
Дальше функция blob достаёт байты по правилам прошлой сцены. Следующие четыре строки нужны опции tls_mod: она подменяет имя в заготовке. В нашем тесте опция сработала и в потоке HTTP, заготовка TLS стала на два байта длиннее, а для UDP не изменилась. Сами опции разбирает выпуск «Другие слои».
|
||
|
||
Главная строка, отправка копии с подставленными данными, в коде rawsend_payload_segmented. Она берёт копию разобранного пакета, подставляет заготовку вместо данных, применяет условия к копии и отправляет, нарезая по размеру сегмента, если нужно. Оригинал в таблице при этом не меняется.
|
||
|
||
Движком мы называем часть программы на Си: она разбирает пакет, зовёт функции на Lua и читает их ответы. А потом функция просто кончается: ничего не возвращает. Для движка «нет результата» значит «пропустить», и настоящий пакет уходит следом. Порядок на проводе, по устройству движка, объясняется так: подделка отдана ядру внутри вызова, а оригинал движок отпускает, когда вернулись все экземпляры. В нашем тесте было сто десять запусков, где программа отправила подделку к настоящему приветствию в двести пятьдесят байт, и во всех ста десяти подделка шла первой. В ста опытах по IPv4 настоящий пакет шёл за ней с тем же идентификатором; в IPv6 такого поля нет.
|
||
|
||
Проверь себя. Строка fake с заготовкой по умолчанию и параметром dir равным in. Приходит исходящее приветствие. Сколько подделок?
|
||
|
||
Ни одной: клетка «исходящий пакет» не отмечена, функция даже не дойдёт до блоба. Это прогон функций, живой запуск мы не делали.
|
||
|
||
![[wiki-anim-video07-s28-fake-and-real-on-wire.svg|Подделка и настоящий пакет на проводе]]
|
||
|
||
Итак. Fake отключает встречное направление, проходит ворота, берёт заготовку, отправляет копию с условиями и возвращается ни с чем, поэтому настоящий пакет идёт следом.
|
||
|
||
|
||
## В коде: `apply_fooling` по порядку, что происходит ниже и карта автоподбора
|
||
|
||
Последняя строка fake отправляет копию с условиями. Вопрос сцены: как аргументы строки превращаются в изменения полей копии? Это делает функция, которую можно назвать применялкой условий, в коде apply_fooling: около ста пятидесяти строк, пройдём их по порядку.
|
||
|
||
Игрушка, пример выдуманный. Закройщик с карточкой заказа. Пункты он выполняет строго по порядку: сначала «спороть старые пуговицы», потом «пришить новые». Поменяй порядок, и новые спорол бы вместе со старыми.
|
||
|
||
Функция получает три вещи: таблицу desync, разобранную копию пакета и условия. Нет копии, она возьмёт живой пакет. Нет условий, возьмёт аргументы вызова. Все значения приходят текстом, а голое имя без значения означает «да», как в выпуске «Читаем командную строку zapret2».
|
||
|
||
Порядок зашит в код. Сначала всё для TCP: сдвиг номера последовательности, сдвиг номера подтверждения, какие флаги снять, какие добавить, удаление пустышек NOP, сдвиг метки времени, опция MD5 и перенос метки времени вперёд. Потом заголовки IPv6, потом срок жизни, и последней, если вы её назвали, ваша собственная функция. А badsum в этом списке нет: контрольную сумму портит уже сборка пакета.
|
||
|
||
Разберём одно условие целиком: tcp_seq. Если значение число, номер в копии складывается с ним по кругу, потому что хранится в четырёх байтах; если нет, ошибка. В нашем тесте минус десять тысяч дал на проводе подделку с номером на десять тысяч раньше настоящего.
|
||
|
||
Второе, tcp_md5. Если опция уже есть, только запись в журнал. Иначе в список добавляется запись «тип девятнадцать, данные»: ваши шестнадцатеричные цифры или шестнадцать случайных байт. Длина не проверяется. В нашем тесте записанные нами четыре цифры, ноль-ноль-один-один, дали опцию в четыре байта, сервер её не отбросил и принял подделку.
|
||
|
||
Порядок важен в трёх местах. Удаление пустышек NOP стоит перед MD5: они исчезают ещё до добавления опции, и заголовок получается короче, сорок восемь байт вместо пятидесяти двух. Перенос времени вперёд стоит после MD5, поэтому время встаёт перед всеми. У клиента Linux перед временем стоят две пустышки NOP, и они остаются на месте. А срок жизни и собственная функция последние: она видит уже изменённую копию и может изменить всё.
|
||
|
||
Что происходит ниже, уже не в этой функции. Как программа разбирает пакет на поля и собирает обратно, показывает выпуск «От байтов к таблице». Нам важно одно: копию собирает функция на Си, пересчитывает длины и суммы и пишет опции из списка, не больше сорока байт всего, с выравниванием нулями. Бывает, что опции не помещаются: две пустышки, три блока SACK, время и MD5 вместе дают пятьдесят шесть байт. SACK, по общим сведениям, опция, которой получатель сообщает о принятых кусках. По прогону функций сборка тогда отказывает, а стратегия не работает.
|
||
|
||
И последнее, badsum. Когда сумма уже посчитана верно, её меняют маской из случайного числа, которое не бывает нулём, поэтому испорченная сумма не окажется верной случайно. Готовый пакет получает отметку программы. Ядро отдаёт программе пакеты через очередь, и правило берёт только пакеты без отметки, поэтому собственные пакеты программы к ней не возвращаются. Как это устроено, расскажет выпуск «Внутри программы».
|
||
|
||
Проверь себя. Условия tcp_md5 и tcp_ts_up вместе. В каком порядке опции окажутся в заголовке?
|
||
|
||
Сначала время, потом две пустышки NOP, потом MD5, а в конце нули выравнивания: перенос стоит после добавления MD5. Это прогон функций, живой запуск мы не делали.
|
||
|
||
![[wiki-anim-video07-s29-apply-fooling-order.svg|Применялка условий по порядку]]
|
||
|
||
Автоподбор срока жизни мы разбирали раньше в этом выпуске, здесь только карта. Входящий срок жизни записывает движок на Си из первого пакета от сервера. Дальше в библиотеке четыре части: угадыватель начального числа, вычислитель со сдвигом и коридором, разборщик строки «сдвиг, от, до» и поиск числа внутри apply_fooling. Если подбор числа не дал, потому что входящего срока нет или правило знака сказало «пусто», берётся запасной ip_ttl, если он записан; если дал, подбор побеждает ip_ttl. Комбинацию «подбор не справился плюс ip_ttl» живьём мы не запускали, это по коду.
|
||
|
||
Руководство в разделах про BSD пишет, что правила, отдающие программе из входящих только SYN-ACK, FIN и RST, позволяют работать автоподбору. Есть и кэш по адресу сервера: он может подставить число из прошлого соединения. В коде он есть, а в нашей лаборатории мы его не гоняли.
|
||
|
||
![[wiki-anim-video07-s29-below-and-autottl-map.svg|Что ниже применялки и карта автоподбора]]
|
||
|
||
Итак. Применялка условий идёт по фиксированному порядку и меняет только копию. Ниже функция на Си пересчитывает суммы и пишет опции, и там же появляется испорченная сумма.
|
||
|
||
|
||
## Итог выпуска: путь фейка, семь условий, граница знаний и что дальше
|
||
|
||
Подведём итог выпуска «Фейк и его условия».
|
||
|
||
Вот путь одной строки. Мы записали fake с заготовкой по умолчанию и условием. Функция отключила встречное направление, прошла четыре клетки, взяла заготовку в шестьсот восемьдесят байт и сделала копию пакета. В копию подставила заготовку, применила условия по порядку, и дальше функция на Си пересчитала суммы и отправила копию. А сама функция вернулась ни с чем, так что настоящий пакет пошёл следом.
|
||
|
||
![[wiki-anim-video07-s30-path-and-example.svg|Путь одной строки fake]]
|
||
|
||
Семь условий, которые мы разобрали. Первое, срок жизни: подделка умирает на роутере, нужное число зависит от пути, а автоподбор считает его по первому пакету от сервера и может промолчать. Второе, опция MD5: отбрасывает получатель, видит отсеивающий MD5. Третье, плохая контрольная сумма: отбрасывает получатель, видит проверяющий сумму. Четвёртое, номер: сдвиг назад не меньше длины подделки.
|
||
|
||
Пятое, номер подтверждения: отбрасывается, пока сервер сам ничего не отправил; игрушки для него у нас нет. Шестое, снятый ACK: отбрасывается молча, а снять один PSH мало. Седьмое, старая метка времени: не меньше чем на две единицы, метка из будущего портит соединение, а при выключенных метках условие не работает.
|
||
|
||
Три предупреждения. В multisplit и в pktmod условия бьют по настоящим пакетам. Ошибка в значении отключает стратегию, а оригинал уходит целым. А на QUIC из проверенного подействовали только срок жизни и плохая сумма; условия TCP код молча не применяет.
|
||
|
||
Теперь о том, что мы показали. Код, который читали строка за строкой, и провод: в нашем тесте, на одном получателе, Linux шесть точка восемнадцать, и против игрушечных инспекторов проекта. Пять условий из семи оставляли след в счётчиках сервера; срок жизни до сервера не доходил, а снятый ACK сервер сбрасывал молча. И игрушка, видящая причину, на такую подделку не поддавалась.
|
||
|
||
И о том, чего мы не знаем. Про настоящий ТСПУ мы не знаем ничего. Мы не проверяли Windows, BSD, macOS, встроенные системы и промежуточные устройства перед сервером. Не проверяли несимметричные пути для автоподбора и память о сроке жизни сервера между соединениями. Плохое подтверждение пробовали только с пятью тысячами уже отправленных сервером байт. Не разбирали, что делают опции tls_mod и заголовки расширения IPv6: это выпуск «Другие слои». И роутер, который не отвечает или переписывает срок жизни, мы тоже не пробовали.
|
||
|
||
Что дальше. В этом выпуске подделка стояла одна, перед целым приветствием. В выпуске «Резать и подделывать вместе» подделки встают между настоящими кусками разрезанного приветствия: функции fakedsplit, fakeddisorder и hostfakesplit. Мы помним ловушку из этого выпуска: условия падали на настоящие части. По коду этих функций настоящим частям достаётся из условий только перенос метки времени вперёд. Как это устроено и что получается на проводе, разберём там.
|
||
|
||
![[wiki-anim-video07-s30-seven-conditions-and-limits.svg|Семь условий и граница знаний]]
|
||
|
||
Итак. Fake это один подложный пакет с условием: он должен выглядеть настоящим для инспектора, умереть до сервера или быть отброшенным им и ничего не испортить. Условий семь, у каждого свой след, кроме двух, и своя оговорка. И всё это мы проверили на нашей игрушке и на нашем Linux, а не на настоящем оборудовании.
|
||
|
||
## См. также
|
||
|
||
- [[06-peremeshat-disorder|Перемешать: disorder]] — предыдущее видео серии
|
||
- [[fake]] — справочная статья о функции
|
||
- [[ts-and-fooling]] — подмена полей пакета и условия
|
||
- [[blob]] — как объявлять данные для подделки
|
||
|
||
---
|
||
|
||
> [!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ
|
||
> При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование доступно в Forgejo: [исходник этой заметки](https://git2.zapret.moe/zapretdiscordyoutube/todo/src/branch/main/Zapret2/video/07-fake-i-ego-usloviya.md) · [скачать весь репозиторий одним zip-архивом](https://git2.zapret.moe/zapretdiscordyoutube/todo/archive/main.zip).
|