Когда стоит вопрос выбора между проприетарным и открытым программным обеспечением, часто в пользу последнего приводят следующий аргумент: в случае необходимости всегда можно взять исходники и поправить их под себя, или исправить ошибку прямо сейчас, а не дожидаясь месяцами реакции от вендора. На самом деле этот аргумент весьма умозрительный — ну право же, кто в здравом уме возьмется за оптимизацию sql-планировщика, когда проще исправить сам sql запрос. Равно как и вряд-ли кто-то начнет искать и исправлять проблему в драйвере, когда быстрее и проще просто сменить железку. Баг-репорт отписать и то не всякий возьмется… Тем не менее, бывают случаи, когда именно наличие открытого кода позволяет избежать потенциальных убытков в случае возникновения непредвиденных проблем. Об одном из таких я и хочу сейчас расказать.

Этот вечер пятницы не предвещал никаких проблем. Впереди были очередные выходные, планов особых не было, предполагалось спокойно отдохнуть ;) Но реальные события оказались куда интереснее предполагаемых…

Первый звоночек зазвенел в субботу поздним вечером. Я уже спал, но пришлось подниматься и, чертыхаясь, идти разбираться, почему слег один из важных серверов. Их в кластере стояло 3+3 штук, и каждый тянет всю нагрузку своей тройки, так что выпадение одного ничем сервису не грозило. Но все же было крайне неприятно осознавать тот факт, что сервера, доселе спокойно принимающие суммарный входящий трафик в 10+К http-запросов в секунду, и имевшие (как казалось) ещё несколько-кратный запас по производительности, вдруг оказались не такими уж и устойчивыми. Что же, пока ребилдился raid1 и постгрес догонял репликацию, было время посмотреть на остальные сервера.

Стоит заранее объяснить, как устроен данный кластер. Сервера стоят в разных местах, два в Европе и четыре в USA. Они разбиты на тройки, каждая обслуживает свою группу IP (т.е. для каждой тройки один сервер в Европе и остальные два в USA). Трафик распределяется средствами anycast — на всех серверах тройки прописаны одни и те же IP, и поднята BGP-сессия с роутером. Если один сервер ложится, то его роутер перестает анонсировать его сеть в Internet и трафик автоматически уходит на оставшиеся сервера.

Смотреть особо было не на что. По данным мониторилки непосредственно перед падением был сильный всплеск входящего и исходящего трафика на оба европейских сервера (один из них и слег), причем если бэндвич вырос просто в два раза, то кол-во пакетов в секунду стало бо ?льшим уже раз в десять, причем в обе стороны. Т.е. пакеты были мелкие, и их было много (под 200к в секунду). На highload сервисах трафик просто-так сам по себе не меняется, а тут ещё и в таких размерах… Очень похоже на DDOS, не так ли? Не сказать что я сильно удивился, DDOS-ов разных видов мне пришлось повидать немало, и пока что, если сетевое оборудование у провайдеров позволяло доставлять трафик без потерь на сервера, их все удавалось успешно блокировать. Удивляло, правда, то, что всплеск трафика был только на европейских серверах, ведь если ботнет распределенный, то и трафик должен быть тоже распределен на весь кластер.

После ввода сервера в строй я запустил `top`, `nload` и стал мониторить загрузку. Ждать пришлось недолго, трафик скоро опять поднялся в два раза и ssh сессия начала ощутимо лагать. Налицо потери пакетов, `mtr -ni 0.1 8.8.8.8` данную гипотезу сразу и подтвердил, а `top -SH` указал, что дело именно в ядре ОС — обработчику входящих сетевых пакетов не хватает CPU. Что же, теперь понятно, почему завис сервер — потери пакетов ему смерти подобны:

У FreeBSD есть одна весьма неприятная особенность в сетевом стеке — он плохо масштабируется относительно кол-ва TCP-сессий :(. Увеличение кол-ва TCP-сессий в несколько раз приводит к непропорционально большему потреблению CPU. Пока сессий немного, то проблем нет, но начиная с нескольких десятков тысяч активных TCP-сессий обработчик входящих пакетов начинает испытывать нехватку CPU и ему приходится дропать пакеты. А это приводит к цепной реакции — из-за потерь пакетов активные TCP-сессии начинают медленно обслуживаться, их кол-во начинает немедленно расти, а с ним растет и нехватка CPU и уровень потерь пакетов.

Пока сервер окончательно не завис, срочно тушу BGP-сессию, и параллельно запускаю проверку потерь пакетов на том сервере, что принял на себя европейский трафик. Он имеет несколько более мощное железо — есть шансы, что в Штатах ничего плохого не случится. Но с проблемным сервером надо срочно решать. Первым делом выключаю keep-alive — TCP-сессии начнут завершаться раньше и в сумме их будет уже меньше. Тюнинг настроек сетевой карты занял не один десяток минут, проверяя наличие потерь пакетов каждый раз кратковременным поднятием BGP-сессии — пришлось оставить polling режим, но активировать idlepoll — теперь одно ядро процессора было занято исключительно сетевой картой, но зато потери пакетов прекратились.

Оставались еще непонятные моменты — например, кол-во TCP-сессий во время атаки и в обычном рабочем режиме практически не отличалось. Но вот что было совершенно непонятно, так это почему на штатовских серверах этой атаки не было видно вообще! Во время отключения европейских серверов на штатовские приходил только актуальный рабочий трафик, но не было никакого дополнительного! Хотя после возвращения трафика в Европу он какое-то время держался на рабочем уровне, а потом начинался очередной всплеск.

Время было за час ночи, потери пакетов, кажется, удалось прекратить, а с этими сетевыми странностями можно разбираться и на свежую голову. С такими мыслями я отправился обратно спать, хотя выспаться мне в ту ночь было не суждено. Через пару часов меня опять разбудили — в этот раз лежали уже оба европейских сервера ;(. Что добавило очередную странность в копилку — ведь время уже было позднее, и пик трафика был давно позади. Хотя, как для DDOS-атаки так вполне нормально, ведь большинство специалистов спит и заниматься атакой есть мало кому ). Оба сервера были вскоре запущены, но последующий мониторинг ситуации ничего нового не дал — атака в тот день больше не повторялась.

В воскресенье пришлось немного поработать ). Отдельный скрипт уже мониторил кол-во TCP-сессий и временно снимал трафик (т.е. переводил его в Штаты) в случае повышенной нагрузки, что уменьшало полученный ущерб. Пока что штатовские сервера работали без проблем, но все же надо было разобраться с этим трафиком и научиться его блокировать. В http-логах никаких аномалий не было, netstat и подобные утилиты тоже ничего подозрительного не показывали. Но раз мы видим повышение трафика на сетевой карте, то с этим уже можно работать, а на помощь придет верный tcpdump )

Пролистывать тонны дампов сетевых пакетов бывает непросто, но в этот раз долго искать не пришлось — среди обычного HTTP/HTTPS обмена было видно аномально много пустых TCP-пакетов, т.е. легальных пакетов с корректными IP и TCP заголовками, но без данных. При выключеном keep-alive пустых пакетов и так немало — три пустых на установление соединения, потом два пакета обмена данными, и потом опять пустые пакеты закрывают соединения. Ну и для HTTPS у нас ещё есть пакеты с данными для установки TLS-сессии. Но вот сейчас в дампе регулярно видны интенсивные обмены пустыми пакетами:

13:48:20.229921 IP 103.248.114.6.49467 > 88.208.9.69.80: Flags [.], ack 1, win 0, length 0
13:48:20.229925 IP 88.208.9.69.80 > 103.248.114.6.49467: Flags [.], ack 4294966738, win 8400, length 0
13:48:20.229927 IP 103.248.114.6.49467 > 88.208.9.69.80: Flags [.], ack 1, win 0, length 0
13:48:20.229931 IP 88.208.9.69.80 > 103.248.114.6.49467: Flags [.], ack 4294966738, win 8400, length 0
13:48:20.229933 IP 103.248.114.6.49467 > 88.208.9.69.80: Flags [.], ack 1, win 0, length 0
13:48:20.229937 IP 88.208.9.69.80 > 103.248.114.6.49467: Flags [.], ack 4294966738, win 8400, length 0
13:48:20.229939 IP 103.248.114.6.49467 > 88.208.9.69.80: Flags [.], ack 1, win 0, length 0

Выборочная проверка (`tcpdump -nc 1000 host 103.248.114.6 and tcp port 49467`) отдельных TCP-сессий показала, что таки да, по некоторым сессиям происходит очень интенсивный обмен пустыми TCP-пакетами. Причем почти все эти сессии были родом из Индии! Было и немного Саудовской Аравии с Кувейтом. Сложно сказать, что это за такой хитрый ботнет, да пока и не до этого. Пишу второй несложный скрипт, который каждую секунду запускает tcpdump на 30к пакетов и ищет среди них сессии, в которых кол-во последовательных обменов пустыми пакетами превышает указанный лимит, найденные IP немедленно блокируются. Результат не заставил себя ждать — при блокировке только пяти IP трафик тут же падает в два раза. Каждую минуту блокировался ещё один-два новых IP. Победа! ))

В понедельник с коллегами обсудили эту проблему, оказалось что все не так радужно (. Во первых, интенсивность блокировки новых IP нарастала — уже не в пик трафика скорость блокировки доходила до нескольких десятков штук в минуту. Во вторых, затронутыми оказались не только эти сервера, но и много других. Что характерно, все в Европе и все на FreeBSD. Стало ясно, что это никакая не DDOS-атака. Вот только что же это?…

Пока суть да дело, а заблокированные IP надо освободить. Вместо блокировки теперь дропались сами TCP-сессии (во FreeBSD для этого есть утилита tcpdrop). Это так же эффективно удерживало нагрузку под контролем. Заодно и keep-alive можно включить.

Пришлось опять брать в руки tcpdump и смотреть трафик дальше. Не буду детально описывать те часы, которые были потрачены на поиск аномалий и закономерностей, рассказ и так уже весьма затянулся ). TCP-сессии были разные. Были и полностью пустые:

dump1
06:07:58.753852 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [S], seq 3258188889, win 64240, options [mss 1452,nop,wscale 8,nop,nop,sackOK], length 0
06:07:58.753868 IP 88.208.9.8.80 > 122.167.126.199.56698: Flags [S.], seq 2165986257, ack 3258188890, win 8192, options [mss 1452,nop,wscale 6,sackOK,eol], length 0
06:07:58.906312 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [S], seq 3258188889, win 64240, options [mss 1452,nop,wscale 8,nop,nop,sackOK], length 0
06:07:58.906327 IP 88.208.9.8.80 > 122.167.126.199.56698: Flags [S.], seq 2165986257, ack 3258188890, win 8192, options [mss 1452,nop,wscale 6,sackOK,eol], length 0
06:07:59.059091 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [S], seq 3258188889, win 64240, options [mss 1452,nop,wscale 8,nop,nop,sackOK], length 0
06:07:59.059103 IP 88.208.9.8.80 > 122.167.126.199.56698: Flags [S.], seq 2165986257, ack 3258188890, win 8192, options [mss 1452,nop,wscale 6,sackOK,eol], length 0
06:07:59.112677 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 1, win 260, length 0
06:07:59.161950 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 1, win 260, options [nop,nop,sack 1 {0:1}], length 0
06:07:59.269749 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 1, win 260, length 0
06:07:59.313826 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 1, win 260, options [nop,nop,sack 1 {0:1}], length 0
06:08:09.313764 IP 88.208.9.8.80 > 122.167.126.199.56698: Flags [.], ack 1, win 136, length 0
06:08:09.569443 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 1, win 260, length 0
06:08:09.678113 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [F.], seq 1, ack 1, win 260, length 0
06:08:09.678132 IP 88.208.9.8.80 > 122.167.126.199.56698: Flags [.], ack 2, win 136, length 0
06:08:09.678206 IP 88.208.9.8.80 > 122.167.126.199.56698: Flags [F.], seq 1, ack 2, win 136, length 0
06:08:09.720977 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 1, win 260, length 0
06:08:09.872479 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 1, win 260, length 0
06:08:09.932997 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 2, win 260, length 0
06:08:10.024179 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 1, win 260, length 0
06:08:20.023725 IP 88.208.9.8.80 > 122.167.126.199.56698: Flags [.], ack 1, win 8712, length 0
06:08:20.279407 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 2, win 0, length 0
06:08:20.279412 IP 88.208.9.8.80 > 122.167.126.199.56698: Flags [.], ack 1, win 8712, length 0
06:08:20.430575 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 2, win 0, length 0
06:08:20.430581 IP 88.208.9.8.80 > 122.167.126.199.56698: Flags [.], ack 1, win 8712, length 0
06:08:20.534901 IP 122.167.126.199.56698 > 88.208.9.8.80: Flags [.], ack 2, win 0, length 0
06:08:20.534908 IP 88.208.9.8.80 > 122.167.126.199.56698: Flags [.], ack 1, win 8712, length 0


а были и с обменом данными, которые потом переходили в цикл обмена пустыми пакетами:

dump2
06:18:39.046506 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [S], seq 1608423399, win 14600, options [mss 1400,sackOK,TS val 2790685 ecr 0,nop,wscale 6], length 0
06:18:39.046525 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [S.], seq 3258835787, ack 1608423400, win 8192, options [mss 1400,nop,wscale 6,sackOK,TS val 2982841058 ecr 2790685], length 0
06:18:39.228192 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 1, win 229, options [nop,nop,TS val 2790704 ecr 2982841058], length 0
06:18:39.234683 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [P.], seq 1:512, ack 1, win 229, options [nop,nop,TS val 2790704 ecr 2982841058], length 511
06:18:39.235039 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [P.], seq 1:358, ack 512, win 130, options [nop,nop,TS val 2982841246 ecr 2790704], length 357
06:18:39.379057 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 1, win 229, options [nop,nop,TS val 2790704 ecr 2982841058], length 0
06:18:39.385527 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [P.], seq 1:512, ack 1, win 229, options [nop,nop,TS val 2790704 ecr 2982841058], length 511
06:18:39.408290 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 358, win 274, options [nop,nop,TS val 2790722 ecr 2982841246], length 0
06:18:39.408304 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [.], ack 512, win 130, options [nop,nop,TS val 2982841420 ecr 2790722], length 0
06:18:39.408305 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [F.], seq 512, ack 358, win 274, options [nop,nop,TS val 2790722 ecr 2982841246], length 0
06:18:39.408312 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [.], ack 513, win 130, options [nop,nop,TS val 2982841420 ecr 2790722], length 0
06:18:39.408319 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [F.], seq 358, ack 513, win 130, options [nop,nop,TS val 2982841420 ecr 2790722], length 0
06:18:39.536434 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [P.], seq 1:512, ack 1, win 229, options [nop,nop,TS val 2790704 ecr 2982841058], length 511
06:18:39.536442 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [F.], seq 358, ack 513, win 130, options [nop,nop,TS val 2982841548 ecr 2790722], length 0
06:18:39.580158 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 359, win 274, options [nop,nop,TS val 2790739 ecr 2982841420], length 0
06:18:39.580167 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 359, win 274, options [nop,nop,TS val 2790739 ecr 2982841420], length 0
06:18:39.687698 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [P.], seq 1:512, ack 1, win 229, options [nop,nop,TS val 2790704 ecr 2982841058], length 511
06:18:39.688031 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [P.], seq 1:358, ack 512, win 138, options [nop,nop,TS val 2982841058 ecr 2790704], length 357
06:18:39.712200 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 359, win 274, options [nop,nop,TS val 2790752 ecr 2982841420], length 0
06:18:39.712204 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [.], ack 512, win 138, options [nop,nop,TS val 2982841083 ecr 2790704], length 0
06:18:39.882468 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 359, win 274, options [nop,nop,TS val 2790769 ecr 2982841420], length 0
06:18:39.882476 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [.], ack 512, win 138, options [nop,nop,TS val 2982841253 ecr 2790704], length 0
06:18:39.884164 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 359, win 274, options [nop,nop,TS val 2790769 ecr 2982841420], length 0
06:18:39.884170 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [.], ack 512, win 138, options [nop,nop,TS val 2982841255 ecr 2790704], length 0
06:18:39.917773 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [P.], seq 1:358, ack 512, win 138, options [nop,nop,TS val 2982841289 ecr 2790704], length 357
06:18:40.033516 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 359, win 274, options [nop,nop,TS val 2790769 ecr 2982841420], length 0
06:18:40.033525 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [.], ack 512, win 138, options [nop,nop,TS val 2982841404 ecr 2790704], length 0
06:18:40.035244 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 359, win 274, options [nop,nop,TS val 2790769 ecr 2982841420], length 0
06:18:40.035248 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [.], ack 512, win 138, options [nop,nop,TS val 2982841406 ecr 2790704], length 0
06:18:40.082506 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 359, win 274, options [nop,nop,TS val 2790789 ecr 2982841420], length 0
06:18:40.082513 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [.], ack 512, win 138, options [nop,nop,TS val 2982841453 ecr 2790704], length 0
06:18:40.132575 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 359, win 274, options [nop,nop,TS val 2790794 ecr 2982841420], length 0
06:18:40.132583 IP 88.208.9.8.80 > 106.193.154.239.1223: Flags [.], ack 512, win 138, options [nop,nop,TS val 2982841503 ecr 2790704], length 0
06:18:40.142588 IP 106.193.154.239.1223 > 88.208.9.8.80: Flags [.], ack 359, win 274, options [nop,nop,TS val 2790795 ecr 2982841420], length 0


Но зацепка все же была. Перед уходом в цикл обмена пустыми пакетами от удаленной стороны приходил FIN пакет (пакет с флагом FIN сигнализирует, что данных больше не будет и сессию надо закрывать), иногда и не один, а бывало и RST пакет (пакет с флагом RST указывает, что сессия уже закрыта и больше не валидна). Что интересно, несмотря на наличие FIN и RST пакетов, потом бывало что на сервер приходили и пакеты с данными. Либо где-то настолько криво реализован TCP-стек, что маловероятно, либо где-то происходит грубое вмешательство в TCP-сессии, а вот это уже вполне вероятно (особенно этим любят баловаться мобильные операторы, не буду показывать пальцем). Вторую версию также подтверждал тот факт, что проверка по http-логу найденных зловредных TCP-сессий показала, что практически во всех них был задействован мобильный браузер, причем как Android, так и iOS.

Логично было предположить, FIN или RST пакет переводил TCP-сессию в закрытое состояние, в котором TCP-стек просто подтверждал получение пакетов. Было интересно, какое конкретно из TCP-состояний.

tcp_fsm.h
#define TCP_NSTATES     11

#define TCPS_CLOSED             0       /* closed */
#define TCPS_LISTEN             1       /* listening for connection */
#define TCPS_SYN_SENT           2       /* active, have sent syn */
#define TCPS_SYN_RECEIVED       3       /* have sent and received syn */
/* states < TCPS_ESTABLISHED are those where connections not established */
#define TCPS_ESTABLISHED        4       /* established */
#define TCPS_CLOSE_WAIT         5       /* rcvd fin, waiting for close */
/* states > TCPS_CLOSE_WAIT are those where user has closed */
#define TCPS_FIN_WAIT_1         6       /* have closed, sent fin */
#define TCPS_CLOSING            7       /* closed xchd FIN; await FIN ACK */
#define TCPS_LAST_ACK           8       /* had fin and close; await FIN ACK */
/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
#define TCPS_FIN_WAIT_2         9       /* have closed, fin is acked */
#define TCPS_TIME_WAIT          10      /* in 2*msl quiet wait after close */

Так себя ведет, и перед вызовом `tcpdrop` я добавил поиск удаляемой TCP-сесии в выводе `netstat -an`. Результат был немного обескураживающим — они все были в состоянии ESTABLISHED! Это уже было сильно похоже на баг — не может закрытая TCP-сессия перейти обратно в состояние ESTABLISHED, не предусмотрен такой вариант. Я немедленно начал проверять исходники и ядра и был обескуражен второй раз:

tp->t_state = TCPS_ESTABLISHED

В коде вызывается ровно два раза, и оба раза непосредственно перед этим проверяется текущее значение t_state — в одном случае оно равно TCPS_SYN_SENT (сервер отослал SYN пакет и получил подтверждение), а во втором это TCPS_SYN_RECEIVED (сервер получил SYN, отправил SYN/ACK и получил подтверждающий ACK). Вывод из это следует вполне конкретный — FIN и RST пакеты сервером игнорировались, и никакого бага в TCP-стеке нет (по крайней мере, бага с неправильным переходом из одного состояния в другое).

Все же было непонятно, зачем серверу отвечать на каждый полученный TCP пакет. Обычно в этом нет необходимости, и TCP-стек работает по другому — он принимает несколько пакетов, а потом отсылает одним пакетом подтверждение для всех сразу — так экономнее ). Пролить свет на ситуацию помогло внимательное изучение содержимого пакетов, в частности 32-х битных счетчиков TCP — sequence и acknowledgement. Поведение tcpdump по умолчанию — показывать разницу seq/ack между пакетами вместо абсолютных значений в данном случае сыграло дурную службу :).

Посмотрим внимательно на абсолютные значения:

16:03:21.931367 IP (tos 0x28, ttl 47, id 44771, offset 0, flags [DF], proto TCP (6), length 60)
46.153.19.182.54645 > 88.208.9.111.80: Flags [S], cksum 0x181c (correct), seq 3834615051, win 65535, options [mss 1460,sackOK,TS val 932840 ecr 0,nop,wscale 6], length 0
16:03:21.931387 IP (tos 0x0, ttl 64, id 1432, offset 0, flags [DF], proto TCP (6), length 60)
88.208.9.111.80 > 46.153.19.182.54645: Flags [S.], cksum 0xa4bc (incorrect -> 0xf9a4), seq 1594895211, ack 3834615052, win 8192, options [mss 1460,nop,wscale 6,sackOK,TS val 2509954639 ecr 932840], length 0
16:03:22.049434 IP (tos 0x28, ttl 47, id 44772, offset 0, flags [DF], proto TCP (6), length 52)
46.153.19.182.54645 > 88.208.9.111.80: Flags [.], cksum 0x430b (correct), seq 3834615052, ack 1594895212, win 1369, options [nop,nop,TS val 932852 ecr 2509954639], length 0
16:03:22.053697 IP (tos 0x28, ttl 47, id 44773, offset 0, flags [DF], proto TCP (6), length 40)
46.153.19.182.54645 > 88.208.9.111.80: Flags [R], cksum 0x93ba (correct), seq 211128292, win 1369, length 0
16:03:22.059913 IP (tos 0x28, ttl 48, id 0, offset 0, flags [DF], proto TCP (6), length 40)
46.153.19.182.54645 > 88.208.9.111.80: Flags [R.], cksum 0xa03f (correct), seq 0, ack 1594897965, win 0, length 0
16:03:22.060700 IP (tos 0x28, ttl 47, id 44774, offset 0, flags [DF], proto TCP (6), length 52)
46.153.19.182.54645 > 88.208.9.111.80: Flags [.], cksum 0x3a48 (correct), seq 3834615953, ack 1594896512, win 1410, options [nop,nop,TS val 932853 ecr 2509954639], length 0
16:03:22.060706 IP (tos 0x0, ttl 64, id 3974, offset 0, flags [DF], proto TCP (6), length 52)
88.208.9.111.80 > 46.153.19.182.54645: Flags [.], cksum 0xa4b4 (incorrect -> 0x475c), seq 1594895212, ack 3834615052, win 135, options [nop,nop,TS val 2509954768 ecr 932852], length 0

Первый пакет содержит seq 3834615051, в ответ от сервера ушел пакет seq 1594895211, ack 3834615052 (в out-ack ушел номер in-seq + 1).

Потом пришло пару RST пакетов, они нам не интересны.

А вот следующий пакет нам интересен — в нем записаны номера seq 3834615953, ack 1594896512. Оба эти номера существенно больше чем initial seq/ack, а это означает, что удаленная сторона уже отослала 3834615953-3834615052=901 байт и даже успела получить 1594896512-1594895212=1300 байт.

Разумеется, этих пакетов с данными мы не видим и не увидим — этот обмен был с MiTM системой. Но сервер то этого не знает. Он видит пакет с seq 3834615953, а следовательно, что ему не пришло 901 байт данных, и отсылает обратно пакет с последними валидными seq/ack номерами, ему известными — seq 1594895212, ack 3834615052. Удаленная сторона получает этот пакет, и в свою очередь рапортует, что у неё все отлично, 1300 байт данных получены успешно. Вот у нас и зацикливание.

Также становится понятным, почему штатовские сервера не видели этого трафика — он на самом деле был, но во много раз меньше — во столько же раз, во сколько пинг из Индии в Штаты больше, чем пинг из Индии в Европу.

Осталось, собственно, найти, как исправить этот баг. Опять берем исходники, интересующий нас код находится в файле tcp_input.c. Это было не особо сложно — первичной обработкой TCP-пакета занимается функция tcp_input() — в самом конце, если пакет проходит все проверки и TCP-соединение находится в состоянии ESTABLISHED — пакет передается на обработку в функцию tcp_do_segment().

Надо просто добавить ещё одну проверку — если ack-счетчик от удаленной стороны показывает, что она получила данные, которые сервер не отсылал — пакет надо игнорировать. Обрывать сразу соединение нельзя — иначе мы откроем злоумышленникам простой способ обрывать чужие TCP-соединения ).

Тестирование патча показало, что в TCP-трафике также присутствуют пакеты с нулевым значением ack — их игнорировать уже не надо. Итоговый патч занял три строчки (без учета комментариев):

+	if(SEQ_GT(th->th_ack, tp->snd_max) && th->th_ack != 0) {
+		goto dropunlock;
+	}

PR (problem report) разработчикам FreeBSD отправлен в тот же день.

P.S. Как с этой проблемой обстоят дела в Linux и Windows? А там все нормально, такие пакеты игнорируются (тестировал Windows 10 и Linux 3.10).
Поделиться с друзьями
-->

Комментарии (31)


  1. alexkunin
    16.06.2017 22:21
    +5

    Мой любимый пятничный жанр — расследование на боевых серверах, да еще с полным хэппи-эндом в виде установленной причины и небольшим ядерным порно на Си. Я не все понял, но я вам аплодирую. (Стоя. (На столе.))


    1. zuborg
      17.06.2017 08:23

      Право же, не стоит ))
      Рад что Вам понравилось, надеюсь, статья также попадется на глаза и другим пострадавшим пользователям FreeBSD, ведь эта индийская MiTM прокси до сих пор активна и отсылает неправильные TCP-пакеты…


  1. robert_ayrapetyan
    16.06.2017 22:36

    А в 11 воспроизводится?


    1. zuborg
      17.06.2017 08:24

      11.0 не тестировал. Мы её не используем — сильно падучая. Зависает под нагрузкой регулярно, бывает по несколько раз на день ;(


      1. click0
        17.06.2017 13:41

        Какая именно версия? Кернел паники были? PR открывали?


        1. zuborg
          17.06.2017 14:38

          Глючит 11.0, я ж написал, что она падучая. Паник не было, тихо, но надежно виснет, непредсказуемо, гарантированно воспроизвести не получается. PR не открывал, каюсь.


          1. robert_ayrapetyan
            17.06.2017 17:49

            Странно, у нас 11 на 16 боевых серверах (пиковые нагрузки до 250К pps на сервер), уже пол года, никаких зависаний. У вас кроме сети и ЦПУ на что еще нагрузки в приложении? Диски?


            1. zuborg
              17.06.2017 21:18

              Есть разные, виснет и на отдаче контента с дисков, и чисто сеть+проц (ну как «чисто», диски все равно используются, но уже не так интенсивно).
              Возможно, у нас сильно специфический конфиг ядра, он весьма отличается от GENERIC…

              Увы, x.0 релизы давно уже не выпускались сразу в продакшн качестве, та же 10-ка только к 10.3 стала более-менее безпроблемной, и все равно 9-ка часто работает лучше в одних и тех же условиях.
              Надеюсь, в 11.1 много пофиксят, если зависния не уйдут — придется разбираться и с ними )
              Также надеюсь, что в 11.1 добавят tcp_сс BBR, иначе в большинстве случаев будет прямой смысл ставить Linux.


          1. click0
            17.06.2017 20:30

            А вы все-таки гляньте 11.1, может и там этот же PR.


            1. zuborg
              17.06.2017 21:20

              Ребята из net-team и сами разберутся, проблема весьма специфическая, просто так её не заметить, так что шансов, что её уже исправили немного.


              1. click0
                17.06.2017 21:29

                На данный момент, в PR и freebsd-net@ нет активности по этому событию.


                1. click0
                  17.06.2017 21:33

                  Как ниже Иван заметил, нужна проверка в 11-HEAD или даже в 12-HEAD, чтоб коммитеры озаботились проблемой.


  1. miga
    17.06.2017 11:08
    +1

    Это что, получается, кто-то на удаленной стороне вбрасывал FIN/RST? Похоже на какой-то вариант блокировки сайтов.


    1. Loiqig
      17.06.2017 11:46

      И возможно даже на попытку обхода, если обмен продолжается то удалённая сторона у себя-то сессию тоже не закрыла или игнорировала FIN/RST. Возможно ошибка реализации, т.е. FIN/RST MiTM системы улетают только в одну сторону сервера, но не клиента.


  1. technic93
    17.06.2017 18:48

    А почему решили сервер на FreeBSD а не на линукс держать?


    1. zuborg
      17.06.2017 21:09

      Исторически у нас FreeBSD используется с самого начала, Linux ставили только по необходимости (если софт, например, работает под линуксом существенно лучше чем под фрей). Ещё несколько лет назад у FreeBSD с Linux был паритет — в каких-то задачах фря была существенно лучше (nginx+aio, например), в каких-то линукс.
      Сейчас FreeBSD весьма отстает, увы ((


      1. AVX
        18.06.2017 23:13

        Вы меня огорчаете :(
        Я только вот созрел, чтобы вплотную изучать FreeBSD, до сих пор считал, что там всё намного более стандартизировано и более строго, что-ли… Изучать всё равно буду, но уже не с таким энтузиазмом.
        Однако статья — супер! Читается легко и увлекательно, как детектив. Пуаро отдыхает!


        1. zuborg
          19.06.2017 01:57

          То, что во фре все более продуманно, чем в линуксе, это факт. И нет множества разных дистрибутивов, каждый со своими заморочками; значительное внимание уделяется обратной совместимости. Для хостинга это серьезный фактор, на самом деле.
          Лично я считаю, что FreeBSD незаслуженно обделена вниманием разработчиков, многие из которых считают, что раз в Linux скомпилировалось и запустилось, то дело сделано.
          Развиваться в таких условиях FreeBSD приходится куда труднее, и то, что ей это удается делать, это уже большое достижение.


      1. rumanzo
        19.06.2017 01:40

        А в чём отстаёт? Из того что вспомнится быстро


        1. zuborg
          19.06.2017 02:13
          +1

          Во первых, это плохая поддержка нового оборудования. Я даже на домашнем десктопе был вынужден перейти на Linux из-за невозможности использования двух мониторов на встроенной видяхе Intel на Broadwell, хотя до этого использовал только FreeBSD. Не без проблем, конечно, но как-то их решал, а вот вопрос с видеокартой был последним гвоздем. На серверах тоже не все гладко бывает, хотя проблемы возможны везде, в том числе и под Linux. Железо приходится подбирать всегда, промахи стоят денег )

          Второе — это вышеупомянутая проблема TCP-стека FreeBSD с большим кол-вом соединений, из-за которой на высокомощных серверах ресурс заканчивается гораздо быстрее, чем если они отдают тот же контент под Linux. Ставить просто больше серверов выйдет весьма дороже.

          Третий очень важный фактор — отсутствие поддержки TCP congestion control algorithm BBR, который в Linux появился в прошлом году. Когда-то его запилят и во фре, но терять в качестве обслуживания клиентов все это время нельзя.


  1. Ivan_83
    17.06.2017 21:10
    +1

    У FreeBSD есть одна весьма неприятная особенность в сетевом стеке — он плохо масштабируется относительно кол-ва TCP-сессий :(. Увеличение кол-ва TCP-сессий в несколько раз приводит к непропорционально большему потреблению CPU.

    Похоже вы просто не умеете его готовить. У Сысоева так всё нормально работало ещё в рамблере.

    Пока сессий немного, то проблем нет, но начиная с нескольких десятков тысяч активных TCP-сессий обработчик входящих пакетов начинает испытывать нехватку CPU и ему приходится дропать пакеты. А это приводит к цепной реакции — из-за потерь пакетов активные TCP-сессии начинают медленно обслуживаться, их кол-во начинает немедленно расти, а с ним растет и нехватка CPU и уровень потерь пакетов.

    Собственно указывает что тюнить вы не умеете.
    1. Сетевая карта с MSI-X аппаратно умеет разбрасывать конекты по прерываниям.
    2. Если карта унылая (ну типа там рылотек) то можно практически тоже самое делать через net.isr.dispatch=deferred, при этом схема будет такой: обработчик прерывания будет по быстрому сваливать пакеты в очередь netisr а та их сама раскладывать по ядрам. При этом ещё не плохо бы и сами очереди увеличить, чтобы за время между прерыванием/разгребанием не успевало переполнится. netstat в помощь.

    Там ещё десятки интересных крутилок в net.inet.tcp которые влияют на скорость работы TCP.

    пришлось оставить polling режим, но активировать idlepoll — теперь одно ядро процессора было занято исключительно сетевой картой

    Жуть. Я думал полинг с ядра давно выкинули. )

    пакет с флагом FIN сигнализирует, что данных больше не будет и сессию надо закрывать

    Пакет фин значит только что данных не будет/можно не ждать. Это не означает что их не надо отправлять.
    Собственно shutdown() на сокете умеет указывать такие штуки. Half closed соединения.
    На практике встречается редко, под вендой я видел как матроскасплиттер делал халфклосед при воспроизведении .ts с хттп линка.
    Типичная ошибка в том, что программист видит что получено 0 байт и EOF, он думает что соединение счастливо завершилось, а на самом деле клиент просто сделал shutdown(SEND) и ждёт данных от сервера.

    PR (problem report) разработчикам FreeBSD отправлен в тот же день: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=219991

    Далеко не факт что возьмут, как минимум оно не под 11, а лучше бы под 12.
    Да и по логике/идеологии работы тцп не факт что правильно делать как сделано в патче.
    Я добавил себя и народ с нетфликса кто тцп пилит вроде как.

    Как с этой проблемой обстоят дела в Linux и Windows?

    Когда то я был вынужден жить с глюкотиком в качестве домашнего роутера.
    Сидел я тогда под вендой 7, и у меня как раз бывало очень похожее поведение: комп с вендой вроде как давно уже даже ребутнулся а в торче видно что летят ретрансмиты по какой то давно не нужной TCP сессии. Какой то дос самого на себя получался.
    Разбираться не стал, и так было понятно что виноват глюкотик, а он мало того что под линухоам так ещё и исходники закрыты.

    2 technic93
    Потому что nginx пилят под фрю, а линух так, вторично.


    1. zuborg
      17.06.2017 23:01

      У FreeBSD есть одна весьма неприятная особенность в сетевом стеке — он плохо масштабируется относительно кол-ва TCP-сессий :(. Увеличение кол-ва TCP-сессий в несколько раз приводит к непропорционально большему потреблению CPU.


      У Сысоева так всё нормально работало ещё в рамблере.

      Честно говоря, не понимаю, каким образом тот факт, что у Игоря Сысоева все работало нормально, опровергает то, что нагрузка на CPU растет быстрее, чем нагрузка на сеть.

      Похоже вы просто не умеете его готовить.

      Вам виднее, конечно же ))
      Со своей стороны могу заметить, что я ещё в 2007 году запускал больше 1.2Гбита на apache!!! 1.2 (с 4-х обычных sata дисков, ssd не было), когда ещё слово nginx никто не слышал ) Сами понимаете, какие в то время были процессоры.
      https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=113885 — тоже пришлось в ядре покопаться, чтобы диски нормально отдавали контент.

      1. Сетевая карта с MSI-X аппаратно умеет разбрасывать конекты по прерываниям.
      2. Если карта унылая (ну типа там рылотек) то можно практически тоже самое делать через net.isr.dispatch=deferred, при этом схема будет такой: обработчик прерывания будет по быстрому сваливать пакеты в очередь netisr а та их сама раскладывать по ядрам. При этом ещё не плохо бы и сами очереди увеличить, чтобы за время между прерыванием/разгребанием не успевало переполнится. netstat в помощь.

      Приятно видеть, что Вы хорошо владеете теорией. Проблемы же, как обычно бывает, находятся в практике )

      Жуть. Я думал полинг с ядра давно выкинули. )

      Сейчас, когда меньше 8 ядер на сервере уже трудно найти, в поллинге особого смысла уже нет. Но все же не стоит забывать, что процессорные ядра занимаются не столько вычислениями, сколько обменом данными, а с этим есть определенные проблемы. За счет того, что все нужные данные находятся в одном и том же кеше одного ядра, в режиме поллинга на обработку условных 10М пакетов тратится 1с работы одного ядра, а в режиме MSI-X на десятке ядер это может быть уже суммарных 5с работы одного ядра.

      Далеко не факт что возьмут, как минимум оно не под 11, а лучше бы под 12

      Не вижу, как эти три строчки одной проверки могут вдруг оказаться несовместимыми с 11-кой или 12-кой )

      Потому что nginx пилят под фрю, а линух так, вторично.

      Исключая пул IO-тредов, да? )


      1. cadmi
        19.06.2017 20:04

        ещё в 2007 году запускал больше 1.2Гбита на apache!!! 1.2 (...), когда ещё слово nginx никто не слышал


        Вот это сейчас довольно обобщающе вышло :) у меня на файлопомойке nginx в 2004 году стоял :)


        1. zuborg
          20.06.2017 06:53

          У меня был не тот случай, когда можно было позволить использовать софт версии 0.1


  1. Ivan_83
    19.06.2017 02:01
    +2

    Так у Игоря она не росла так быстро, он тюнил.

    В 2007 году были кореквады, которые вполне могли осилить отдать гиг статики. Да собственно отдать гиг в пару конектов не проблема даже для более слабых систем.
    В то время юзали лайти, он тоже вполне резво отдавал, как минимум жрал меньше.

    Что у вас там на практике в sysctl.conf, показывайте уже.
    (покажи мне свой sysctl и я скажу кто ты :) )

    Вакуумное объяснение коня в вакууме.
    У вас там раздача чего то, оно через TSO замечательно оффлоадится, как при передаче с юзерспейса а особенно при работе sendfile().
    Более того, если бы полинг был перспективной темой тот же интел сидел бы на попе ровно и нифига не заморачивался с MSI-X, отложенными прерываниями, размерами очередей и прочими свистелками.

    Там бывает сильно всё переписывают внутри, кажется TCP одна из немногих горячих зон у нас в ядре.

    kqueue() по удачнее и по фичастее, чем epoll(), как следствие под линух костылей больше внутри спрятано.

    2 AVX
    В BSD не используется базарный стиль разработки, те архитектура внутри редко меняется настолько значительно чтобы сломать совместимость между модулями/драйверами и требовалось вмешательство.
    Есть геом и нетграф — они вообще имеют стабильный апи, те можно накорабять модуль и он без изменений будет работать и в 7.0 и в 11.1 (те 2008-2017), либо потребуется совсем косметика.
    Отчасти это из за академического подхода, отчасти потому что людей которые пилят каждый день/на постоянной основе не так уж много.
    Если смотреть на ситуацию в целом, то малое количество задействованных людей часто компенсируется более обдуманным подходом:
    — 10 раз обсудить решение потом протестить и закоммитить а не переписывать 100500 как в линухе местами (про skbuf)
    — там где не угнаться, типа писания дров, просто ассимилировать существующее: дрова юзби устройств (вебкамеры, планшеты, тюнеры) от линуха просто запихнуты в webcamd, профит в отладке просто фантастический
    — дрова видюх пока портируются руками с болью, но тоже хотят накорябать прослойку совместимости чтобы свести ручной труд портирования к минимуму
    — линуховые бинарники тоже в большинстве запускаются и работаю (флеш, скайп — из наиболее требовательных и одиозных примеров), через прослойку совместимости/эмуляции. (ни разу не пользовался)
    — ядерные фреймворки типа геом, нетграф и мак — одно удовольствие писать самому модули и пользоваться чужими, низкий порог входа для программиста (относительно даже линуха, не говоря о венде с её мегадровами)

    Минусы тоже есть:
    — не так много активного народу, некоторые вещи вообще заброшены потому что всем и так хорошо а на лучшее/фиксы нет времени/людей/желания
    — куча дедов, которым бывает трудно доказать что вот то говно мамонта надо было викинуть из системы лет 10 назад, они всегда отвечают что у них оно активно юзается и вообще они так привыкли. Бывает и что что то новое протащить трудно или объяснить где бага, хотя больше различия языковые и культурные.
    — не всегда в портах всё самых последних версий, иногда нужно и самому взять и обновить чтонить, благо часто достаточно в makefile порта сменить номер версии на актуальный и сделать make makesum а потом make и посмотреть что не работает, выкинуть старые/добавть новые патчи и поделится с сообществом через багтрекер
    — как бы связанно с предыдущим — софт не падает с неба, те разработчки какого нибудь опенцв/вайна/хорга/хфриирдп/глиб/фаерфокс знать ничего не знают про бсд и если хочешь всех плюшек то придётся дописывать работу со специфичными подсистемами самому (типа дописать чтобы понимало звуковую систему осс, с устройствами ввода работало через девд/имеющиеся дрова и тп), а не просто починить сборку. Ну либо сидеть и ждать когда кто то может быть это сделает.

    Впрочем всё описанное выше, оно если тебя и накроет то ещё не скоро, тк пока я не поставил фрю на десктоп многие пункты вообще не задевали никак, ибо на сервере редко больше сотни портов, и обычно они более-менее свежие, тк пользователей в кач сервера на порядки больше, чем пользователей на десктопе, да и портов на десктопе менее 500 сложно представить, иначе будет как то слишком аскетично / хардкорно в настройке.


    1. lieff
      19.06.2017 11:41

      А можете подсказать по возникшим проблемам во фре?
      1) Делал ptrace песочницу. Мне не хватило того что есть в линухе __WNOTHREAD и PTRACE_O_TRACESYSGOOD кажется. Для корректной работы с тредами и сигналами. Нашел systrace, но тоже не понял как корректно работать с тредами.

      2) По поводу kqueue, с точки зрения пользователя все здорово и удобно. А вот попытался я драйвер сделать, и у меня вопросы по методам накопления эвентов в ядре. Для epoll все просто, все дескрипторы преаллоцированы, для всего остального можно дропать. А при kqueue, если приложение залипнет на секунду, на две? Сколько и каких эвентов накапливать попадающих в фильтр? Там же переполнение может возникнуть.

      3) Пробелма не софсем про фрю, пробовал только на маке — dtrace буфера не гарантированно доставляются, особенно если начать уменьшать размер буфера. Соответственно я не могу на него рассчитывать, если что-то надо посчитать точно, или протрейсить вызовы, особенно если хочется развернуть то что по ссылкам структур, как это делает strace. Вот мой костыль, чтобы хотя бы аргумент execve() достать https://github.com/lieff/mac_exectrace


      1. Ivan_83
        20.06.2017 13:26

        Я не гуру по всем вопросам, лучше спрашивать в мыллистах.

        1. хз

        2. Там эвенты по дескрипторам (кроме таймеров и может ещё каких то фильтров), один дескриптор = один эвент на фильтр. Макс размер списка=макс количество дескрипторов, обычно больше 256к лимит дескрипторов никто не задирает, а это совсем крохи памяти. Так что потеряться ничего не должно.

        3. То что по ссылкам — должно копироваться при передаче в ядро (copyin) и копироваться из ядра (copyout), вроде как оно пишется в бинарный лог dtrace.
        Вопрос только в том, что родной дампер может это от туда не вытаскивать и тогда нужно писать свой.
        Опять я не экперт по этому механизму, скорее проходил мимо.
        По работе требовалось получить список всех файлов которые использовались при компеляции большого проекта.
        Я сделал так:
        ktrace -f /tmp/build.ktrace -d -i -t cn build-build.sh
        kdump -f /tmp/build.ktrace > /tmp/build.ktext
        parser -i /tmp/build.ktrace -o /tmp/build.log

        где, kdump вываливал текстовый лог /tmp/build.ktext который я юзал скорее для отладки своего parser.
        parser — моя фигулина, которая умеет парсить и текстовый лог с предыдущего шага и бинарный. (кажется текстовый я забросил и там есть косяки)
        Структуры меня не интересовали, меня интересовали nami конверсии имён и сисколы им предшествующие.
        Ещё оно отслеживает curdir, форки, наследование окружения и открытых дескрипторов… потому что в nami всё часто уходит от curdir, плюс всякие openat() для которых нужно знать какой путь привязан к дескриптору и прочие плюшки.

        2 zuborg
        Касательно TCP congestion control algorithm BBR.
        Проблема совсем не в его отсутствии, ибо она решается за пару дней — сс модули пишутся относительно легко, там микрофреймворк для них есть давно уже.
        Когда я пробовал впилить hybla то наткнулся на то, что оно на моём длинном линке (RTT ~70мс) с потерями работает хуже чем в линухе. Понятное дело я подумал что руки/моск у меня не те. Но тесты показали что тот же htcp и прочие сс алгоритмы с одинаковыми именами и крутилками в линухе работают лучше, сколько бы я не крутил крутилок сисцтл.
        Насколько помню занимался я этим на позапрошлые новогодние праздники на 10.х, вероятно с тех пор могло стать лучше ибо уже 11.1 почти, просто некогда тестировать снова.
        Надо будет посмотреть, может таки починили.

        Вместо newreno лучше ставь htcp, ощутимо быстрее.
        В моих условиях с 70+потери hybla работала совсем немного лучше, чем htcp. BBR пока не тестил, набы тоже поглядеть.
        А если невтерпёж — портируй BBR сам, если лицуха позволяет. Если нет — можно в порты положить.

        2 acmnu
        Есть ещё DragonFlyBSD, там вроде всё проще — Мэт нормальный мужик.
        Насчёт OpenBSD не в курсе, Тео тот ещё троль в инете, но как это сказывается на приёмке коммитов хз.


        1. zuborg
          20.06.2017 13:41

          BBR требует ко всему ещё и возможность отсылать пакеты через указанные интервалы, во фре такой функциональности пока нет, а в линуксе для корректной работы BBR надо дополнительно настраивать tc (traffic control)

          Да, htcp лучший CC на фре на текущий момент, использую его с самого начала.


          1. zuborg
            20.06.2017 13:45

            В линуксе htcp работает по другому, думаю, из-за более продвинутого алгоритма управления tcp-буфером. На фиксированном размере буфера скорость должна быть одинаковой.


        1. lieff
          20.06.2017 14:20

          Проблема в том, что один добавленный эвент в kqueue — на самом деле фильтр, который может генерировать много разных эвентов в сек. Например можно добавить:

              EV_SET(ke, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_*..., 0, NULL);
              EV_SET(ke + 1, SIGXXX, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
          

          Всего 2 эвента, но при этом начнут накапливаться пачки ke->filter == EVFILT_VNODE, ke->fflags & NOTE_* (и агрегировать их нельзя, т.к. у них разные ke->data) в перемешку с ke->filter == EVFILT_SIGNAL.

          Вот надежного решения этой проблемы я и не вижу.


  1. acmnu
    19.06.2017 10:28
    -1

    10 раз обсудить решение потом протестить и закоммитить а не переписывать 100500 как в линухе местами (про skbuf)

    У меня три знакомых были коммитерами ещё со времен 4ки. И все оценивают уровень бюрократии и бесполезных "согласований" как заоблачный. Как сказал, бывший начальник: "там все хуже чем в Debian". Хотя по мне куда уж хуже, блин.


    Думаю именно ваш спицифический подход к разработке отпугивает других разработчиков и уменьшает базу пользователей.