вторник, 8 декабря 2015 г.

if_enc(4) интерфейс во FreeBSD

Ни для кого, наверное, не новость уже, что IPSec во FreeBSD 11 включён в GENERIC ядро. Но вот if_enc(4) туда включать почему-то не стали. Модулем оно загружаться не умеет, а временами его функционал бывает довольно полезен. Поэтому, вслед за if_gre(4) и if_gif(4), я решил переделать и if_enc(4). Теперь в head/ его можно загружать из модуля, а так же выгружать после использования.
Пару слов для тех кто не знает, для чего он нужен. IPSec  может работать в туннельном и транспортном режимах. В туннельном режиме IPSec код инкапсулирует IP пакет внутрь IP-IP (другими словами - добавляет новый IP заголовок) и затем шифрует. При этом адреса во внешнем заголовке могут отличаться от адресов оригинальной (ещё незашифрованной) IP  дейтаграммы.
Внутри того же IPSec кода есть четыре места (два для входящих пакетов и два для исходящих), из которых происходит обращение к коду if_enc(4) драйвера. В этих местах у пользователя есть возможность посмотреть содержимое пакета, либо отфильтровать его файрволом. Про файрвол многие забывают, и именно поэтому при использовании if_enc(4) можно заблокировать весь IPSec трафик одним лишь поднятием интерфейса ifconfig enc0 up. Пока интерфейс не поднят, оверхеда он практически не создаёт, т.е. не выполняет никаких действий. Как только он поднят, пакеты начинают попадать в bpf(4) и могут быть просмотрены в tcpdump(8), а так же начинают отправляться в pfil(9), откуда, соответственно, попадают на рассмотрение пакетных фильтров.
Для исходящих пакетов в if_enc(4) пакет попадает перед тем как будет добавлен внешний IP заголовок и сразу после добавления. Если используется транспортный режим, то в обоих случаях пакет будет передан без изменений.
Для входящих пакетов, соответственно, сразу после расшифровки, но до того как внешний IP заголовок будет отброшен и после отбрасывания внешнего заголовка. Опять же, для транспортного режима пакет будет тот же самый.
В if_enc(4) имеется возможность контролировать, в каких из этих четырёх точкек мы хотим "видеть" пакеты. Управление осуществляется при помощи sysctl(8) net.enc.[in|out].ipsec_bpf_mask и net.enc.[in|out].ipsec_filter_mask.
Битовая маска определяет из каких мест IPSec кода следует обрабатывать пакеты. ipsec_bpf_mask отвечает за bpf(4), ipsec_filter_mask за pfil(9). Значение 1 включает обработку пакета "до" изменения, 2 - "после". Если хочется видеть оба, нужно использовать значение 3.
Внутри файрвола пакеты полученные из if_enc(4) выглядят как если бы они были получены или отправлены через интерфейс "enc0". И в зависимости от момента, когда вызван обработчик, проверять там нужно соответствующие адреса (внешнего заголовка или внутреннего).