Разрешение проблем, связанных с пакетным фильтром PF [2008] (Часть 2)
Отладка правил
Если ваш набор правил блокирует конкретный протокол потому что вы не открыли нужный порт, это больше проблема стадии проектировки нежели баг в правилах. Но что если вы видите, что блокируется соединение для которого у вас есть пропускающее его правило?
Например ваш набор содержит правило
block in return-rst on $ext_if proto tcp from any to $ext_if port ssh
Но когда вы пытаетесь подсоединиться к TCP порту 22, соединение принимается! Похоже, что файрвол игнорирует ваше правило. Как и в сборке "паззлов", в этих случаях, когда с ними сталкиваешься первые несколько раз, существует простое логическое и, как правило, тривиальное объяснение.
Первое, вы должны проверить все те шаги, о которых говорилось ранее. К примеру, положим, что файрвол работает и содержит правило, приведённое выше. Маловероятно, что имеют место наши предыдущие опасения, но это легко проверить:
# pfctl -si | grep Status Status: Enabled for 4 days 14:03:13 Debug: Urgent
# pfctl -gsr | grep 'port = ssh' @14 block return-rst in on kue0 inet proto tcp from any to 62.65.145.30 port = ssh
Следующее, что мы имеем: принимаются соединения TCP на порт 22 на kue0. Можно подумать, что это и так очевидно, но нелишним будет проверить. Запустите tcpdump:
# tcpdump -nvvvi kue0 tcp and port 22 and dst 62.65.145.30
Теперь повторите SSH соединение. Вы должны увидеть пакеты из вашего соединения в выводе tcpdump. Возможно вы их не видите, а это может быть из за того, что соединение на самом деле не проходит через kue0, а проходит через другой интерфейс, что объясняет, почему не срабатывает правило. Или вы возможно соединяетесь с другим адресом. Если вкратце, то если вы не видите ssh пакеты, то pf их также не увидит возможно не может их заблокировать используя правило, приведённое в нашей задаче.
Но если вы видите пакеты с помощью tcpdump, pf их тоже "увидит" и будет их фильтровать. Следующим предположением будет то, что блокирующее правило должно не просто присутствовать в наборе (что мы уже выяснили), а быть последним совпадающим правилом для нужных пакетов. Если же это не последнее правило, очевидно, согласно этому не принимается решение о задержании пакетов.
В каких случаях правило может быть не последним совпадающим правилом? Возможны три причины:
А) правило не срабатывает, так как просмотр правил не доходит до нужного нам.
Ранее присутствующее правило также срабатывает и вызывает прекращение выполнения опцией `quick';
Б) обработка правила производится, но правило не срабатывает из за несовпадения отдельных критериев.
В) обработка правила производится, правило срабатывает, но обработка продолжается и последующие правила также срабатывают для пакета.
Чтобы отвергнуть эти три случая вы можете, глядя на загруженный набор правил мысленно представить себе обработку гипотетического TCP пакета, приходящего на интерфейс kue0 и порт 22. Выделите отлаживаемый блок. Начните обход с первого правила. Совпадает? Если да - пометьте его. Имеет ли оно опцию `quick'? Если так, то прекращаем обход. Если же нет, продолжаем со следующим правилом. Повторяйте проверку, до нахождения совпадения с опцией `quick' или достижения конца набора правил. Какое правило совпало последним? Если это не правило номер 14, вы нашли объяснение проблемы.
Обход правил вручную может показаться забавным, тем не менее, он, при наличии достаточного опыта может быть проделан достаточно быстро и большой степенью надёжности. Если набор достаточно большой, вы можете временно его сократить. Сохраните копию реального списка правил и удалите те записи, которые, на ваш взгляд, не повлияют на результат. Загрузите этот набор и повторите проверку. Если теперь соединение блокируется, следовательно, казавшиеся несвязанными с искомыми пакетами правила ответственны за случаи А либо Б. Добавляйте правила одно за другим, повторяя тест, до тех пор, пока не найдёте нужное. Если соединение всё еще пропускается после удаления не влияющих на него правил - повторите мысленный обход уменьшенного набора.
Другой метод, это использование способности pf вести логи для идентификации случаев А или С. Добавьте `log' ко всем правилам с `pass quick` перед 14ым правилом. Добавьте `log' ко всем правилам с `pass', стоящим после 14ого правила. Запустите tcpdump для интерфейса pflog0 и устанавливайте ssh соединение. Вы увидите, какие правила помимо 14ого срабатывают на ваших пакетах последним. Если в логе ничего нет, значит имеет место случай Б.
Отслеживание соединений через файрвол
Когда соединение проходит через файрвол, пакеты приходят на один интерфейс, а передаются наружу через второй. Ответы приходят на второй интерфейс, а уходят в первый. Соединения, таким образом, могут обрываться в каждом из этих четырёх случаев.
Первое, вы должны выяснить, в каком из четырёх случаев проблема. Если вы попытаетесь установить соединение, вы должны будете увидеть пакет TCP SYN на первом интерфейсе, используя tcpdump. Вы должны также увидеть тот же TCP SYN пакет на выходе со второго интерфейса. Если вы его не видите, следовательно, заключаем, что pf блокирует входящий пакет на первом интерфейсе, либо исходящий на втором.
Если же SYN посылка не блокируется, вы должны будете видеть SYN+ACK, приходящий на второй интерфейс и выходящий с первого. Если нет - pf блокирует SYN+ACK на каком-то интерфейсе.
Добавьте опцию `log' к правилам, которые должны пропускать SYN и SYN+ACK на обоих интерфейсах, также к правилам, которые должны их блокировать. Повторите попытку соединения и проверьте pflog. Он должен прояснить, в каком случае происходит блокировка и каким правилом.
Отладка состояний соединений
Наиболее распространённая причина блокирования пакетов pf состоит в том, что в наборе существует избыточное блокирующее правило. Соответствующее правило, срабатывающее по последнему совпадению, может быть найдено добавлением опции `log' во все потенциально влияющие на результат правила и прослушиванием интерфейса pflog.
В меньшем количестве случаев случается так, что pf "молча" отбрасывает пакеты основываясь не на правилах, и здесь добавление `log' во все правила не приведёт к попаданию сброшенных пакетов в pflog. Часто пакет почти, но не полностью подпадает под запись в таблице состояний (state entry).
Помните, что для каждого обрабатываемого пакета, пакетный фильтр производит просмотр таблицы состояний. Если найдена совпадающая запись, пакет немедленно пропускается, не вызывая для себя обработки набора правил.
Запись в таблице состояний содержит информацию, относящуюся к одному соединению.
Каждая запись имеет уникальный ключ. Этот ключ состоит из нескольких значений, которые ограничивают constant throughout время жизни соединения. Вот они:
* Тип адреса (Ipv4 или Ipv6) * Адрес источника * Адрес приёмника * Протокол (TCP UDP) * Порт источника * Порт приёмника
Этот ключ используется для всех пакетов, относящимися к одному и тому же соединению, и пакеты разных соединений будут всегда иметь разные ключи.
Когда опцией `keep state' из правила создаётся запись в таблице состояний, запись о соединении сохраняется с использованием ключа данного соединения. Важное ограничение для таблицы состояний - все ключи должны быть уникальными. Т.е. не может быть двух записей с одинаковыми ключами.
Возможно сразу не очевидно то, что те же два хоста не могут установить несколько сосуществующих соединений используя те же адреса, протоколы и порты, но это есть фундаментальное свойство как TCP так и UDP. Фактически, стеки TCP/IP всего лишь могут ассоциировать отдельные пакеты с их сокетами выполняя выборку, основанную на адресах и портах.
Даже если соединение закрыто, та же пара адресов и портов не может быть заново задействована сразу же. Сетевым оборудованием могут позднее доставляться повторно переданные пакеты, и если стеком TCP/IP получателя они будут ошибочно приняты за пакеты вновь созданного соединения, это помешает или вовсе разорвёт новое соединение. По этой причине оба хоста должны выждать определённый промежуток времени, называемый 2MSL ("двойное время жизни сегмента", "twice the maximum segment lifetime") перед тем, как снова иметь возможность использовать те же адреса и порты для нового соединения.
Вы можете пронаблюдать это свойство, вручную устанавливая несколько соединений к одному и тому же хосту. К примеру, имея веб-сервер работающий на 10.1.1.1 и порту 80, и дважды подсоединяясь с 10.2.2.2. используя nc:
$ nc -v 10.1.1.1 80 & nc -v 10.1.1.1 80 Connection to 10.1.1.1 80 port [tcp/www] succeeded! Connection to 10.1.1.1 80 port [tcp/www] succeeded!
Во то время, пока соединения открыты, можете с помощью netstat на клиенте или сервере вывести информацию об этих соединениях:
$ netstat -n | grep 10.1.1.1.80 tcp 0 0 10.2.2.6.28054 10.1.1.1.80 ESTABLISHED tcp 0 0 10.2.2.6.43204 10.1.1.1.80 ESTABLISHED
Как видите, клиент выбрал два различных (случайных) порта-источника, поэтому это не нарушает требования к уникальности ключей.
Вы можете указать nc использовать определённый порт-источник опцией -p:
$ nc -v -p 31234 10.1.1.1 80 & nc -v -p 31234 10.1.1.1 80 Connection to 10.1.1.1 80 port [tcp/www] succeeded! nc: bind failed: Address already in use
TCP/IP стек клиента предотвратил нарушение уникальности ключей. Некоторые редкие и ошибочные реализации TCP/IP стеков не отвечали этому правилу, и поэтому, как мы скоро увидим, pf заблокирует их соединения при нарушении уникальности ключей.
Вернёмся назад к тому месту, когда pf делает запрос в таблицу состояний, в то время когда пакет начинает отфильтровываться. Запрос состоит из двух шагов. Первый запрос делается для поиска вхождения в таблицу записи с ключом, соответствующим протоколу, адресам, и порту пакета. Поиск будет вестись для пакетов, идущих в любом направлениях. Предположим, что приведённый ниже пакет создал запись в таблице состояний:
incoming TCP from 10.2.2.2:28054 to 10.1.1.1:80
Запрос к таблице обнаружит следующие записи в таблице состояний:
incoming TCP from 10.2.2.2:28054 to 10.1.1.1:80 outgoing TCP from 10.1.1.1:80 to 10.2.2.2:28054
Запись в таблице включает информацию о направлении (входящий или исходящий) первого пакета, создавшего запись. Например следующие записи не вызовут совпадения:
outgoing TCP from 10.2.2.2:28054 to 10.1.1.1:80 incoming TCP from 10.1.1.1:80 to 10.2.2.2:28054
Причина этих ограничений неочевидна, но довольно проста. Представьте, что у вас всего один интерфейс с адресом 10.1.1.1, где веб-сервер осуществляет прослушивание порта 80. Когда клиент 10.2.2.2 подсоединяется, используя случайно выбранный исходящий порт 28054, первый пакет соединения приходит на ваш интерфейс и все ваши исходящие ответы должны будут идти с 10.1.1.1:80 на 10.2.2.2:28054. Вы же не будете пропускать исходящие пакеты с 10.2.2.2:28054 к 10.1.1.1:80, так как такие пакеты не имеют смысла.
Если ваш файрвол сконфигурирован для двух интерфейсов, то, наблюдая за пакетами, проходящими через него, вы увидите, что каждый пакет приходящий на первый интерфейс проходит наружу и через второй. Если вы создадите запись о состоянии, в которой начальный пакет прибывает на первый интерфейс, то эта запись не позволит такому же пакету покинуть второй интерфейс, потому что имеет неверное направление.
Когда попытка отыскать пакет среди записей в таблице состояний терпит неудачу, производится обход списка правил фильтра. Вы должны специально разрешить прохождение пакета наружу через второй интерфейс отдельным правилом. Наверняка вы используете `keep state' в этом правиле, чтобы вторая запись в таблице состояний охватывала всё соединение и на втором интерфейсе.
Вы можете удивиться, как же возможно создать вторую запись в таблице, если мы только что разъяснили, что записи должны иметь уникальные ключи. Объяснением здесь будет то, что запись также содержит информацию о направлении соединения, и комбинация этого с остальными данными должны быть уникальна.
Теперь мы также сможем объяснить разницу между свободным соединением и соединением, привязанным к интерфейсу. По умолчанию pf создаёт записи, которые не привязаны ни к какому интерфейсу. Поэтому, если вы разрешаете соединения на одном интерфейсе, пакеты относящиеся к соединению и подпадающие под запись в таблице (включая информацию о направлении пакета!) проходят через любой интерфейс. В простых инсталляциях со статической маршрутизацией это больше теоретические выкладки. В принципе, вы не должны видеть пакеты одного соединения, прибывающие через несколько интерфейсов и ответные пакеты, уходящие также на несколько интерфейсов. Однако при динамической маршрутизации такое возможное. Вы можете привязать записи о состояниях к конкретному интерфейсу используя глобальную установку `set state-policy if-bound' или опцией для каждого правила `keep state (if-bound)'. Так вы будете уверены, что пакеты будут подпадать под записи только с интерфейса, который эти записи создал.
Если используется интерфейсы для туннелей, то одно и то же соединение проходит через файрвол несколько раз. Например первый пакет соединения сначала может пройти через интерфейс A, затем через B, потом С и наконец покинуть нас через интерфейс D. Обычно пакеты будут инкапсулированы на интерфейсах A и D и декапсулируется на B и C, поэтому pf видит пакеты разных протоколов и вы можете создать 4 различных записи в таблице состояний. Без инкапсуляции пакет будет неизменен на всех четырёх интерфейсах и вы не сможете использовать некоторые возможности, как трансляцию адреса или модуляцию номера tcp последовательности, потому что это приведёт к появлению в таблице состояний конфликтующих ключей. До тех пор, пока у вас не будет законченной установки включающей интерфейсы с туннелированием и отлаженными ошибкми вида 'pf: src_tree insert failed', вы не сможете считать свою инталяцию достаточно успешной. Вернёмся к запросу к таблице состояний, производящемуся для каждого пакета перед проверкой правил. Запрос должен вернуть единственную запись с подходящим ключом, либо не вернуть ничего. Если запрос ничего не возвращает, производится обход списка правил.
Если же запись найдена, вторым шагом для пакетов TCP, перед тем как они начнут считаться принадлежащими конкретному соединению и подвергнутся фильтрации, будет проверка номера последовательности.
Есть большое количество TCP атак, в которых атакующий пытается управлять соединением между двумя хостами. В большинстве случаев, атакующий не находится на пути маршрутов между хостами, поэтому не может прослушать легитимные пакеты пересылаемые хостами. Однако он может отсылать пакеты любому из хостов, имитирующие пакеты его собеседника, путём спуфинга("spoofing") - подделки адреса отправителя. Целью атакующего может являться предотвращение возможности создания соединений между хостами или обрыв уже установленных соединений (чтобы вызвать отказ в обслуживании) или для создания вредоносной загрузки на соединения.
Для успешной атаки атакующему необходимо верно "угадать" несколько параметров соединения, таких как адрес/порт источника и приёмника. И для широко распространённых протоколов это может быть не так уж сложно, как может показаться. Если атакующий знает адреса хостов и один из портов ( поскольку речь идёт о распространённом сервисе), ему будет нужно только "угадать" один порт. Даже если клиент использует по-настоящему случайный порт-источник (что на самом деле не всегда верно), атакующему нужно всего лишь перебрать 65536 портов за короткий промежуток времени. (В большинстве случаев даже (65536-1024) портов, т.е. только непривилегированные порты - прим. переводчика))
Но вот что по настоящему трудно угадать для нападающего, так это верный номер последовательности (и его подтверждение). Если оба хоста выбирают начальный номер последовательности случайным образом (или вы используете модуляцию номера последовательности для хостов, которые имеют "слабый" генератор ISN (Initial Sequence Number)), то атакующему не удастся подобрать соответствующее значение в нужный момент соединения.
Во время существования валидного TCP соединения номера последовательностей (и подтверждения) для отдельных пакетов изменяются согласно определенным правилам.
Например, если хост посылает некоторый сегмент данных и его получатель подтвердил приём, не должно быть причины, по которой отправитель должен послать данные сегмента еще раз. Но, фактически, попытка перезаписать части информации уже полученные хостом не является нарушением протокола TCP, хотя и может быть разновидностью атаки.
pf использует правила, чтобы определить наименьший диапазон для легитимных номеров последовательностей. В общем случае, pf может точно определить подлинность только 30000 из 4294967296 возможных номеров последовательности в любой момент соедиения. Только если номер последовательности и подтверждение входит в это окно, pf убедится в том, что пакет легитимный и пропустит его.
Если во время проведения запроса к таблице состояний найдена подходящая запись, на следующем шаге номера последовательностей пакетов, сохранённых в таблице, проверяются на вхождение в диапазон возможных значений. В случае неудачи при сравнении pf сгенерирует сообщение 'BAD state' и отбросит пакет без вычисления набора правил. Есть две причины, по которым может не произойти сравнения с правилами: почти наверняка будет являтся ошибкой пропуск пакета, т.к. если вычисление набора приведёт к попаданию в правиле на опцию 'keep state' и pf не сможет вынести решение и создать новую запись потому что это приведёт к появлению кнфликтующих ключей в таблице.
Для того чтобы видеть и записывать в лог сообщения 'BAD state', вам необходимо включить отладочный режим используя команду:
$ pfctl -xm
Отладочные сообщения по умолчанию попадают на консоль,также syslogd записывает их в /var/log/messages. Ищите сообщения начинающиеся на 'pf':
pf: BAD state: TCP 192.168.1.10:20 192.168.1.10:20 192.168.1.200:64828 [lo=1185380879 high=1185380879 win=33304 modulator=0 wscale=1] [lo=1046638749 high=1046705357 win=33304 modulator=0 wscale=1] 4:4 A seq=1185380879 ack=1046638749 len=1448 ackskew=0 pkts=940:631 dir=out,fwd pf: State failure on: 1 |
Эти сообщения всегда идут парами. Первое сообщение показывает запись в таблице состояний в момент, когда пакет был заблокирован и номера последовательности пакета, который привел к ошибке. Вторая запись отображает условия, которые были нарушены.
В конце первого сообщения вы увидите, была ли создана запись состояния на входящий (dir=in) или исходящий (dir=out) пакет, и шел ли заблокированный пакет в том же напрвлении (dir=,fwd) или противоположном (dir=,rev) направлении.
Запись в таблице содержит три адреса: пары портов, два из которых всегда равны между собой, в случае если соединение не подверглось преобразованию nat,rdr или bnat. Для исходящих соединений источник выводится слева, а приёмник пакета - справа. Если исходящее соединение задействует преобразование адреса источника, пара в середине показывает источник после преобразования. Для входящих соединений источник находится справа в выводе, а адрес назначения посередине. Если входящее соединение подвергается преобразованию адреса назначения, пара ip/port слева показывает приёмник после проведённого преобразования. Этот формат соответствует выводу pfctl -ss, ч той лишь разницей, что pfctl показывает направление пакета используя стрелки.
В выводе вы можете видеть текущие номера последовательности у хостов в квадратных скобках. Так значение '4:4' означает, что соедиенение установлено полностью (меньшие значения более вероятны на этапе установления соединения, большие - к моменту закрытия соедиения). 'A' означает, что заблокированный пакет имел установленный флаг ACK (также как и в выводе флагов у tcpdump), далее идут значения номеров последовательностей (seq=) и (ack=) в заблокированных пакетах и длина полезной нагрузки пакета - длина данных (len=). askskew это часть внутреннего представления данных в таблице, задействующаяся только при значениях не равных нулю.
Запись 'pkts=930:631' обзначает, что с ней совпало 940 пакетов, шедших напрвлении, совпадающим с пакетом, вызвавшим создание данной записи, и 631 пакет в противоположном напрвлении. Эти счётчики будут особенно полезны при поиске проблем на этапе установления соединения, если один из них равен нулю, это будет противоречить вашему ожиданию того, что с данной записью совпадают пакеты, идущие в обоих напрвлениях.
Следующее сообщение собдержит список из одной или нескольких цифр. Каждая цифра представляет собой проверку, на которой произошла ошибка:
1. размер окна пакета превышает максимальный размер у получателя (seq + len > high) 2. пакет содержит уже переданные данные (seq< lo - win) 3. ackskew меньше минимального значения 4. ackskew больше максимального значения 5. то же, что и в (1), но с разницей (seq + len > high + win) 6. то же, что и в (2), но (seq < lo - maximum win)
К счастью, сообщения 'BAD state' не относятся к реальному повседневному трафику и проверка pf номера последовательности позволяет не столкнуться с большинством аномалий. Если вы видите эти сообещения появляющиеся нерегулярно и не замечаете большое число подвисших соединений, вы можете просто их игногрировать. В интернет работает множество реализаций TCP/IP и некоторые из них могут иногда генерировать ошибочные пакеты.
Однако этот класс проблем может быть легко диагностирован по появлению сообщений 'BAD state', появляющихся только в подобных случаях.
Создание записей состояний TCP по начальному SYN пакету.
В идеале, записи состояний должны создваться при появлении первого пакета SYN.
Вы можете принудительно включить использование этого правила, пользуясь принципом:
"Использовать опции 'flags S/SA' во всех правилах 'pass proto tcp keep state'"
Только начальные SYN пакеты (и только они) имеют установленный флаг SYN и сборшенный ACK. Когда применение опции 'keep state' привязывается только к начальным SYN пакетам, только эти пакеты будут создавать записи в таблице состояний. Таким образом любая существующая запись в таблице состояний будет произведена от начального SYN пакета.
Причиной создания записей только по начальным пакетам служит расширение протокола TCP под названием 'масштабирование окна' ("window scaling"), определённое в RFC1323. Поле заголовка TCP, используещееся для оповещения о размере принятых окон, слишком мало для сегодняшних высокоскоростных линий связи. Современные реализации TCP/IP предпочитают использование больших значений размера окна, чем может уместиться в существующем поле. Масштабирование размера окна означает, что все размеры окон, о которых известно от хоста-получателя, должны быть умножены на определённое значение, заданное получателем, а не взяты сами по себе. Для того, чтобы данная схема заработала, оба хоста должны поддерживать расширение и обозначить друг для друга свою возможность реализации его на этапе установления соединения ("handshake") используя опции TCP. Эти опции представлены только в начальных пакетах SYN и SYN+ACK. И только если каждый из этих пакетов содержит опцию, взаимосогласование будет успешным и размер окна всех последующих пакетов будет умножаться на коэффициент.
Если бы pf "не знал" об используемом масштабировании окна, бралось бы предоставляемое значение без коэфиициента, и вычисление размеров окна для приемлемых значений номеров последовательности производилось бы неверно. В типовом случае хосты в начале соединения предоставляют малые значения размеров окна и увеличивают их в процессе соединения. Не подозревающий о существовании факторов изменяющих размер окна, pf с некоторого момента начнет блокирование пакетов, потому что будет считать, что один их хостов пытается обойти предоставленный "собеседником" максимальный размер окна. Эффекты от этого могут быть более или менее заметны. Иногда, хосты отреагируют на потерю пакетов переходом в т.н. "loss recovery mode" ("режим повторной передачи") и будут анонсировать меньший размер окна. После того, как pf повторно передаст отброшенные в первый раз пакеты, размеры окон будут далее возрастать, до точки в которой pf снова начнёт их блокировать. Внешним проявлением может быть временное зависание соединений и низкая производительность. Возможно также полное зависание или сброс соединений по таймауту.
Но pf знает от возможности масштабирования окон и поддерживает такую возможность. Как бы то ни было, предпосылкой для создания записей в таблицу состояний по первым SYN пакетам будет то, что pf может ассоциировать первые два пакета соединения с записью в таблице. А так как полное согласование коэффициентов размера окон имеет место только в этих первых двух пакетах, то нет надёжного метода определить эти коэффициенты после согласования соединения.
В прошлом, масштабирование размеров окна использовалось не так широко, но ситуация быстро меняется. Только недавно в Linux включена эта опция по умолчанию. Если вы испытываете трудности с зависающими соединениями, особенно с некоторыми комбинациями хостов и видите сообщения 'BAD state' относящиеся к этим соединениям, проверьте, что вы действительно создаёте записи для таблицы состояний по первым пакетам соединения.
Вы можете определить, использует ли pf опцию масштабирования для соедиения из вывода pfctl:
Если присутствует запись 'wscale x' выведенная во второй строчке (даже если x равен нулю), pf зачит знает о том, что соединение использует масштабирование.
Другой простой метод для выявления проблем связанных с масштабированием это временное выключение поддержки масштабирования и потвторное воспроизведение ситуации. В OpenBSD использование масштабирования может управлятся опцией sysctl:
Подобные проблемы появляются когда вы создаёте записи в таблице состояний по пакетам, отличным от начальных SYN и используете опцию 'moulate state' либо трансляцию. В обоих случаях трансляция производится в начале соединения. Если первый пакет не транслирован, транчляция последующих обычно обескураживает принимающую сторону и приводит к тому, что посланные ответы блокируются pf с сообщением 'BAD state'.