Histoire des interfaces programmatiques avec iptables, ipchains et ipfw

Sep 04 2020

J'ai dû faire quelques manipulations avec les iptablesrègles de Go récemment, et j'ai remarqué que les bibliothèques de wrapper de docker et de coreos étaient envoyéesexec() à la iptablescommande et à l'écran grattant la sortie standard. Cela me parut surprenant.

Dans Python-land, il y a python-iptables :

L'interopérabilité avec iptables est obtenue en utilisant les bibliothèques iptables C (libiptc, libxtables et les extensions iptables), sans appeler le binaire iptables et en analysant sa sortie.

Je suppose qu'une bibliothèque Python est autorisée à charger des bibliothèques C au moment de l'exécution, et Go ne peut pas [modifier: je me trompe], mais ne peut pas aller de manière statique à établir un lien vers ces bibliothèques? Parce que la bibliothèque Go devrait obtenir libxtables.aetc. de quelque part? (J'utilise dpkg -Lles -devpaquets pertinents dans Debian, et je ne vois que les .sos.)

Quoi qu'il en soit, selon la FAQ Netfilter :

4.5 Existe-t-il une API C / C ++ pour ajouter / supprimer des règles?

La réponse est malheureusement: non.

Maintenant, vous pourriez penser «mais qu'en est-il de libiptc?». Comme cela a été souligné à plusieurs reprises sur la ou les listes de diffusion, libiptc n'a JAMAIS été conçu pour être utilisé comme une interface publique. Nous ne garantissons pas une interface stable, et il est prévu de la supprimer dans la prochaine incarnation du paquet linux

Nous sommes bien conscients qu'il existe un manque fondamental pour une telle API et nous travaillons à améliorer cette situation. Jusque-là, il est recommandé d'utiliser system () ou d'ouvrir un tube dans stdin de iptables-restore. Ce dernier vous donnera une bien meilleure performance. libiptc est bien trop bas pour être utilisé de toute façon raisonnablement.

Ok, alors peut-être que vous n'êtes pas censé traiter ces bibliothèques C comme stables. Puis j'ai pensé "pourquoi ne pas simplement parler /procou quoi que ce soit directement?" Il s'avère que la iptablesplupart du temps, il n'utilise pas /procpour parler au noyau (juste pour lire les noms de table?), Et l'interface principale est en fait getsockopt()et setsockopt()sur un socket non lié.

C'était une autre surprise! (Mais c'est peut-être juste parce que je ne suis pas familier avec ce type de code que cela semble être une façon amusante d'interagir avec le noyau.)

J'ai fait un peu d'archéologie à l'envers de iptablesà ipchainsà ipfwet j'ai trouvé une page de manuel ipfw de 1997 qui m'a fait me sentir un peu mieux:

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.

Vous pouvez trouver ailleurs que "L'utilitaire ipfw est apparu pour la première fois dans FreeBSD 2.0" et

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.

Voici la version FreeBSD 2.0 ipfwde 1994 . Il utilise toujours setsockopt(), mais semble utiliser à la kvm_read()place de getsockopt().

Mes questions

  • Veuillez corriger tout ce que j'ai dit de mal ci-dessus :)
  • Pourquoi ont-ils choisi {get,set}sockopt()en premier lieu? Est-ce rare ou tout simplement nouveau pour moi?
  • Pourquoi n'a-t-il pas changé à un moment donné? Comme à quelque chose /proccomme le suggère la page de manuel de 1997.
  • Pourquoi n'ont-ils jamais proposé une interface C stable / publique iptables?

Mes questions de suivi

"Pas tout à fait, l'interface principale est la famille NETLINK_NETFILTER de netlink"

Merci pour ce pointeur!

Je regarde https://git.netfilter.org/iptables/treeet ne libipqsemble utiliser qu'une socket netlink, sauf si quelque chose me manque.

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

Les libipqregards de choses à me « normal »: faire une prise, se lient avec une sockaddr, sendto(), recvfrom(), etc.

xtables-monitorSemble également utiliser des mnl_socket_*wrappers qui font des trucs netlink.

Mais je ne peux pas comprendre libiptcet xtables- aucune mention de netlink, et ils ne lient même pas le socket.

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

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

Il y a d'autres exemples qui n'utilisent pas (semble-t-il?) Netlink, comme ce code du débogueur rr de Mozilla: https://github.com/mozilla/rr/blob/master/src/test/netfilter.c

Et netlink est uniquement Linux, à partir de l'an 2000 (2.2), alors que ce {get,set}sockopt()truc dans (au moins) ipfwest plus ancien, et dans BSD-land.

Je ne sais plus vraiment quelle est ma question :) Comment les sockets non-netlink communiquent-ils d'une manière ou d'une autre avec la bonne chose côté noyau sans même bind()et un sockaddr?

Réponses

StephenKitt Sep 04 2020 at 20:22

Je suppose qu'une bibliothèque Python est autorisée à charger des bibliothèques C au moment de l'exécution, et Go ne peut pas

Go peut utiliser des bibliothèques C partagées.

Il s'avère que la iptablesplupart du temps, il n'utilise pas /procpour parler au noyau (juste pour lire les noms de table?), Et l'interface principale est en fait getsockopt()et setsockopt().

Pourquoi ont-ils choisi {get,set}sockopt()en premier lieu? Est-ce rare ou tout simplement nouveau pour moi?

(Cette réponse indiquait précédemment, à tort, que l'interface principale est netlinkla NETLINK_NETFILTERfamille de.)

Vous avez raison, iptablesutilise des appels à getsockoptsur des sockets non liés du type approprié pour interagir avec le noyau, ce qui finit par appeler le netfiltercode du noyau . Je soupçonne que c'est pour des raisons historiques ...

Incidemment, getsockoptet setsockoptfonctionnent sur des sockets dans divers états, y compris non liés, et ils doivent le faire car ils sont utilisés pour définir des options avant de se connecter. La connexion netfilter-via- sockoptest en fait un canal secondaire.

Pourquoi n'a-t-il pas changé à un moment donné? Comme à quelque chose /proccomme le suggère la page de manuel de 1997.

Il y a probablement un certain nombre de raisons, mais l'une d'entre elles est sans aucun doute la rétrocompatibilité - sous Linux, les interfaces utilisateur-espace ne sont jamais censées se rompre. Ainsi, même si une nouvelle interface pourrait être fournie, celle existante resterait, ce qui réduit l'incitation à l'améliorer ou à réécrire des programmes qui fonctionnent déjà.

Tant que l' iptablesoutil de ligne de commande pouvait être utilisé sans trop d'efforts et que la fourniture d'une meilleure interface impliquait beaucoup plus d'efforts, le changement serait difficile. Une libiptcbibliothèque de style bien entretenue aurait également été une possibilité, mais les iptablesdéveloppeurs n'ont jamais été très incités à le faire.

Pourquoi n'ont-ils jamais proposé une interface C stable / publique iptables?

Parce que «ils» ne l'ont jamais compris. Maintenant , iptablesest progressivement en faveur de nftables, qui comprend un certain nombre de bibliothèques offrant un accès à différents niveaux d'abstraction ( libnftables, libnftnl, libnfnetlink). Cela utilise netlink, qui a été conçu comme une interface basée sur des sockets pour transférer des informations entre le noyau et l'espace utilisateur, et convient parfaitement à des outils tels que nftables(et iptables). La netlinkdocumentation répertorie les autres familles disponibles; ceux-ci couvrent un grand nombre de fonctionnalités hébergées par le noyau. netlink.

Vous pourriez trouver https://github.com/vishvananda/netlinkutile pour commencer à conduire iptablesdepuis Go. (Il faut beaucoup de travail pour cela, mais c'est mieux que de partir de zéro.)