Historia de las interfaces programáticas para iptables, ipchains e ipfw

Sep 04 2020

Tenía que hacer un poco de tocar el violín con iptableslas reglas de Ir recientemente, y me di cuenta tanto del cargador de muelle y de CoreOS las bibliotecas de derivadores exec()hacia el iptablesmando y la pantalla raspar la salida estándar. Esto me pareció sorprendente.

En Python-land, hay python-iptables :

La interoperabilidad con iptables se logra mediante el uso de las bibliotecas C de iptables (libiptc, libxtables y las extensiones de iptables), sin llamar al binario de iptables y analizar su salida.

Supongo que una biblioteca de Python puede cargar bibliotecas de C en tiempo de ejecución, y Go no puede [editar: estoy equivocado], pero ¿no podría Go enlazar estáticamente a esas bibliotecas? ¿Porque la biblioteca Go tendría que obtener, libxtables.aetc. de algún lugar? (Estoy dpkg -Lusando los -devpaquetes relevantes en Debian, y solo veo .sos.)

De todos modos, de acuerdo con las preguntas frecuentes de Netfilter :

4.5 ¿Existe una API C / C ++ para agregar / eliminar reglas?

Desafortunadamente, la respuesta es no.

Ahora podría pensar 'pero ¿qué pasa con libiptc?'. Como se ha señalado en numerosas ocasiones en las listas de correo, libiptc NUNCA debe utilizarse como interfaz pública. No garantizamos una interfaz estable, y se planea eliminarla en la próxima encarnación del paquete linux.

Somos conscientes de que existe una carencia fundamental de dicha API y estamos trabajando para mejorar esa situación. Hasta entonces, se recomienda utilizar system () o abrir una tubería en stdin de iptables-restore. Este último le dará un mejor rendimiento. libiptc es una capa demasiado baja para ser utilizada razonablemente de todos modos.

Ok, quizás no se supone que deba tratar esas bibliotecas C como estables. Entonces pensé "¿por qué no simplemente hablar con /proco lo que sea directamente?" Resulta que la iptablesmayoría de las veces no se usa /procpara hablar con el kernel (¿solo para leer los nombres de las tablas?), Y la interfaz principal está en realidad getsockopt()y setsockopt()en un socket no vinculado.

¡Esta fue otra sorpresa! (Pero tal vez sea solo porque no estoy familiarizado con este tipo de código que parece una forma divertida de interactuar con el kernel).

Hice un poco de arqueología al revés de iptablesa ipchainsa ipfwy encontré una página de manual de ipfw de 1997 que me hizo sentir un poco mejor:

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.

Puede encontrar en otra parte que "La utilidad ipfw apareció por primera vez en FreeBSD 2.0" y

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.

Aquí está la versión 2.0 de FreeBSD ipfwde 1994 . Todavía usa setsockopt(), pero parece usar en kvm_read()lugar de getsockopt().

Mis preguntas

  • Corrija todo lo que dije mal anteriormente :)
  • ¿Por qué eligieron {get,set}sockopt()en primer lugar? ¿Es poco común o simplemente nuevo para mí?
  • ¿Por qué no se cambió en algún momento? Me gusta algo debajo /proccomo sugiere la página de manual de 1997.
  • ¿Por qué nunca se les ocurrió una interfaz C pública / estable iptables?

Mis preguntas de seguimiento

"No del todo, la interfaz principal es la familia NETLINK_NETFILTER de netlink"

¡Gracias por este puntero!

Estoy mirando https://git.netfilter.org/iptables/treey solo libipqparece estar usando un conector netlink, a menos que me falte algo.

$ 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);

Las libipqmiradas de cosas "normales" a mí: hacer una toma, se unen con un sockaddr, sendto(), recvfrom(), etc.

También xtables-monitorparece estar usando algunos mnl_socket_*envoltorios que hacen cosas de netlink.

Pero no puedo entenderlo libiptcy xtables, sin mencionar netlink, y ni siquiera enlazan el socket.

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

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

Hay otros ejemplos que (¿parece que no?) Usan netlink, como este código del depurador rr de mozilla: https://github.com/mozilla/rr/blob/master/src/test/netfilter.c

Y netlink es solo para Linux, desde el año 2000 (2.2), mientras que estas {get,set}sockopt()cosas en (al menos) ipfwson más antiguas y en BSD-land.

Ya no estoy realmente seguro de cuál es mi pregunta :) ¿Cómo se comunican de alguna manera los sockets que no son de enlace de red con la cosa correcta del lado del kernel sin par bind()y un sockaddr?

Respuestas

StephenKitt Sep 04 2020 at 20:22

Supongo que una biblioteca de Python puede cargar bibliotecas de C en tiempo de ejecución, y Go no puede

Go puede usar bibliotecas C compartidas.

Resulta que la iptablesmayoría de las veces no se usa /procpara hablar con el kernel (¿solo para leer los nombres de las tablas?), Y la interfaz principal es en realidad getsockopt()y setsockopt().

¿Por qué eligieron {get,set}sockopt()en primer lugar? ¿Es poco común o simplemente nuevo para mí?

(Esta respuesta indicó anteriormente, incorrectamente, que la interfaz principal es netlinkla NETLINK_NETFILTERfamilia).

Tienes razón, iptablesusa llamadas a getsockoptsockets no enlazados del tipo apropiado para interactuar con el kernel, que termina llamando al código del kernelnetfilter . Sospecho que esto es por razones históricas ...

Por cierto, getsockopty setsockoptel trabajo en sockets en una variedad de estados, incluyendo no unido, y que tienen que porque están acostumbrados a opciones de configuración antes de conectar. La conexión netfilter-via- sockoptes efectivamente un canal lateral.

¿Por qué no se cambió en algún momento? Me gusta algo debajo /proccomo sugiere la página de manual de 1997.

Probablemente haya varias razones, pero una es sin duda la compatibilidad con versiones anteriores: en Linux, se supone que las interfaces de espacio de usuario nunca se rompen. Entonces, aunque podría proporcionarse una nueva interfaz, se mantendría la existente, lo que reduce el incentivo para mejorarla o reescribir programas que ya funcionan.

Siempre que la iptablesherramienta de línea de comandos se pueda utilizar sin demasiado esfuerzo, y proporcionar una mejor interfaz implique mucho más esfuerzo, el cambio será difícil. Una libiptcbiblioteca de estilo adecuadamente mantenida también habría sido una posibilidad, pero nunca hubo muchos incentivos para que los iptablesdesarrolladores hicieran eso.

¿Por qué nunca se les ocurrió una interfaz C pública / estable iptables?

Porque "ellos" nunca llegaron a hacerlo. Ahora iptablesse está eliminando gradualmente en favor de nftables, que incluye una serie de bibliotecas que dan acceso a diferentes niveles de abstracción ( libnftables, libnftnl, libnfnetlink). Este utiliza netlink, que fue diseñado como una interfaz basada en sockets para transferir información entre el kernel y el espacio de usuario, y es un ajuste natural para herramientas como nftables(y iptables). La netlinkdocumentación enumera las otras familias disponibles; estos cubren bastante funcionalidad alojada en el núcleo. netlink.

Podrías encontrar https://github.com/vishvananda/netlinkútil para empezar a conducir iptablesdesde Go. (Se necesita bastante trabajo para eso, pero es mejor que empezar desde cero).