История программных интерфейсов к iptables, ipchains и ipfw

Sep 04 2020

Я должен был сделать некоторые пустячную с iptablesправилами от Go в последнее время , и я заметил , как докер - х и coreos игровой обертки библиотека exec()из к iptablesкоманде и экрану царапать стандартный вывод. Мне это показалось удивительным.

В Python-land есть python-iptables :

Взаимодействие с iptables достигается за счет использования библиотек C iptables (libiptc, libxtables и расширения iptables), без вызова двоичного файла iptables и анализа его вывода.

Я предполагаю, что библиотеке Python разрешено загружать библиотеки C во время выполнения, а Go не может [edit: я ошибаюсь], но не может Go статически связываться с этими библиотеками? Потому что библиотека Go должна libxtables.aоткуда-то брать и т. Д.? (Я dpkg -Lиспользую соответствующие -devпакеты в Debian и вижу только .sos.)

Во всяком случае, согласно Netfilter FAQ :

4.5 Есть ли API C / C ++ для добавления / удаления правил?

К сожалению, ответ: нет.

Теперь вы можете подумать: «А как же libiptc?». Как неоднократно указывалось в списках рассылки, libiptc НИКОГДА не предназначался для использования в качестве общедоступного интерфейса. Мы не гарантируем стабильный интерфейс, и его планируется убрать в следующей инкарнации пакета linux.

Мы прекрасно понимаем, что у такого API есть фундаментальный недостаток, и мы работаем над улучшением этой ситуации. До тех пор рекомендуется либо использовать system (), либо открыть канал в stdin iptables-restore. Последний даст вам лучшую производительность фильтрации. libiptc слишком низкоуровневый, чтобы его можно было разумно использовать.

Хорошо, так что, возможно, вы не должны рассматривать эти библиотеки C как стабильные. Тогда я подумал: «Почему бы просто не поговорить с /procкем-то или что-то еще напрямую? Оказывается, в iptablesосновном не используется /procдля разговора с ядром (только для чтения таблицы имен?), И основной интерфейс на самом деле getsockopt()и setsockopt()на несвязанной розетке.

Это был еще один сюрприз! (Но, может быть, просто потому, что я незнаком с таким кодом, это кажется забавным способом взаимодействия с ядром.)

Я сделал немного археология назад от iptablesдо ipchainsдо ipfwи нашел IPFW человека страницу с 1997 , который заставил меня чувствовать себя немного лучше:

BUGS
       The  setsockopt(2)  interface  is a crock.  This should be
       put under /proc/sys/net/ipv4 and the world would be a bet-
       ter place.

Вы можете найти в другом месте, что «Утилита ipfw впервые появилась во FreeBSD 2.0» и

HISTORY
      Initially this utility was written for BSDI by:
       Daniel Boulet    <[email protected]>
      The FreeBSD version is written completely by:
       Ugen J.S.Antsilevich <[email protected]>
      while synopsis partially compatible with old one.

Вот версия FreeBSD 2.0 ipfw1994 года . Он по-прежнему использует setsockopt(), но, кажется, kvm_read()вместо getsockopt().

Мои вопросы

  • Пожалуйста, исправьте все, что я сказал неправильно выше :)
  • Почему они сделали выбор {get,set}sockopt()в первую очередь? Это необычно или просто для меня ново?
  • Почему его не изменили в какой-то момент? Как и что-то ниже, /procкак предлагает справочная страница 1997 года.
  • Почему они никогда не придумали стабильный / общедоступный интерфейс C iptables?

Мои последующие вопросы

"Не совсем, основной интерфейс - это семейство NETLINK_NETFILTER netlink"

Спасибо за указатель!

Я смотрю на https://git.netfilter.org/iptables/treeи, libipqкажется, использует только сокет netlink, если я что-то не упускаю.

$ ick "socket\("
include/libiptc/libiptc.h
158:int iptc_get_raw_socket(void);

include/libiptc/libip6tc.h
152:int ip6tc_get_raw_socket(void);

utils/nfsynproxy.c
129:    fd = socket(AF_INET, SOCK_STREAM, 0);

libiptc/libiptc.c
1312:   sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);

extensions/libxt_set.h
14: int res, sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);

libipq/libipq.c
223:                h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
225:                h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IP6_FW);

libxtables/xtables.c
881:    sockfd = socket(afinfo->family, SOCK_RAW, IPPROTO_RAW);

В libipqматериал выглядит «нормальным» для меня: сделать сокет, привязать его с SOCKADDR, sendto(), recvfrom()и т.д.

Также, xtables-monitorпохоже, используются некоторые mnl_socket_*обертки, которые выполняют работу с netlink.

Но я не могу понять libiptcи xtables- нет упоминания о netlink, и они даже не связывают сокет.

https://git.netfilter.org/iptables/tree/libxtables/xtables.c#n881

https://git.netfilter.org/iptables/tree/libiptc/libiptc.c#n1312

Есть и другие примеры, которые (кажется?) Не используют netlink, например этот код из отладчика mozilla rr: https://github.com/mozilla/rr/blob/master/src/test/netfilter.c

И netlink предназначен только для Linux, начиная с 2000 года (2.2), в то время как этот {get,set}sockopt()материал (по крайней мере) ipfwстарше и находится в области BSD.

Я не совсем уверен, в чем мой вопрос :) Как сокеты, не связанные с netlink, каким-то образом взаимодействуют с правильной частью ядра без даже bind()и sockaddr?

Ответы

StephenKitt Sep 04 2020 at 20:22

Я предполагаю, что библиотеке Python разрешено загружать библиотеки C во время выполнения, а Go не может

Go может использовать общие библиотеки C.

Оказывается, в iptablesосновном не используется /procдля общения с ядром (просто для чтения имен таблиц?), А основной интерфейс на самом деле getsockopt()и setsockopt().

Почему они сделали выбор {get,set}sockopt()в первую очередь? Это необычно или просто для меня ново?

(Ответ на этот вопрос ранее, неверно, что основной интерфейс netlink«s NETLINK_NETFILTERсемья.)

Вы правы, iptablesиспользует вызовы getsockoptнесвязанных сокетов соответствующего типа для взаимодействия с ядром, что в конечном итоге вызывает вызов кода ядраnetfilter . Я подозреваю, что это по историческим причинам ...

Между прочим, getsockoptи setsockoptработают с сокетами в различных состояниях, включая несвязанные, и они должны это делать, потому что они используются для установки параметров перед подключением. Соединение netfilter-via- sockoptфактически является побочным каналом.

Почему его не изменили в какой-то момент? Как и что-то ниже, /procкак предлагает справочная страница 1997 года.

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

Пока iptablesинструмент командной строки можно было использовать с небольшими усилиями, а создание лучшего интерфейса требовало бы гораздо больших усилий, внесение изменений было бы затруднительным. Также libiptcможно было бы создать правильно поддерживаемую библиотеку в стиле, но у iptablesразработчиков никогда не было особого стимула делать это.

Почему они никогда не придумали стабильный / общедоступный интерфейс C iptables?

Потому что «они» так и не дошли до этого. Теперь iptablesпостепенно сокращается в пользу nftables, которая включает в себя ряд библиотек , обеспечивающих доступ на разных уровнях абстракции ( libnftables, libnftnl, libnfnetlink). Это использует netlink, который был разработан как интерфейс на основе сокетов для передачи информации между ядром и пользовательским пространством и естественным образом подходит для таких инструментов, как nftablesiptables). В netlinkдокументации перечислены другие доступные семейства; они охватывают довольно много функций, размещенных в ядре. netlink.

Вы можете найти https://github.com/vishvananda/netlinkполезно для начала вождения iptablesс Go. (Для этого потребуется изрядно поработать, но это лучше, чем начинать с нуля.)