Storia delle interfacce programmatiche per iptables, ipchains e ipfw

Sep 04 2020

Ho dovuto fare un po 'giocherellare con iptablesle regole da Go di recente, e ho notato sia di scaricatore di porto e delle CoreOS involucro biblioteche exec()fuori per il iptablescomando e lo schermo raschiare lo standard output. Questo mi è sembrato sorprendente.

In Python-land, c'è python-iptables :

L'interoperabilità con iptables si ottiene utilizzando le librerie C di iptables (libiptc, libxtables e le estensioni di iptables), senza chiamare il binario di iptables e analizzarne l'output.

Immagino che una libreria Python sia autorizzata a caricare librerie C in fase di esecuzione, e Go non può [modificare: mi sbaglio], ma non potrebbe Go collegarsi staticamente a quelle librerie? Perché la libreria Go dovrebbe ottenere libxtables.aecc. Da qualche parte? (Sto dpkg -Limportando i -devpacchetti rilevanti in Debian e vedo solo .soi pacchetti ).

Ad ogni modo, secondo le FAQ di Netfilter :

4.5 Esiste un'API C / C ++ per aggiungere / rimuovere regole?

La risposta purtroppo è: No.

Ora potresti pensare "ma che dire di libiptc?". Come è stato sottolineato numerose volte sulle mailinglist, libiptc non è mai stato concepito per essere usato come interfaccia pubblica. Non garantiamo un'interfaccia stabile e si prevede di rimuoverla nella prossima incarnazione del pacchetto linux

Siamo ben consapevoli della mancanza fondamentale di tale API e stiamo lavorando per migliorare questa situazione. Fino ad allora, si consiglia di usare system () o aprire una pipe nello stdin di iptables-restore. Quest'ultimo ti darà un modo migliore per prestazioni. libiptc è di livello troppo basso per essere usato comunque ragionevolmente.

Ok, quindi forse non dovresti trattare quelle librerie C come stabili. Poi ho pensato "perché non parlare /procdirettamente o qualsiasi altra cosa?" Si scopre che iptablesper lo più non si usa /procparlare con il kernel (solo per leggere i nomi delle tabelle?), E l'interfaccia principale è in realtà getsockopt()e setsockopt()su un socket non associato.

Questa è stata un'altra sorpresa! (Ma forse è solo perché non ho familiarità con questo tipo di codice che sembra un modo divertente per interfacciarsi con il kernel.)

Ho fatto un po 'di archeologia all'indietro da iptablesa ipchainsa ipfwe ho trovato una pagina man ipfw del 1997 che mi ha fatto sentire un po' meglio:

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.

Puoi trovare altrove che "L'utility ipfw è apparsa per la prima volta in FreeBSD 2.0" e

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.

Ecco la versione 2.0 di FreeBSD del ipfw1994 . Usa ancora setsockopt(), ma sembra usare al kvm_read()posto di getsockopt().

Le mie domande

  • Per favore correggi tutto ciò che ho detto sbagliato sopra :)
  • Perché hanno scelto {get,set}sockopt()in primo luogo? È raro o solo nuovo per me?
  • Perché non è stato cambiato a un certo punto? Mi piace qualcosa sotto /proccome suggerisce la pagina man del 1997.
  • Perché non hanno mai creato un'interfaccia C stabile / pubblica per iptables?

Le mie domande di follow-up

"Non proprio, l'interfaccia principale è la famiglia NETLINK_NETFILTER di netlink"

Grazie per questo suggerimento!

Sto guardando https://git.netfilter.org/iptables/treee libipqsembra che stia usando solo un socket netlink, a meno che non mi manchi qualcosa.

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

Gli libipqsguardi roba "normale" a me: fanno una presa di corrente, si legano con un sockaddr, sendto(), recvfrom(), etc.

xtables-monitorSembra anche che stia usando alcuni mnl_socket_*wrapper che fanno roba netlink.

Ma non riesco a capire libiptce xtables- nessuna menzione di netlink, e non vincolano nemmeno il socket.

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

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

Ci sono altri esempi che non usano (sembra?) Netlink, come questo codice dal debugger rr di mozilla: https://github.com/mozilla/rr/blob/master/src/test/netfilter.c

E netlink è solo Linux, dall'anno 2000 (2.2), mentre questa {get,set}sockopt()roba in (almeno) ipfwè più vecchia e in BSD-land.

Non sono più sicuro di quale sia la mia domanda :) In che modo i socket non netlink comunicano in qualche modo con la cosa corretta lato kernel senza nemmeno bind()e un sockaddr?

Risposte

StephenKitt Sep 04 2020 at 20:22

Immagino che una libreria Python sia autorizzata a caricare librerie C in fase di esecuzione e Go no

Go può utilizzare le librerie C condivise.

Si scopre che iptablesper lo più non si /procparla con il kernel (solo per leggere i nomi delle tabelle?), E l'interfaccia principale è in realtà getsockopt()e setsockopt().

Perché hanno scelto {get,set}sockopt()in primo luogo? È raro o solo nuovo per me?

(Questa risposta in precedenza affermava, in modo errato, che l'interfaccia principale è netlinkla NETLINK_NETFILTERfamiglia di.)

Hai ragione, iptablesusa chiamate a getsockoptsu socket non associati del tipo appropriato per interagire con il kernel, che finisce per chiamare il netfiltercodice del kernel . Sospetto che sia per ragioni storiche ...

Per inciso, getsockopte setsockoptlavorano sui socket in una varietà di stati, incluso non associato, e devono farlo perché sono usati per impostare le opzioni prima di connettersi. La connessione netfilter-via- sockoptè effettivamente un canale laterale.

Perché non è stato cambiato a un certo punto? Mi piace qualcosa sotto /proccome suggerisce la pagina man del 1997.

Probabilmente ci sono una serie di ragioni, ma una è senza dubbio la compatibilità con le versioni precedenti: in Linux, le interfacce dello spazio utente non dovrebbero mai interrompersi. Così, mentre potrebbe essere fornita una nuova interfaccia, rimarrebbe quella esistente, il che riduce l'incentivo a migliorarla o riscrivere programmi che già funzionano.

Fintanto che lo iptablesstrumento della riga di comando può essere utilizzato senza troppi sforzi e fornire un'interfaccia migliore richiede molto più impegno, il cambiamento sarebbe difficile. Anche una libiptclibreria in stile correttamente mantenuta sarebbe stata una possibilità, ma non c'erano mai molti incentivi per gli iptablessviluppatori a farlo.

Perché non hanno mai creato un'interfaccia C stabile / pubblica per iptables?

Perché "loro" non ci sono mai riusciti. Ora iptablesè in fase di esaurimento a favore di nftables, che comprende una serie di librerie che forniscono accesso a diversi livelli di astrazione ( libnftables, libnftnl, libnfnetlink). Questo utilizza netlink, che è stato progettato come un'interfaccia basata su socket per trasferire informazioni tra il kernel e lo spazio utente, ed è una scelta naturale per strumenti come nftables(e iptables). La netlinkdocumentazione elenca le altre famiglie disponibili; questi coprono un bel po 'di funzionalità ospitate dal kernel. netlink.

Potresti trovare https://github.com/vishvananda/netlinkutile per iniziare a guidare iptablesda Go. (È necessaria una discreta quantità di lavoro per questo, ma è meglio che iniziare da zero.)