\documentclass[a4paper,german]{article}
\usepackage{linuxdoc-sgml}
\usepackage{qwertz}
\usepackage{url}
\usepackage[latin1]{inputenc}
\usepackage{babel}
\usepackage{epsfig}
\usepackage{null}
\def\addbibtoc{
\addcontentsline{toc}{section}{\numberline{\mbox{}}\relax\bibname}
}%end-preamble
\setcounter{page}{1}
\title{Linux netfilter Hacking HOWTO}
\author{Rusty Russell, mailing list {\ttfamily netfilter@lists.samba.org}}
\date{v1.0 Tue May  2 14:07:03 CST 2000}


\begin{document}
\maketitle
Ins Deutsche übersetzt von Melanie Berg (mel@sekurity.de)
\abstract{Dieses Dokument beschreibt die netfilter-Architektur für Linux, wie man sie
hacken kann, und einiger darauf aufbauende größere Systeme, wie Paketfilter,  connection tracking und Network Address Translation (NAT). Diese deutsche
Übersetzung steht unter den Bedingungen der GNU General Public License
(http://www.gnu.org/copyleft/gpl.html).}


\tableofcontents




:%sno/ `/ `/gI


\section{Einleitung\label{intro}}

Hi Leute.



Dieses Dokument ist eine Reise; manche Teile sind gut besucht, und in anderen
Bereichen wirst Du Dich fast alleine fühlen. Der beste Rat, den ich Dir
geben kann, ist Dir eine große, heiße Tasse Kaffee oder Schokolade zu
besorgen, Dich in einen bequemen Stuhl zu setzen, und den Inhalt in Dich
aufzunehmen, bevor Du Dich in die gefährliche Welt des Netzwerk-Hackens
begibst.



Um die Verwendung der Infrastruktur auf dem Netfilter-Rahmenwerk besser
zu verstehen, empfehle ich, das Paketfiltering-HOWTO und das NAT-HOWTO
zu lesen. Für Informationen über Kernel-Programmierung empfehle ich
Rusty's Unreliable Guide to Kernle Hacking und Rusty's Unreliable Guide
to Kernel Locking.



Dieses Dokument enthält Flüche. Das ist ein natürliches Gewürz in
meiner Sprache, aber Du kannst die Originalversion dieses HOWTOs in
American Broadcast übersetzen, wenn Du den Filter benutzt:
\begin{tscreen}
\begin{verbatim}
sed `s/[^]aeio]ck/reak/'
\end{verbatim}
\end{tscreen}





\subsection{Was ist Netfilter?}

Netfilter ist eine Basis der Paketbehandlung, gehört aber nicht zu dem
normalen Berkeley Socket Interface. Es besteht aus vier Teilen. Zuerst
definiert jedes Protokoll `Hooks' (IPv4 definiert 5), welche wohldefinierte
Punkte auf der Reise eines Pakets durch den Protokoll-Stack sind. An jedem
dieser Punkte wird das Protokoll Netfilter mit dem Paket und der Hook-Nummer
aufrufen.



Zweitens können Teile des Kernels sich für das Aufpassen auf verschiedene
Hooks für jedes Protokoll registrieren. Wenn ein Paket also an Netfilter
gereicht wird, wird überprüft, ob irgendjemand für dieses Protokoll
oder für diesen Hook registriert ist; Wenn ja, bekommt jeder von ihnen
der Reihe nach die Chance, das Paket zu untersuchen (es möglicherweise
zu verändern), das Paket zu verwerfen, es durchzulassen oder Netfilter
zu beauftragen, das Paket für Userspace einzureihen.



Der dritte Teil besteht darin, dass eingereihte Pakete gesammelt werden
(von ip\_queue-Treiber), um an den Userspace geschickt zu werden; diese
Pakete werden asynchron behandelt.



Der letzte Teil besteht aus coolen Kommentaren im Code und Dokumentation.
Das ist Bestandteil eines jeden experimentiellen Projekts. Das Netfilter-Motto ist (schamlos von Cort Dougan gestohlen):

\begin{tscreen}
\begin{verbatim}
         `` Also... wo ist das hier besser als KDE? ``
\end{verbatim}
\end{tscreen}




(Dieses Motto hat sich ein wenig ausgeweitet: ``Peitsch mich aus,
schlag mich, laß mich ipchains benutzen'').



Zusätzlich zu diesem einfach Rahmenwerk wurden verschiedene Module
geschrieben, welche Funktionalitäten ähnlich zu früheren (pre-netfilter) Kernel bieten, im Besonderen ein erweiterbares NAT-System,
und ein erweiterbares Paketfilter-System (iptables).




\subsection{Was stimmte nicht mit dem, was wir im 2.0er und 2.2er Kernel hatten?}

\begin{enumerate}
\item Keine ausgebaute Infrastruktur, um Pakete an Userspace weiterzugeben:
\begin{itemize}
\item Kernelprogrammierung ist schwer
\item Kernelprogrammierung muß in C/C++ gemacht werden
\item Dynamische Filter-Policies gehören nicht in den Kernel
\item 2.2 führte ein, Pakete via netlink zum Userspace zu kopieren,
aber das ist langsam, und `sanity checks' (wenn ein Paket z.B.
nur behauptet, von einer existierenden Schnittstelle zu kommen)
sind nicht möglich. 
\end{itemize}

\item Transparente Proxies sind eine Qual:

\begin{itemize}
\item Wir untersuchen {\bfseries jedes} Paket, um zu sehen, ob ein Socket an 
diese Adresse gebunden ist 
\item Root darf an fremde Adressen binden
\item Lokal-generierte Pakete können nicht redirected werden
\item REDIRECT behandelt keine UDP-Antworten: Ein UDP-Named-Paket an
1153 weiterleiten funktioniert nicht, weil manche Clients
Antworten von anderen Ports als 53 nicht mögen.
\item REDIRECT koordiniert sich nicht mit der TCP/UDP Portzuordnung:
Ein Benutzer kann einen Port durch eine REDIRECT-Regeln shadowen.
\item Es ist während der 2.1er-Serie mindestens zweimal zusammengebrochen.
\item Code ist extrem aufdringlich. Denk an die Statistiken von
\#ifdef CONFIG\_IP\_TRANSPARENT\_PROXY in 2.2.1: es tritt in 11 Dateien
insgesamt 34 mal auf. Im Vergleich dazu taucht CONFIG\_IP\_FIREWALL
in 5 Dateien nur insgesamt 10 mal auf.
\end{itemize}

\item Es ist nicht mögliche, Paketfilter-Regeln unabhängig von der Adresse
der Schnittstelle zu erstellen:

\begin{itemize}
\item Um lokal-generierte oder lokal-bestimmte Pakete von durchgehenden
Paketen unterscheiden zu können, muß die lokale Adresse der
Schnittstelle bekannt sein.
\item Sogar das reicht bei Redirection oder Masquerading nicht aus.
\item Die FORWARD-Kette besitzt nur Informationen über die ausgehende
Schnittstelle, was bedeutet, dass Du herausfinden mußt, woher
ein Paket kam, indem Du Dein Wissen über die Netzwerk-Topologie
anwendest.
\end{itemize}

\item Masquerading ist abhängig von Paketfiltern:Interaktionen zwischen Paketfiltern und Masquerading machen eine
Firewall komplex:
\begin{itemize}
\item Beim Eingangsfilter scheinen Antwortpakete für den Rechner selbst
bestimmt zu sein
\item Beim Forward-Filter werden demaskierte Pakete überhaupt nicht
gesehen
\item Beim Ausgangsfilter scheinen Pakete vom lokalen Rechner zu kommen
\end{itemize}




\item TOS-Manipulation, Redirect, ICMP unreachable und mark (welche
Effekte auf Portforwarding, Routing und QoS haben können) hängen
ebenfalls vom Paketfilter-Code ab.
\item ipchains-Code ist weder modular noch erweiterbar (zum Beispiel MAC-Adressen Filter, Filtern nach Optionen, etc).
\item Mangel an ausreichender Infrastruktur hat zu einem Überfluß von
von verschiedenen Techniken geführt:
\begin{itemize}
\item Masquerading plus Per-Protokoll Module
\item Schnelles, statisches NAT durch Routing Code (hat keine Per-Protokoll Routinen)
\item Portforwarding, Redirect, Auto-Forwarding
\item Das Linux NAT und virtuelle Server Projekte.
\end{itemize}

\item Inkompatibilität zwischen CONFIG\_NET\_FASTROUTE und Paketfiltern:
\begin{itemize}
\item Weitergeleitete Pakete gehen trotzdem durch drei Ketten
\item Keine Möglichkeit, zu sagen, ob diese Ketten umgangen werden können
\end{itemize}

\item Wegen Routing-Protection (z.B. Verifikation der Quelladresse)
keine Möglichkeit, verworfene Pakete zu untersuchen.
\item Keine Möglichkeit, automatisch die Zähler auf Paketfilter-Regeln
zu lesen.
\item CONFIG\_IP\_ALWAYS\_DEFRAG ist eine Compilezeit-Option, die das Leben
für Distributionen, die einen general-purpose Kernel wollen,
schwer macht.
\end{enumerate}





\subsection{Wer bist Du?}

Ich bin der einzige, der dumm genug ist, das zu tun. Als ipchains Co-Autor
und jetziger Linux Kernel IP Firewall Maintainer sehe ich viele der
Probleme, die die Leute mit dem jetzigen System haben, da ich erkenne,
was sie alles machen wollen.




\subsection{Wieso stürzt es ab?}

Woah! Du hättest es {\bfseries letzte} Woche sehen sollen!

Weil ich kein so guter Programmierer bin, wir wir alle es uns wünschen
würden, und ich natürlich nicht alle Szenarien getestet habe, wegen
Mangel an Zeit, Ausstattung und/oder Inspiration. Ich habe eine Testumgebung, und ich will Dich ermutigen, daran teilzunehmen.




\section{Wo kann ich die neueste Version herbekommen?}

Es gibt einen CVS Server auf samba.org, der die neuesten HOWTOs, Anwendertools und Testseiten enthält. Wenn Du browsen möchtest, kannst Du
das Webinterface benutzen:
\nameurl{http://www.samba.org/cgi-bin/cvsweb/netfilter/}{Web Interface}.  

Um die neuesten Quellcodes zu bekommen, kannst Du folgendes tun:

\begin{enumerate}
\item Log Dich anonym auf dem SAMBA CVS Server ein:
\begin{tscreen}
\begin{verbatim}
cvs -d :pserver:cvs@cvs.samba.org:/cvsroot login
\end{verbatim}
\end{tscreen}
\item Wenn Du nach einem Paßwort gefragt wirst, tippe `cvs'. 
\item Überprüfe den verwendeten Code: 
\begin{tscreen}
\begin{verbatim}
cvs -d :pserver:cvs@cvs.samba.org:/cvsroot co netfilter
\end{verbatim}
\end{tscreen}
\item Um ein Update auf die neueste Version zu bekommen, verwende
\begin{tscreen}
\begin{verbatim}
cvs update -d -P
\end{verbatim}
\end{tscreen}
\end{enumerate}





\section{Netfilter Architektur}

Netfilter ist mehr eine Serie von Hooks auf bestimmten Positionen in
einem Protokoll-Stack (zur Zeit IPv4, IPv6 und DECnet).
Das (idealisierte) IPv4 Reise-Diagramm sieht folgendermaßen aus:

%\begin{tscreen}
%\begin{verbatim}
%Ein Paket, das das Netfilter System durchreist:
%
%   --->[1]--->[ROUTE]--->[3]--->[4]--->
%                 |            ^
%                 |            |
%                 |         [ROUTE]
%                 v            |
%                [2]          [5]
%                 |            ^
%                 |            |
%                 v            |
%\end{verbatim}
%\end{tscreen}
\begin{center}
{% Picture saved by xtexcad 2.4
\unitlength=1.000000pt
\begin{picture}(280.00,120.00)(0.00,0.00)
\put(110.00,20.00){\vector(0,-1){20.00}}
\put(110.00,100.00){\vector(0,-1){60.00}}
\put(220.00,0.00){\vector(0,1){20.00}}
\put(220.00,80.00){\vector(0,1){30.00}}
\put(220.00,40.00){\vector(0,1){20.00}}
\put(260.00,110.00){\vector(1,0){20.00}}
\put(200.00,110.00){\vector(1,0){40.00}}
\put(140.00,110.00){\vector(1,0){40.00}}
\put(40.00,110.00){\vector(1,0){40.00}}
\put(0.00,110.00){\vector(1,0){20.00}}
\put(110.00,30.00){\makebox(0.00,0.00){2}}
\put(220.00,30.00){\makebox(0.00,0.00){5}}
\put(250.00,110.00){\makebox(0.00,0.00){4}}
\put(190.00,110.00){\makebox(0.00,0.00){3}}
\put(30.00,110.00){\makebox(0.00,0.00){1}}
\put(190.00,60.00){\framebox(60.00,20.00){Route}}
\put(110.00,30.00){\circle{20.00}}
\put(30.00,110.00){\circle{20.00}}
\put(220.00,30.00){\circle{20.00}}
\put(250.00,110.00){\circle{20.00}}
\put(190.00,110.00){\circle{20.00}}
\put(80.00,100.00){\framebox(60.00,20.00){Route}}
\end{picture}}
\end{center}
\label{netfilter-traversal}

Links geht das Paket ein: Den einfachen sanity-check überstanden
(ich meine, nicht verstümmelt, Checksumme OK, kein promiscuous
receive), werden sie an den NF\_IP\_PRE\_ROUTING {[}1] Hook des Netfilter
Rahmenwerks weitergereicht.



Als nächstes kommen sie in den Routing-Code, welcher entscheidet,
ob das Paket für eine andere Schnittstelle oder für einen lokalen
Prozeß bestimmt ist. Der Routing-Code kann unroutebare Pakete
verwerfen.



Wenn es für den Rechner selbst bestimmt ist, wird der NF\_IP\_LOCAL\_IN
{[}2] Hook des Netfilter Rahmenwerks wieder aufgerufen, bevor das
Paket an den Prozeß (wenn es einen gibt) geschickt wird.



Wenn der Zielort stattdessen eine andere Schnittstelle ist, wird der
NF\_IP\_FORWARD {[}3] Hook des Netfilter Rahmenwerks stattdessen
aufgerufen.



Das Paket durchläuft dann den letzten Netfilter Hook, den
NF\_IP\_POST\_ROUTING {[}4] Hook, bevor es wieder in die Leitung geschickt
wird.



Für Pakete, die lokal generiert wurden, wird der NF\_IP\_LOCAL\_OUT {[}5]
Hook aufgerufen. Hier kannst Du sehen, dass Routing erst dann einsetzt,
wenn dieser Hook aufgerufen wurde: Tatsächlich wird zuerst der
Routing-Code aufgerufen (um Angaben über Quelladresse und IP-Optionen
zu bestimmen) und wird erneut aufgerufen, wenn das Paket geändert
werden sollte.




\subsection{Netfilter Grundlagen}

Jetzt haben wir ein Beispiel von netfilter für IPv4, Du kannst sehen,
wann jeder Hook aktiviert wird. Das ist die Essenz von Netfilter.



Kernelmodule können sich registrieren, um an irgendeinem dieser Hooks
lauschen. Wenn dieser Netfilter Hook dann vom Networking-Code aufgerufen
wird, hat an diesem Punkt jedes registrierte Modul die Möglichkeit,
das Paket zu verändern. Das Modul kann Netfilter sagen, eine von drei
Sachen zu tun:

\begin{enumerate}
\item  NF\_ACCEPT: Die Reise wie gewöhnlich fortsetzen.
\item  NF\_DROP: das Paket verwerfen, die Reise nicht fortsetzen.
\item  NF\_STOLEN: Ich habe das Paket übernommen, setz die Reise nicht fort.
\item  NF\_QUEUE: Das Paket einreihen (gewöhnlich für Userspace).
\item  NF\_REPEAT: Diesen Hook erneut aufrufen.
\end{enumerate}




Die anderen Teile von Netfilter (eingereihte Pakete behandeln, coole
Kommentare) werden später in der Kernel-Sektion erklärt.



Auf diesem Fundament können wir ziemliche komplexe Paketfilter-Manipulationen aufbauen, wie in den nächsten zwei Sektionen gezeigt werden wird.





\subsection{Paketauswahl: iptables}

Ein System zur Paketauswahl mit dem Namen IP-Tables wurde über das
Netfilter Rahmenwerk gebaut. Es ist ein direkter Abkömmling von
ipchains (welches von ipfwadm abstammt, welches, wenn ich mich richtig
erinnere, von BSD's ipfw abstammt), mit Erweiterungen. Kernelmodule
können eine neue Tabelle registrieren und von einem Paket verlangen,
eine vorgegebene Tabelle zu durchwandern. Diese Methode zur
Paketauswahl wird für Paketfilter (die `Filter'-Tabelle), Network
Address Translation (die `NAT'-Tabelle) und allgemeine Behandlung
von pre-routing Paketen (die `mangle'-Tabelle) verwendet.






\subsubsection{Paketfiltern}

Diese Tabelle, `filter', sollte die Pakete niemals verändern: sie
soll sie nur filtern.



Einer der Vorteile von iptables Filtern gegenüber ipchains ist, dass
es klein und schnell ist, und es die Netfilter-Hooks NF\_IP\_LOCAL\_IN,
NF\_IP\_FORWARD und NF\_IP\_LOCAL\_OUT verwendet. Das bedeutet, dass es
für jedes gegebene Paket einen (und nur einen) möglichen Ort gibt,
um es zu filtern. Dies vereinfacht die Dinge ungemein. Außerdem
bedeutet der Fakt, dass das Netfilter Rahmenwerk beides, eingehende
und ausgehende Schnittstellen für den NF\_IP\_FORWARD Hook bietet,
dass viele Arten des Filterns weitaus einfacher werden.



Beachte: Ich habe die Kernelteile sowohl von ipchains als auch von
ipfwadm zu Modulen auf Netfilter portiert, was es ermöglicht, die
alten ipfwadm und ipchains Anwendungstools ohne ein Upgrade weiterzuverwenden.




\subsubsection{NAT}

Dies ist das Königreich der NAT-Tabelle, welche mit Paketen von drei
Netfilter-Hooks gefüttert wird: für nicht-lokale Pakete sind für
Quell- und Zielveränderungen jeweils der NF\_IP\_PRE\_ROUTING und der
NF\_IP\_POST\_ROUTING Hook perfekt. Um das Ziel von lokalen Paketen zu
verändern, wird der NF\_IP\_LOCAL\_OUT Hook verwendet.



Diese Tabelle unterscheidet sich insoweit leicht von der `Filter'-Tabelle,
als dass nur das erste Paket einer neuen Verbindung die Tabelle durchwandern wird: Das Resultat dieser Untersuchung wird dann auf alle
weiteren Pakete derselben Verbindung angewandt.




\paragraph{Masquerading, Portforwarding, transparente Proxies}

Ich unterteile NAT in Source NAT (wo die Quelle des ersten Pakets verändert wird) und in Destination NAT (wo das Ziel der ersten Pakets verändert wird).



Masquerading ist eine spezielle Form von Source NAT: Port-Forwarding
und transparente Proxies sind eine spezielle Form von Destination NAT.
Dies wird nun alles mit dem NAT-Rahmenwerk erledigt, anstatt unabhängige
Einheiten zu sein.




\subsection{Connection Tracking}

Connection Tracking ist ein Fundament von NAT, wurde aber als separates
Modul implementiert; dies erlaubt es, durch eine Erweiterung der Paketfilter einfach und sauber Connection Tracking zu verwenden (das `state'
Modul).




\subsection{Sonstige Möglichkeiten}

Die neue Flexibilität bietet sowohl die Möglichkeit, wirklich abgefahrene
Dinge zu tun, als auch Erweiterungen oder einen kompletten Ersatz zu
schreiben, die auch vermischt werden können.




\section{Informationen für Programmierer}

Ich weihe Dich in ein Geheimnis ein: mein Hamster hat alles programmiert.
Ich war nur das Medium, wenn Du es so willst das `Front-End', im großen
Plan meines Hamsters. Also mach mich nicht für irgendwelche Bugs
verantwortlich. Mach den schlauen mit dem Fell verantwortlich.




\subsection{ip\_tables verstehen}

iptables besteht einfach aus einem benannten Array von Regeln im Speicher
(daher der Name `iptables') und Informationen darüber, wo Pakete von
jedem Hook die Reise beginnen sollen. Nachdem eine Tabelle registriert
wurde, kann ihr Inhalt von Anwenderseite her gelesen oder ersetzt
werden, indem getsockopt() und setsockopt() benutzt werden.



iptables registriert nicht mit irgendwelchen Netfilter Hooks: es verlässt
sich auf andere Module, die das tun und es angemessen mit Paketen
füttern sollen.




\subsubsection{ip\_tables Datenstruktur}

Der Bequemlichkeit halber wird dieselbe Datenstruktur verwendet, um eine
Regel von Anwenderseite her oder im Kernel selbst zu repräsentieren,
obwohl einige Felder nur im Kernel selbst benutzt werden.



Jede Regel besteht aus folgenden Teilen:
\begin{enumerate}
\item Einem `struct ip\_entry'.
\item Null oder mehr `struct ipt\_entry\_match' Strukturen, an jede Platz
für Variablen (0 oder mehr Bytes) von Daten angehängt.
\item Einer `struct ipt\_entry\_target' Struktur, an jede Platz
für Variablen (0 oder mehr Bytes) von Daten angehängt.
\end{enumerate}


Die Variablen-Natur der Regeln gibt eine große Flexibilität für
Erweiterungen, wie wir sehen werden, besonders, da jeder Treffer
oder jedes Ziel eine willkürliche Menge von Daten tragen können.
Dies birgt zwar auch ein paar Fallen, aber wie auch immer: Wir
müssen aufpassen. Wir tun dies, indem wir uns darüber versichern,
dass die `ipt\_entry', die `ipt\_entry\_match' und die `ipt\_entry\_target'
Strukturen eine angenehme Größe haben, und dass alle Daten auf
die maximale Auslastung des Rechners aufgerundet werden, indem wir
den IPT\_ALIGN() Makro verwenden.



`struct ipt\_entry' hat die folgenden Felder:
\begin{enumerate}
\item Einen `struct ipt\_ip' Teil, der die Bestimmungen für den IP-Header
enthält, der zutreffen soll.
\item Ein `nf\_cache' Bitfeld, das zeigt, welche Teile des Pakets diese
Regel untersucht hat.
\item Ein `target\_offset' Feld, das den Absatz vom Anfang dieser Regel
angibt, wo die ipt\_entry\_target Struktur beginnt.
\item Ein `next\_offset' Feld, das die absolute Größe dieser Regel angibt,
einschließlich Treffer und Ziele.
\item Ein `comefrom' Feld, das vom Kernel benutzt wird, um die Reise des
Pakets zurückzuverfolgen. 
\item Ein `struct ipt\_counters' Feld, was die Paket- und Bytezähler für
Pakete, die auf diese Regel gepaßt haben, enthält. 
\end{enumerate}




ipt\_entry\_match' und `struct ipt\_entry\_target' sind sehr ähnlich
darin, dass sie ein Feld für die Gesamtlänge (jeweils `match\_size' und
`target\_size') enthalten, einen Union, der den Namen des Treffers oder
des Ziels (auf Anwenderseite) enthält, und einen Pointer (für den
Kernel). 



Wegen der trickreichen Natur der Datenstruktur einer Regel werden einige
Hilfsroutinen angeboten:

\begin{description}
\item[ipt\_get\_target()] \mbox{}

Diese Funktion liefert einen Pointer auf das Target einer Regel.



\item[IPT\_MATCH\_ITERATE()] \mbox{}

Dieser Makro ruft die gegebene Funktion für jeden Treffer einer
Regel auf. Das erste Argument der Funktion ist `ipt\_match\_entry'
und andere Argumente (wenn es welche gibt) sind die, die im
IPT\_MATCH\_ITERATE() Makro angeboten werden.



\item[IPT\_ENTRY\_ITERATE()] \mbox{}

Diese Funktion macht einen Pointer zu einem Eintrag, der Gesamtgröße
der Tabelle der Einträge und einer Funktion, die aufzurufen ist.
Das erste Argument der Funktion ist `struct ipt\_entry', und andere
Argumente (wenn es welche gibt) sind die, die im IPT\_ENTRY\_ITERATE()
Makro angeboten werden.



\end{description}





\subsubsection{ip\_tables aus Sicht der Anwender}

Userspace hat vier Operationen: Es kann die aktuelle Tabelle lesen, die
Info lesen (Hook-Positionen und Größe der Tabelle), die Tabelle ersetzen
(und die alten Zähler nehmen), und neue Zähler einbauen.



So kann Userspace eine kleine Operation simulieren: Dies wird von der
libiptc Library getan, welche die einfachen ``add/delete/replace'' semantics
für Programme bietet.



Da diese Tabellen in den Kernel transferiert werden, stellt sich die Frage
der Auslastung der Maschinen, die unterschiedliche Userspace- und Kernelspace Typ-Regeln haben (z.B. Sparc mit 32-Bit Userland). Diese Fälle
werden mit dem Überschreiben der Definition von IPT\_ALIGN für diese
Platformen in `libiptc.h' behandelt.




\subsubsection{ip\_tables verwenden und erforschen }

Der Kernel beginnt die Untersuchung an der Stelle, die durch einen bestimmten
Hook definiert ist. Diese Regel wird überprüft; wenn das `struct ipt\_ip'
Element zutrifft, wird jedes `struct ip\_entry\_match' der Reihe nach
untersucht (es wird die match-Funktion entsprechend dem Treffer aufgerufen).
Wenn die match-Funktion 0 liefert, endet die Iteration auf dieser Regel.
Wenn sie den `hotdrop' Parameter auf 1 setzt, wird das Paket ebenfalls
sofort verworfen werden (Dies wird für einige verdächtige Pakete benutzt,
so wie in der TCP-match Funktion).



Wenn diese Iteration bis zum Ende durchläuft, werden die Counter erhöht,
`struct ipt\_entry\_target' wird untersucht: Wenn es ein Standardtarget ist,
wird das `verdict' Feld gelesen (negativ bedeutet ein Urteil über das
Paket, positiv steht für ein Offset, zu dem man springen kann).
Wenn die Antwort positiv ist und das Offset nicht das der nächsten
Regel ist, wird die `back' Variable gesetzt, und der vorige `back' Wert
wird in das `comefrom' Feld der Regel geschrieben.



Für nicht-standard Targets wird die target Funktion aufgerufen: Sie liefert
ein Urteil (nicht-standard Ziele können zu nichts springen, das würde
den statischen Loop-Detection-Code brechen). Das Urteil kann IPT\_CONTINUE
sein, um bei der nächsten Regel weiterzumachen.




\subsection{iptables erweitern}

Weil ich so faul bin, ist {\ttfamily iptables} leicht erweiterbar. Dies ist hauptsächlich der Versuch, die Arbeit auf die anderen zu schieben, worum es
bei Open Source eigentlich geht (Bei Freier Software, wie RMS sagen
würde, geht es um Freiheit, und ich saß bei einem seiner Gespräche
dabei, als ich das hier schrieb).



{\ttfamily iptables} erweitern besteht aus zwei Teilen: den Kernel erweitern, 
indem man ein neues Modul schreibt, und möglicherweise das Anwendungsprogramm
{\ttfamily iptables} erweitern, indem man eine neue Shared Library schreibt.




\subsubsection{Der Kernel}

Ein Kernelmodul selbst zu schreiben ist ziemlich einfach, wie Du an den
Beispielen sehen kannst. Eine Sache, auf die Du achten mußt, ist, dass
Dein Code integriert werden muß: Es kann ein Paket von Anwenderseite her
ankommen, während ein anderes an einem Interrupt eintrifft. Tatsächlich
kann es ab 2.3.4 bei SMP vorkommen, dass ein Paket an einem Interrupt
pro CPU eintrifft.



Folgende Funktionen mußt Du kennen:

\begin{description}
\item[init\_module()] \mbox{}

Das ist der Eintrittspunkt des Moduls. Es liefert eine negative Zahl
oder 0, wenn Netfilter es erfolgreich registriert.



\item[cleanup\_module()] \mbox{}

Das ist der Austrittspunkt des Moduls; es soll die Registration beim
Kernel wieder aufheben.



\item[ipt\_register\_match()] \mbox{}

Dies wird verwendet, um einen neuen, zutreffenden Typ zu registrieren.
Du übergibst ihm ein `struct ip\_match', was gewöhnlich als statische
Variable deklariert wird (file-scope).



\item[ipt\_register\_target()] \mbox{}

Dies wird verwendet, um einen neuen Typ zu registrieren. Du
übergibst ihm ein `struct\_ip\_target', was gewöhnlich als statische
Variable deklariert wird (file-scope).



\item[ipt\_unregister\_target()] \mbox{}

Um die Registration des Ziel aufzuheben.



\item[ipt\_unregister\_match()] \mbox{}

Um die Registration des Treffers aufzuheben.



\end{description}




Eine Warnung zu trickreichen Dingen (wie das Verwenden von Countern) für
Deine neuen Treffer oder Ziele. Auf SMP-Maschinen wird die vollständige
Tabelle mit Hilfe von memcpy für jede CPU dupliziert: Wenn Du die
Information wirklich zentral halten willst, solltest Du Dir die Treffermethode zu `limit' ansehen.




\paragraph{Neue Match-Funktionen}

Neue match-Funktionen werden gewöhnlich als alleinstehende Module geschrieben. 
Es ist möglich, diese Module der Reihe nach zu erweitern, obwohl
es normalerweise nicht nötig ist. Eine Möglichkeit, um den Benutzern
zu erlauben, direkt mit Deinem Modul zu sprechen, ist es, die
`nf\_register\_sockopt' Funktion des Netfilter Rahmenwerks zu nutzen.
Eine andere Möglichkeit ist es, Symbole für andere Module zu exportieren,
damit sie sich registrieren können; auf diese Weise machen es Netfilter
und iptables.



Das Wesentliche Deiner neuen Match-Funktion ist `struct ipt\_match', was
an `ipt\_register\_match()' übergeben wird. Diese Struktur hat die
folgenden Felder:

\begin{description}
\item[list] \mbox{}

Dieses Feld ist auf irgendeinen Blödsinn gesetzt, z.B. `$\{$NULL, NULL$\}$'.



\item[name] \mbox{}

Dieses Feld ist der Name der Match-Funktion, auf die sich der Anwender
bezieht. Der Name sollte dem Modul entsprechend gewählt werden (wenn
der Name ``mac'' ist, muß das Modul ``ipt\_mac.o'' heißen), damit es
automatisch geladen werden kann.



\item[match] \mbox{}

Diese Feld ist ein Pointer auf eine Match-Funktion, welche aus einem
skb, dem Pointer auf ein Eingangs- und ein Ausgangsgerät (eins davon
kann NULL sein, das hängt von dem Hook ab), einem Pointer auf die
Match-Daten der zutreffenden Regel, der Größe dieser Regel, dem
IP-Offset (nicht Null steht für ein non-head Fragment), einem
Pointer auf den Protokoll-Header (ich meine nur den IP-Header), der
Gesamtlänge der Daten (ich meine die Paketlänge minus der IP-Header
Länge) und schließlich einem Pointer auf die `hotdrop' Variable
besteht. Es soll nicht Null liefern, wenn die Regel auf das Paket
zutrifft, und kann `hotdrop' auf 1 setzen, wenn es Null liefert,
um anzuzeigen, dass das Paket sofort verworfen werden soll.



\item[checkentry] \mbox{}

Dieses Feld ist ein Pointer auf eine Funktion, die die Bestimmungen
für eine Regel untersucht; Wenn es 0 liefert, wird die Regel des
Benutzers nicht akzeptiert werden. Zum Beispiel wird der ``tcp'' Match
Typ nur TCP-Pakete akzeptieren, wenn also der `struct ipt\_ip' Teil
der Regel nicht spezifiziert, dass das Protokoll TCP sein muß, wird
Null geliefert. Das Tablename Argument erlaubt Deinem Match, zu
kontrollieren, in welchen Tabellen es eingesetzt werden kann, und
`hook\_mask' ist eine Bitmaske von Hooks, von denen diese Regel
möglicherweise aufgerufen wurde: Wenn Dein Match von einem der
Netfilter-Hooks keinen Sinn macht, kannst Du das hier umgehen.



\item[destroy] \mbox{}

Dieses Feld ist ein Pointer auf eine Funktion, die aufgerufen wird,
wenn ein Eintrag, der dieses Match verwendet, gelöscht wird. Dies
erlaubt Dir auch, Resourcen in checkentry dynamisch zuzuordnen und
sie hier wieder aufzuräumen.



\item[me] \mbox{}

Dieses Feld ist auf `\&\_\_this\_module' gesetzt, welches Deinem Modul
einen Pointer gibt. Es bewirkt, dass der usage-count steigt und
fällt, je nachdem, ob Regeln dieses Typs erstellt oder gelöscht
werden. Das bewahrt den Benutzer davon, das Modul zu entfernen
(und somit das cleanup\_module aufzurufen), wenn sich eine Regel
darauf bezieht.



\end{description}





\paragraph{Neue Targets}

Neue Targets werden auch gewöhnlich als alleinstehende Module geschrieben.
Die Diskussionen der oberen Sektion `Neue Match Funktionen' können hier
ähnlich angewandt werden.



Das Wesentliche Deines neuen Targets ist `struct ipt\_target', das an
`ipt\_register\_target()' übergeben wird. Diese Struktur hat die
folgenden Felder:

\begin{description}
\item[list] \mbox{}

Dieses Feld ist auf irgendeinen Blödsinn gesetzt, z.B. `$\{$NULL, NULL$\}$'.



\item[name] \mbox{}

Dieses Feld ist der Name der Ziel-Funktion, auf die sich der Anwender
bezieht. Der Name sollte dem Modul entsprechend gewählt werden (wenn
der Name ``REJECT'' ist, muß das Modul ``ipt\_REJECT.o'' heißen), damit es
automatisch geladen werden kann.



\item[target] \mbox{}

Diese Feld ist ein Pointer auf eine Ziel-Funktion, welche aus einem
skbuff, dem Pointer auf ein Eingangs- und ein Ausgangsgerät (eins davon
kann NULL sein) einem Pointer auf die Ziel-Daten, der Größe der
Ziel-Daten, und der Position der Regel in der Tabelle besteht.
Die Target-Funktion liefert eine nicht-negative, absolute Position,
zu der zu springen ist, oder ein negatives Urteil (was das negierte
Urteil minus 1 ist).



\item[checkentry] \mbox{}

Dieses Feld ist ein Pointer auf eine Funktion, die die Bestimmungen
für eine Regel überprüft; wenn das 0 liefert, wird die Regel
des Benutzers nicht akzeptiert werden.



\item[destroy] \mbox{}

Dieses Feld ist ein Pointer auf eine Funktion, die aufgerufen wird,
wenn ein Eintrag, der dieses Match verwendet, gelöscht wird. Dies
erlaubt Dir auch, Resourcen in checkentry dynamisch zuzuordnen und
sie hier wieder aufzuräumen.



\item[me] \mbox{}

Dieses Feld ist auf `\&\_\_this\_module' gesetzt, welches Deinem Modul
einen Pointer gibt. Es bewirkt, dass der usage-count steigt und
fällt, je nachdem, ob Regeln dieses Typs erstellt oder gelöscht
werden. Das bewahrt den Benutzer davon, das Modul zu entfernen
(und somit das cleanup\_module aufzurufen), wenn sich eine Regel
darauf bezieht.

\end{description}





\paragraph{Neue Tabellen}

Wenn Du willst, kannst Du für einen speziellen Zweck eine neue Tabelle
erstellen. Um das zu tun, rufst Du `ipt\_register\_table()' mit
`struct ipt\_table' auf, was die folgenden Felder hat:

\begin{description}
\item[list] \mbox{}

Dieses Feld ist auf irgendeinen Blödsinn gesetzt, z.B. `$\{$NULL, NULL$\}$'.



\item[name] \mbox{}

Dieses Feld ist der Name der Ziel-Funktion, auf die sich der Anwender
bezieht. Der Name sollte dem Modul entsprechend gewählt werden (wenn
der Name ``nat'' ist, muß das Modul ``ipt\_nat.o'' heißen), damit es
automatisch geladen werden kann.



\item[table] \mbox{}

Dies ist ein voll ausgenutztes `struct ipt\_replace', wie es vom
Anwender genutzt werden kann, um eine Tabelle zu ersetzen. Der
`counters' Pointer sollte auf NULL gesetzt sein. Diese Datenstruktur
kann als `\_\_initdata' deklariert werden, damit es nach dem Booten
verworfen werden kann.



\item[valid\_hooks] \mbox{}

Dies ist eine Bitmaske der IPv4 Netfilter Hooks, mit der Du die
Tabellen betreten wirst: Sie wird verwendet, um zu überprüfen,
dass diese Eintrittspunkte gültig sind und um die möglichen Hooks
für die ipt\_match und die ipt\_target `checkentry() Funktionen zu
berechnen.



\item[lock] \mbox{}

Dies ist das read-write Lock für die vollständige Tabelle, initialisiere es auf RW\_LOCK\_UNLOCKED.



\item[private] \mbox{}

Dies wird vom ip\_tables Code intern verwendet.

\end{description}





\subsubsection{Anwendertools}

Jetzt, wo Du Dein nettes cooles Kernelmodul geschrieben hast, möchtest
Du vielleicht seine Optionen von Anwenderseite her kontrollieren. Lieber,
als für jede Erweiterung von {\ttfamily iptables} eine abgespaltene Version 
zu haben, benutze ich eine Technologie der späten 90er: Furbies.
Sorry, ich meine shared Libraries.



Gewöhnlich benötigen neue Tabellen keine Erweiterungen von {\ttfamily iptables}:
Der Benutzer verwendet einfach die `-t' Option, um auf eine neue Tabelle
zugreifen zu können.



Die shared Libraries sollten eine `\_init()' Funktion haben, die beim
Laden automatisch aufgerufen wird: Das moralische Gegenstück zu der
`init\_module()' Funktion der Kernelmodule. Dies sollte `register\_match()'
oder `register\_target()' aufrufen, abhängig davon, ob Deine shared
Library ein neues Match oder ein neues Ziel ist.



Du brauchst nur dann eine shared Library, wenn Du einen Teil der Struktur
initialisieren oder eine zusätzliche Option bieten willst. Das Ziel
`REJECT' zum Beispiel benötigt nichts dergleichen, hat also auch keine
shared Library.



Es gibt nützliche Funktionen, die in `iptables.h' beschrieben werden,
besonders:
\begin{description}
\item[check\_inverse()] \mbox{}

Überprüft, ob eins der Argumente ein `!' ist, und wenn ja, setzt
es das `invert' Flag, wenn das noch nicht geschehen sein sollte.
Wenn sie `true' liefert, solltest Du optind wie im Beispiel erhöhen.



\item[string\_to\_number()] \mbox{}

Konvertiert einen String zu einer Zahl in der gegebenen Größenordnung.
Sie liefert -1, wenn der String ungültig ist oder außerhalb der
Größenordnung liegt.



\item[exit\_error()] \mbox{}

Diese Funktion sollte aufgerufen werden, wenn ein Fehler gefunden wird.
Das erste Argument ist gewöhnlich `PARAMETER\_PROBLEM', was bedeutet,
dass der User die Kommandozeile nicht richtig benutzt hat.

\end{description}





\paragraph{Neue Match-Funktionen}

Die \_init() Funktion Deiner shared Library übergibt einen Pointer auf
ein statisches `struct iptables\_match' an `register\_match()', was die
folgenden Felder hat:

\begin{description}
\item[next] \mbox{}

Dieser Pointer wird verwendet, um eine verlinkte Liste von Matches
zu erstellen (wie sie für die listing-Regeln verwendet wird).
Am Anfang sollte er auf NULL gesetzt sein.



\item[name] \mbox{}

Der Name der Match-Funktion. Er sollte entsprechend dem Namen der
Library gewählt sein (z.B. ``tcp'' für `libipt\_tcp.so').



\item[version] \mbox{}

Dies wird gewöhnlich auf den NETFILTER\_VERION Makro gesetzt: Es
stellt sicher, dass das {\ttfamily iptables} Binary nicht aus Versehen die
falsche Library auswählt.



\item[size] \mbox{}

Die Größe der Match-Daten für dieses Match; damit Du Dir keine
Sorgen machen mußt, wird sie von IPT\_ALIGN() automatisch aufgerundet
werden.



\item[userspacesize] \mbox{}

Für einige Matches ändert der Kernel intern einige Felder (z.B. im
Fall von `limit'). Das bedeutet, dass ein einfaches `memcmp' nicht
mehr ausreicht, um zwei Regeln zu vergleichen (benötigt für die
delete-matching-rule Funktionalität). Wenn dies der Fall ist, plaziere all die Felder, die sich nicht ändern, an den Anfang der
Struktur, und lege die Größe der sich nicht ändernden Felder hier
ab.



\item[help] \mbox{}

Eine Funktion, die die Synopsis der Option ausdruckt.



\item[init] \mbox{}

Dies kann verwendet werden, um etwas mehr Platz (wenn vorhanden)
in der ipt\_entry\_match Stuktur zu schaffen, und um jegliche
nfcache Bits zu setzen; Wenn Du etwas untersuchst, das Du nicht
ausdrücken kannst, indem Du den Inhalt von `/linux/include/
netfilter\_ipv4.h' verwendest, wende im NFC\_UNKNOWN Bit einfach OR an.
Dies wird vor `parse()' aufgerufen werden.



\item[parse] \mbox{}

Dies wird aufgerufen, wenn eine nicht erkannte Option auf der
Kommandozeile gesehen wird: Es sollte nicht NULL liefern, wenn diese
Option tatsächlich für Deine Library bestimmt war. `invert' liefert
`true', wenn bereits ein `!' gesehen wurde. Der `flags' Pointer ist
für die exklusive Verwendung Deiner Match-Library und wird gewöhnlich
dazu verwendet, eine Bitmaske von bestimmten Optionen zu speichern.
Vergiß nicht, das nfcache Feld auszurichten. Wenn nötig, kannst
Du die Größe der `ipt\_entry\_match' Struktur erhöhen, indem Du
Sie erneut zuordnest, dann mußt Du aber auch sicherstellen, dass
die Größe durch den IPT\_ALIGN Makro gehen muß.



\item[final\_check] \mbox{}

Dies wird nach überstandener Kommandozeileneingabe aufgerufen. Ihm
werden die für Deine Library reservierten Integer-'flags' übergeben. Dies gibt Dir die Gelegenheit, zu überprüfen, ob alle
benötigten Optionen angegeben wurden, zum Beispiel `exit\_error()'
aufzurufen, wenn dies der Fall sein sollte.



\item[print] \mbox{}

Dies wird von Chain-Listing-Code verwendet, um jegliche zusätzliche
Match-Information einer Regel (wenn vorhanden) ausdrucken (zum
Standard Output). Das numerische Flag wird gesetzt, wenn der User
es mit der `-n' Option angegeben hat.



\item[save] \mbox{}

Dies ist das Gegenteil von parse: Es wird von `iptables-save' verwendet,
um die Optionen, die die bestimmte Regel erstellt haben, zu reproduzieren.



\item[extra\_opts] \mbox{}

Dies ist eine NULL-terminierte Liste von Extra-Optionen, die Deine
Library anbietet. Dies wird vermischt mit den jetzigen Optionen
und so an getopt\_long übergeben; Für Details siehe Manpage.

\end{description}


Es gibt noch extra Elemente am Ende dieser Struktur, die {\ttfamily iptables} 
intern verwendet: Du brauchst sie hier nicht zu setzen.




\paragraph{Neue Targets}

Die \_init() Funktionen Deiner shared Libraries übergeben `register\_target()'
einen Pointer auf ein statisches `struct iptables\_target', welches ähnliche
Felder hat wie die iptables\_match Strukturen, die oben detailliert beschrieben wurden.



Manchmal braucht ein Target keine Library für den Userspace; Eine triviale
mußt Du sowieso erstellen: Es gab früher zu viele Probleme mit schlecht
plazierten Libraries.




\subsubsection{`libiptc' verwenden}

{\ttfamily libiptc} ist die Kontroll Library für iptables, entworfen, um Regeln 
im iptables Kernelmodul aufzulisten und zu manipulieren. Während seine
jetzige Verwendung sich auf das iptables Tool beschränkt, macht es
das Schreiben neuer Tools recht einfach. Um diese Funktionen nutzen zu
können, mußt Du root sein.



Die Kerneltabellen selbst bestehen nur aus ein paar Tabellen mit Regeln
und einer Anzahl von Nummern, die die Eintrittspunkte repräsentieren.
Die Namen der Ketten (``INPUT'') werden als Abstraktion von der Library
unterstützt. Benutzerdefinierte Ketten werden benannt, indem ein
Fehlerverweis vor dem Beginn der benutzerdefinierten Kette eingefügt
wird, der den Kettennamen in einer speziellen Datensektion des Targets
enthält (Die Positionen der eingebauten Ketten werden durch die
drei Eintrittspunkte der Tabelle definiert).



Wenn `iptc\_init()' aufgerufen wird, wird die Tabelle einschließlich der
Counter gelesen. Diese Tabelle wird von der `iptc\_insert\_entry()',
`iptc\_replace\_entry()', `iptc\_append\_entry()', `iptc\_delete\_entry()',
`iptc\_delete\_num\_entry()', `iptc\_flush\_entries()',
`iptc\_zero\_entries()', `iptc\_create\_chain()' `iptc\_delete\_chain()',
und `iptc\_set\_policy()' Funktion manipuliert.



Die Änderungen an der Tabelle werden nicht eher zurückgeschrieben, bis
die `iptc\_commit()' Funktion aufgerufen wird. Das bedeutet, dass es
für zwei Benutzer der Library gleichzeitig möglich ist, auf derselben
Kette zu operieren, um jeweils der Schnellere zu sein; Um das zu
verhindern, ist Locking nötig, was aber zur Zeit nicht eingesetzt wird.



Es gibt kein Wettrennen mit bei Countern, wie auch immer; Counter werden
so in den Kernel zurückaddiert, dass eine Erhöhung der Zähler zwischen
dem Lesen und dem Schreiben der Tabelle immernoch in der neuen Tabelle
auftauchen wird.



Es gibt verschiedene Hilfsfunktionen:

\begin{description}
\item[iptc\_first\_chain()] \mbox{}

Diese Funktion liefert den Namen der ersten Kette in dieser Tabelle.



\item[iptc\_next\_chain()] \mbox{}

Diese Funktion liefert den Namen der nächsten Kette in dieser Tabelle:
NULL bedeutet, es gibt keine weiteren Ketten.



\item[iptc\_builtin()] \mbox{}

Liefert true, wenn der Name der gegebenen Kette der Name einer
eingebauten Kette ist.



\item[iptc\_first\_rule()] \mbox{}

This returns a pointer to the first rule
in the given chain name: NULL for an empty chain.



\item[iptc\_next\_rule()] \mbox{}

Dies liefert einen Pointer auf die erste Regel im Namen der gegebenen
Kette: NULL für eine leere Kette.



\item[iptc\_get\_target()] \mbox{}

Dies gibt das Target der gegebenen Regel. Wenn es kein erweitertes
Target ist, wird der Name dieses Targets geliefert. Wenn es ein Sprung
auf eine andere Kette ist, wird der Name dieser Kette geliefert.
Wenn es ein Urteil (z.B. DROP) ist, wird dieser Name geliefert.
Wenn die Regel kein Ziel hat (und eine accountig-style Regel), wird
ein leerer String geliefert.



Beachte, dass Du diese Funktion verwenden solltest, anstatt den Wert
des `verdict' Felds der ipt\_entry Struktur direkt zu benutzen, da sie
die oben genannten weiteren Interpretationen des Standard Urteils
bietet.



\item[iptc\_get\_policy()] \mbox{}

Dies liefert die Policy einer eingebauten Kette und füllt das Counterargument mit den Treff-Statistiken dieser Policy.



\item[iptc\_strerror()] \mbox{}

Diese Funktion liefert eine aussagekräftigere Erklärung von gescheitertem Code in der iptc Library. Wenn eine Funktion scheitert, wird es
errno setzen: Dieser Wert kann an iptc\_strerror() übergeben werden,
um eine Fehlermeldung zu erhalten.



\end{description}





\subsection{NAT verstehen}

Willkommen zu Network Address Translation im Kernel. Beachte, dass die
angebotene Infrastruktur mehr für Vollständigkeit als für Effizienz
entworfen wurde und dass weitere Entwicklungen die Effizienz erhöhen
können. Im Moment bin ich froh, dass es überhaupt funktioniert.



NAT wird aufgeteilt in Connection Tracking (was die Pakete überhaupt
nicht verändert) und in den NAT Code selbst. Connection Tracking kann
auch als iptables Modul verwendet werden, um subtile Unterscheidungen
von Zuständen zu machen, die NAT egal sind.




\subsubsection{Connection Tracking}

Connection Tracking benutzt als Hooks die von der Priorität
höhergestellten NF\_IP\_LOCAL\_OUT und NF\_IP\_PRE\_ROUTING,
um Pakete zu sehen, bevor sie das System betreten.



Das ncft Feld in skb ist ein Pointer auf einen der infos{[}] Arrays im
Inneren von struct ip\_conntrack. So können wir den Zustand von skb
durch das Element bestimmen, auf welches dieser Array zeigt: Dieser
Pointer erkennt beides, die State Struktur und die Beziehung des skb
zu diesem Zustand.



Der beste Weg, das `nfct' Feld zu lesen, ist es, die `ip\_conntrack\_get()'
Funktion aufzurufen, die NULL liefert, wenn es nicht gesetzt ist, oder
den Connection Pointer. Außerdem füllt sie ctinfo, was die Beziehung
des Paket zu der Verbindung beschreibt. Dieser numerierte Typ hat
folgende Werte:

\begin{description}


\item[IP\_CT\_ESTABLISHED] \mbox{}

Das Paket ist Teil einer in Originalrichtung aufgebauten Verbindung.



\item[IP\_CT\_RELATED] \mbox{}

Das Paket steht in Zusammenhang mit der Verbindung und geht in die
Originalrichtung.



\item[IP\_CT\_NEW] \mbox{}

Das Paket versucht, eine neue Verbindung aufzubauen (offensichtlich
geht es in die Originalrichtung).



\item[IP\_CT\_ESTABLISHED + IP\_CT\_IS\_REPLY] \mbox{}

Das Paket ist Teil einer aufgebauten Verbindung, und zwar in Antwortrichtung.



\item[IP\_CT\_RELATED + IP\_CT\_IS\_REPLY] \mbox{}

Das Paket steht in Zusammenhang mit der Verbindung und geht in die
Antwortrichtung.

\end{description}


Ein Antwortpaket kann also als solches indentifiziert werden, indem man
auf {$>$}= IP\_CT\_IS\_REPLY testet.




\subsection{NAT/Connection Tracking erweitern}

Diese Rahmenwerke wurden entworfen, um jegliche Art von Protokollen und
verschiedenen Mapping-Arten unterzubringen. Manche dieser Mapping-Arten
können sehr spezifisch sein, so wie load-balancing oder fail-over Mappings.



Intern konvertiert Connection Tracking ein Paket zu einem `Tupel', der
den interessanten Teil des Pakets repreasentiert, bevor nach darauf 
zutreffenden Regeln gesucht wird. Dieser Tupel hat einen manipulierbaren
Teil und einen nicht-manipulierbaren Teil; ``src'' und ``dst'' genannt,
da dies die Ansicht des ersten Pakets in der Source NAT Welt ist (in
der Destination NAT Welt würde es ein Antwortpaket sein). Die Tupel
aller Pakete in demselben Paket-Stream in dieser Richtung sind gleich.



Das Tupel eine TCP-Pakets enthält z.B. den manipulierbaren Teil (Quell-IP und Quellport) und den nicht-manipulierbaren Teil (Ziel-IP und Zielport)
Der manipulierbare und der nicht-manipulierbare Teil müssen trotzdem
nicht vom selben Typ sein; Das Tupel eines ICMP-Pakets enthält den
manipulierbaren Teil (Quell-IP und ICMP-ID) und den nicht-manipulierbaren
Teil (Ziel-IP und ICMP Typ und Code).



Jedes Tupel hat ein Inverses, nämlich das Tupel des Antwortpakets in dem
Stream. Das Inverse eines ICMP Ping-Pakets (icmp id 1234, von
192.168.1.1 an 1.2.3.4) ist zum Beispiel ein Ping Antwort Paket (icmp id
1234, von 1.2.3.4 an 192.168.1.1).



Diese Tupel, die von `struct ip\_conntrack\_tuple' repräsentiert werden,
werden häufig verwendet. Tatsächlich ist das, zusammen mit dem Hook,
an dem das Paket einging (was einen Effekt auf die zu erwartende Art
der Manipulation hat) und dem beteiligten Device, die komplette
Information über das Paket.



Die meisten Tupel sind Teil von `struct ip\_conntrack\_tuple\_hash', was
einen doppelt verlinkten Listeneintrag und einen Pointer auf die
Verbindung, zu der das Paket gehört, hinzufügt.



Eine Verbindung wird von `struct ip\_conntrack' repräsentiert: Es hat
zwei `struct ip\_conntrack\_tuple\_hash' Felder: Eins, was sich auf
die Richtung des Originalpakets bezieht (tuplehash{[}IP\_CT\_DIR\_ORIGINAL]),
und eins, was sich auf die Antwortrichtung des Pakets bezieht
(tuplehash{[}IP\_CT\_DIR\_REPLY]).



Wie auch immer, das erste, was der NAT Code tut, ist nachzusehen, ob
der Connection Tracking Code es geschafft hat, ein Tupel zu extrahieren
und ein bestehende Verbindung zu finden, indem er in das nfct Feld
von skbuff sieht; Das sagt uns, ob es ein Versuch ist, eine neue
Verbindung aufzubauen, oder, wenn nicht, in welche Richtung das ganze
geht; im letzteren Fall werden die Manipulationen ausgeführt, die
vorher für diese Verbindung bestimmt wurden.



Wenn es der Anfang einer neuen Verbindung war, suchen wir nach einer
Regel für dieses Tupel, indem wir den Standard-Mechanismus von
iptables verwenden. Trifft eine Regel zu, wird sie verwendet, um
Manipulationen sowohl für die Original-, als auch für die Antwortrichtung auszuführen; dem Connection Tracking Code wird mitgeteilt,
dass die zu erwartenden Antwort sich geändert hat. Dann wird das
Paket wie oben beschrieben verändert.



Wenn es keine Regel gibt, wird eine `Null' Bindung erstellt: Das mappt
das Paket gewöhnlich nicht, stellt aber sicher, dass wir keinen
anderen Stream über den existierenden mappen. Manchmal kann die
Null-Bindung nicht erstellt werden, weil wir bereits einen existierenden
Stream darübergemappt haben. In diesem Fall könnte die Pre-Protocol
Manipulation versuchen, ihn zu remappen, obwohl es vom Namen her
keine Null-Bindung ist.




\subsubsection{Standard NAT-Targets}

NAT Targets sind wie alle anderen iptables Target-Erweiterungen, außer,
dass sie darauf bestehen, nur in der `nat' Tabelle verwendet zu werden.
Sowohl SNAT als auch DNAT Targets verwenden `struct ip\_nat\_multi\_range'
als Extradaten; dies wird benutzt, um einen Bereich von Adressen zu
bestimmen, auf denen ein Mapping gültig ist. Ein Element dieses Bereichs,
`struct ip\_nat\_range', besteht aus einer inklusiven minimalen und maximalen IP-Adresse und aus einem inklusiven minimalen und maximalen
Wert zur Protokollbestimmung (zum Beispiel TCP Ports). Es gibt auch
Platz für Flags, die uns sagen, ob die IP-Adresse gemappt werden kann
(manchmal wollen wir nur den protokoll-spezifischen Teil eines Tupels
mappen, nicht die IP), oder ein anderes, das uns sagt, dass der
protokoll-spezifische Teil des Bereichs `valid' ist.



Ein Multi-Range ist ein Array aus diesen `struct ip\_nat\_range' Elementen;
das bedeutet, dass ein Bereich ``1.1.1.1-1.1.1.2 Ports 50-55 AND 1.1.1.3
Port 80'' sein kann. Jedes Element wird zu dem Bereich dazuaddiert (Union,
für die, die diese Theorie mögen).




\subsubsection{Neue Protokolle}


\paragraph{Der Kernel von innen}

Ein neues Protokoll zu implementieren bedeutet zuerst, zu entscheiden,
was der manipulierbare und was der nicht-manipulierbare Teil des
Tupels sein sollen. Jedes Teil des Tupels hat die Eigenschaft, dass es
den Stream eindeutig identifiziert. Der manipulierbare Teil des Tupel
ist der Teil, mit dem Du NAT machen kannst: Für TCP ist das der
Quellport, für ICMP ist das die ICMP id; etwas, das das ``Stream
Identifier'' benutzt werden kann. Der nicht-manipulierbare Teil ist
der Rest des Pakets, der den Stream zwar auch eindeutig identifiziert,
mit dem wir aber nicht spielen können (z.B. TCP Zielport, ICMP Typ).



Sobald Du Dich dazu entschieden hast, kannst Du eine Erweiterung für
den Connection Tracking Code schreiben und die `ip\_conntrack\_protocol'
Struktur, die Du an `ip\_conntrack\_register\_protocol()' übergeben
mußt, ausbauen.



Die Felder von `struct ip\_conntrack\_protocol' sind:

\begin{description}
\item[list] \mbox{}

Setz es auf `$\{$NULL, NULL$\}$'; verwendet, um sich der Liste zu nähern.



\item[proto] \mbox{}

Die Nummer Deines Protokolls, siehe `/etc/protocols'.



\item[name] \mbox{}

Der Name Deines Protokolls. Das ist der Name, den der Anwender sehen
wird; meistens ist es das beste, den entsprechenden Namen aus
`/etc/protocols' zu verwenden.



\item[pkt\_to\_tuple] \mbox{}

Die Funktion, die die protokollspezifischen Teile des Tupels ausfüllt, sobald ihr das Paket übergeben wird. Der `datah' Pointer
zeigt auf den Anfang des Headers (direkt hinter der IP-Adresse),
und datalen ist die Länge des Pakets. Es liefert NULL, wenn das
Paket nicht lang genug ist, um Header-Informationen zu enthalten;
datalen wird (gezwungenermaßen) immer mindestens 8 Byte lang sein.



\item[invert\_tuple] \mbox{}

Diese Funktion wird einfach dazu verwendet, den protokollspezifischen
Teil des Tupels so zu ändern, wie eine Antwort auf dieses Paket
aussehen würde.



\item[print\_tuple] \mbox{}

Diese Funktion wird verwendet, um den protokollspezifischen Teil
eines Tupels auszudrucken; gewöhnlich wird es mit sprintf() in
den mitgegebenen Buffer geschrieben. Geliefert wird die Anzahl
von verwendeten Buffer-Zeichen. Dies wird verwendet, um den
Status des /proc Eintrags zu drucken.



\item[print\_conntrack] \mbox{}

Diese Funktion wird benutzt, um den privaten Teil der Conntrack
Struktur (wenn vorhanden) zu drucken. Sie wird auch verwendet, um
den Status unter /proc zu drucken.



\item[packet] \mbox{}

Diese Funktion wird aufgerufen, wenn ein Paket erkannt wird, das Teil
einer aufgebauten Verbindung ist. Du bekommst einen Pointer auf die
Conntrack Struktur, den IP-Header, die Länge und ctinfo. Du gibt
ein Urteil über das Paket zurück (gewöhnlich NF\_ACCEPT), oder
-1 wenn das Paket kein gültiger Teil der Verbindung ist. Innerhalb
dieser Funktion kannst Du, wenn Du willst, diese Verbindung löschen,
Du solltest aber folgendes Idiom verwenden, um races zu vermeiden:

\begin{tscreen}
\begin{verbatim}
if (del_timer(&ct->timeout))
        ct->timeout.function((unsigned long)ct);
\end{verbatim}
\end{tscreen}




\item[new] \mbox{}

Diese Funktion wird aufgerufen, wenn ein Paket zum ersten Mal eine
Verbindung aufbaut; es gibt kein ctinfo arg, da das erste Paket
der Definition nach ctinfo IP\_CT\_NEW ist. Wenn es beim Verbindungsaufbau scheitert, liefert die Funktion 0, oder im selben Moment
ein Connection Timeout.

\end{description}


Sobald Dein fertig bist und getestet hast, dass Du Dein neues Protokoll
einsetzen kannst, ist es an der Zeit, NAT beizubringen, wie es zu
übersetzen ist. Dies bedeutet, ein neues Modul zu schreiben; eine
Erweiterung zum NAT-Code und die `ip\_conntrack\_protocol'
Struktur, die Du an `ip\_conntrack\_register\_protocol()' übergeben
mußt.

\begin{description}
\item[list] \mbox{}

Setz es auf `$\{$NULL, NULL$\}$'; verwendet, um sich der Liste zu nähern.



\item[name] \mbox{}

Der Name Deines Protokolls. Das ist der Name, den der Anwender sehen
wird; meistens ist es das beste, den entsprechenden Namen aus
`/etc/protocols' zu verwenden, um auto-loading zu ermöglichen, wie
wir später noch sehen werden.



\item[protonum] \mbox{}

Die Nummer Deines Protokolls, siehe `/etc/protocols'.



\item[manip\_pkt] \mbox{}

Das ist die andere Hälfte der von Conntection Tracking benutzten
`ptk\_to\_tuple' Funktion: Stell sie Dir als ``tuple\_to\_ptk'' vor.
Trotzdem gibt es ein paar Unterschiede: Du bekommst einen Pointer
auf den Anfang des IP-Headers, und die Gesamtlänge des Pakets. Das
liegt daran, dass manche Protokolle (UDP, TCP) den IP-Header kennen
müssen. Dir wird das `ip\_nat\_tuple\_manip' Feld des Tupels gegeben
(ich meine das ``src'' Feld) statt des ganzen Tupels und der Art der
Manipulation, die auszuführen ist.



\item[in\_range] \mbox{}

Diese Funktion wird verwendet, um zu sagen, ob manipulierbarer Teil
des gegebenen Tupels im gegebenen Bereich liegt. Diese Funktion ist
ein bißchen trickreich: Wir erhalten die Art der Manipulation, die
auf das Tupel angewandt wurde, die uns sagt, wie wir den Bereich
interpretieren können (ist es ein Quell- oder ein Zielbereich, auf
den wir abzielen?).



Diese Funktion wird benutzt, um zu überprüfen, ob ein existierendes
Mapping uns in den richtigen Bereich bringt, außerdem noch, um zu
überprüfen, ob vielleicht überhaupt keine Manipulation nötig ist.



\item[unique\_tuple] \mbox{}

Diese Funktion ist das Herzstück von NAT: Wir erhalten ein Tupel
und einen Bereich und sollen den Per-Protocol Teil des Tupels
verändern, um es im Bereich zu plazieren und somit einzigartig zu
machen. Wenn wir kein unbenutztes Tupel in diesem Bereich finden,
wird 0 geliefert. Außerdem bekommen wir einen Pointer auf die
Conntrack Struktur, welcher für ip\_nat\_user\_tuple() benötigt wird.



Gewöhnlich wird einfach der Per-Protocol Teil des Tupels durch den
Bereich iteriert, und `ip\_nat\_user\_tupel()' wird solange überprüft,
bis es einmal false liefert.



Beachte, dass der Null-Mapping Fall bereits überprüft wurde:
Entweder liegt er außerhalb des gegebeben Bereichs, oder er ist
schon besetzt.



Wenn IP\_NAT\_RANGE\_PROTO\_SPECIFIED gesetzt ist, bedeutet dies, dass
der Anwender NAT, und nicht NAPT macht: Etwas Vernünftiges mit
dem Bereich tun. Wenn kein Mapping erwünscht ist (In TCP zum
Beispiel sollte kein Ziel-Mapping den TCP-Port ändern, wenn es
nicht ausdrücklich verlangt wird), wird 0 geliefert.



\item[print] \mbox{}

Given a character buffer, a match tuple and a mask,
write out the per-protocol parts and return the length of the buffer
used.



\item[print\_range] \mbox{}

Mit einem gegebenen Charakter-Buffer, einem Match-Tupel und einer
Maske wird dies den Per-Protocol Teil ausdrucken und die Länge
des Buffers liefern.

\end{description}





\subsubsection{Neue NAT-Targets}

Das ist der wirklich interessante Teil. Du kannst neue NAT-Targets
schreiben, die einen neuen Mapping-Typ bieten: Zwei Extra-Targets
werden im Standard Package geliefert: MASQUERADE und REDIRECT. Diese
illustrieren recht einfach das Potential und die Macht des Schreibens
neuer NAT-Targets.



Sie werden genauso wie alle anderen iptables Targets geschrieben, nur
intern werden sie `ip\_nat\_setup\_info()' aufrufen und die Verbindung
extrahieren.




\subsubsection{Protokoll-Hilfen für TCP und UDP}

Dieser Teil befindet sich noch in der Entwicklung.




\subsection{Understanding Netfilter}

Netfilter ist ziemlich simpel und wurde in den vorangegangenen Sektionen
recht ausführlich beschrieben. Wie auch immer, manchmal ist es notwendig,
weiter zu gehen als die NAT oder die ip\_tables Infrastruktur bietet,
oder vielleicht möchtest Du sie auch vollständig ersetzen.



Ein wichtiger Punkt bei Netfilter (Naja, in der Zukunft) ist Caching.
Jedes skb hat ein `nfcache' Feld: Eine Bitmaske, die sagt, welche Felder
im Header untersucht wurden und ob das Paket verändert wurde oder nicht.
Die Idee ist, dass auf jeden Netfilter Hook in den dazu relevanten Bits
eine OR-Verknüpfung angewandt werden kann, so dass wir später ein
Cache-System schreiben können, das clever genug sein wird, um zu
erkennen, wann Pakete nicht an Netfilter gereicht werden müssen.



Die wichtigsten Bits sind NFC\_ALTERED, was bedeutet, dass das Paket
verändert wurde (Dies wird für den NF\_IP\_LOCAL\_OUT Hook von IPv4
bereits verwendet, um geänderte Pakete erneut zu routen), und
NFC\_UNKNOWN, was aussagt, dass Caching nicht angewandt werden sollte,
da einige Eigenschafte untersucht wurden, die nicht ausgedrückt
werden konnten. Im Zweifelsfall solltest Du das NFC\_UNKNOWN Flag
in das nfcache Feld von skb in Deinem Hook setzen.




\subsection{Neue Netfilter-Module schreiben}


\subsubsection{Netfilter-Schnittstellen benutzen}

Um Pakete im Kernel zu empfangen/behandeln, kannst Du einfach ein
Modul schreiben, welches beim ``Netfilter Hook'' registriert. Im Grunde
ist das ein Ausdruck von Interesse an einem gegebenen Punkt; der
genaue Punkt ist protokollspezifisch und wird in protokollspezifischen
Netfilter-Headern, wie ``netfilter\_ipv4.h'', definiert.



Um Netfilter Hooks zu registrieren (und das wieder aufzuheben), benutzt
Du die `nf\_register\_hook' und die `nf\_unregister\_hook' Funktionen.
Beide benötigen einen Pointer auf `struct nc\_hook\_ops', welchen Du
wie folgt benutzt:

\begin{description}
\item[list] \mbox{}

Setz es auf `$\{$NULL, NULL$\}$'; verwendet, um sich der Liste zu nähern.



\item[hook] \mbox{}

Die Funktion, die aufgerufen wird, wenn ein Paket auf diesen Hook-Punkt trifft. Deine Funktion muß NC\_ACCEPT, NC\_DROP oder NC\_QUEUE
liefern. Wenn NC\_ACCEPT geliefert wird, wird der nächste an diesen
Punkt angehängte Hook aufgerufen werden. Wenn NC\_DROP geliefert
wird, wird das Paket verworfen werden. Wenn NC\_QUEUE geliefert wird,
wird das Paket gequeued werden. Wenn Du einen Pointer auf einen
skb Pointer erhälst, kannst Du skb, wenn Du willst, vollständig
ersetzen.



\item[flush] \mbox{}

Zur Zeit unbenutzt: Es wurde entworfen, um Paket-Treffer weiterzureichen,
wenn der Cache gelöscht wird. Vielleicht wird es niemals implementiert
werden: Setz es auf NULL.



\item[pf] \mbox{}

Die Protokollfamilie, zum Beispiel `PF\_INET' für IPv4.



\item[hooknum] \mbox{}

Die Nummer des Hooks, für den Du Dich interessierst, z.B.
`NC\_IP\_LOCAL\_OUT'.

\end{description}





\subsubsection{Eingereihte Pakete behandeln}

Dies ist die zur Zeit von `ip\_queue' verwendete Schnittstelle; Du kannst
hier registrieren, um Pakete für ein gegebenes Protokoll zu behandeln.
Hier herrscht eine ähnliche Semantik wie bei der Registrierung für
einen Hook, außer, dass Du eine Behandlung des Pakets blockieren kannst.
Außerdem siehst Du nur Pakete, für die ein Hook mit `NC\_QUEUE' geantwortet hat.



Die zwei Funktionen, die verwendet werden, um Interesse an gequeueten
Paketen zu registrieren, heißen `nc\_register\_queue\_handler()' und
`nf\_unregister\_queue\_handler()'. Die Funktion, die Du registrieren
wirst, wird mit dem `void *' Pointer aufgerufen werden, mit dem
Du sie an `nf\_register\_queue\_handler()' übergeben hast.



Wenn niemand registriert ist, ein Paket zu behandeln, ist das Liefern
von NF\_QUEUE äquivalent zum Liefern von NF\_DROP.



Die Pakete werden eingereiht werden, sobald Du Dich dafür registriert
hast. Du kannst mit ihnen tun, wasimmer Du willst, wenn Du aber fertig
bist mit ihnen, mußt Du `nf\_reinject()' aufrufen (benutz nicht einfach
nur `kfree\_skb()'). Wenn Du ein skb reinjizierst, übergibst Du ihm
das skb, das dem Queue-Handler gegebene `struct nf\_info' und ein
Urteil: NF\_DROP verwirft es, NF\_ACCEPT läßt es weiter durch die
Hooks iterieren, NF\_QUEUE reiht sie erneut ein und NF\_REPEAT bewirkt,
dass der Hook, der das Paket eingereiht hat, erneut konsultiert wird
(Paß auf unendliche Loops auf!).



Du kannst in `struct nf\_info' sehen, um hilfreiche Informationen über
das Paket zu bekommen, z.B. über die Schnittstelle oder den betreffenden
Hook.




\subsubsection{Kommandos vom Anwender empfangen}

Netfilter Komponenten wollen mit dem Anwender interagieren. Die Methode
hierfür ist die Verwendung des setsockopt Mechanismus. Beachte, dass
jedes Protokoll modifiziert werden muß, um nf\_setsockopt() für nicht
verstandene setsockopt Nummern (und nf\_getsockopt() für getsockopt
Nummern) aufrufen zu können, und das bis jetzt nur IPv4, IPv6 und
DECnet modifiziert wurden.



Wir verwenden eine mittlerweile bekannte Technik: Wir registrieren ein
`struct nf\_sockopt\_ops', indem wir nf\_register\_sockopt() aufrufen.
Die Felder dieser Struktur sind wie folgt:

\begin{description}
\item[list] \mbox{}

Setz es auf `$\{$NULL, NULL$\}$'; verwendet, um sich der Liste zu nähern.



\item[pf] \mbox{}

Die Protokollfamilie, die Du behandelst, zum Beispiel PF\_INET.



\item[set\_optmin] \mbox{}

und

\item[set\_optmax] \mbox{}

Diese bestimmen den (exklusiven) Bereich der verwendeten setsockopt
Nummern. 0 und 0 bedeutet also, dass Du keine setsockopt Nummern
benutzt.



\item[set] \mbox{}

This is the function called when the user calls one of
your setsockopts.  You should check that they have NET\_ADMIN
capability within this function.



\item[get\_optmin] \mbox{}

and

\item[get\_optmax] \mbox{}

These specify the (exclusive) range of getsockopt numbers handled.
Hence using 0 and 0 means you have no getsockopt numbers.



\item[get] \mbox{}

Das ist die Funktion, die aufgerufen wird, wenn der User eine Deiner
getsockopts aufruft. Du solltest überprüfen, ob es eine NET\_ADMIN
Capability in dieser Funktion gibt.

\end{description}




Die zwei letzten Felder werden intern verwendet.




\subsection{Behandlung von Paketen aus Sicht der Anwender}

Durch das Verwenden der libipq Library und des `ip\_queue' Moduls kann
nun fast alles, was im Kernel getan werden kann, auch von Anwenderseite
her geschehen. Das bedeutet, dass Du, mit etwas Geschwindigkeitsverlust,
Deinen Code ausschließlich im Userspace entwickeln kannst. Wenn Du keine
große Bandbreite filtern mußt, wirst Du das angenehmer finden, als die
Pakete im Kernel zu behandeln.



In den frühen Tagen von Netfilter habe ich das bewiesen, indem ich eine
Embryo-Version von iptables zum Userspace portierte. Netfilter öffnet
den Leute die Tür, um ihre eigenen recht effizienten Module zu schreiben,
und das in welcher Sprache sie wollen.




\section{2.0er und 2.2er Paketfilter-Module übersetzen}

Sieh Dir die ip\_fw\_compat.o Datei an; diese sollte eine Portierung recht
einfach machen.




\section{Die Testsuite}

Auf dem CVS Server gibt es eine Test-Suite: Je größer der Bereich ist,
den diese Testsuite abdeckt, desto größer wird Deine Zufriedenheit
sein, zu sehen, dass Änderungen am Code nichts komplett zerbrochen
haben. Triviale Tests sind mindestens so wichtig, wie trickreiche Tests:
Es sind die trivialen Tests, die die komplexen vereinfachen (da Du
weißt, dass die Grundlagen gut funktionieren, bevor die trickreichen
Tests ausgeführt werden).



Die Tests sind einfach: Sie sind nur Shell-Scripte im /testsuite Verzeichnis,
die erfolgreich sein sollen. Die Scripte werden in alphabetischer Reihenfolge ausgeführt, `01test' kommt also vor `02test'. Zur Zeit gibt es
fünf Testverzeichnisse:

\begin{description}
\item[00netfilter/] \mbox{}

Generelle Tests zum Netfilter-Rahmenwerk.

\item[01iptables/] \mbox{}

Tests zu iptables.

\item[02conntrack/] \mbox{}

Tests zu Connection Tracking.

\item[03NAT/] \mbox{}

Tests zu NAT.

\item[04ipchains-compat/] \mbox{}

Tests zur Kompatibilität von ipchains/ipfwadm.

\end{description}


Im testsuite/ Verzeichnis gibt es ein Script mit dem Namen `test.sh'.
Es konfiguriert zwei Dummy-Schnittstellen (tap0 und tap1), aktiviert
Forwarding und beseitigt alle Netfilter-Module. Dann geht es durch die
oberen Verzeichnisse und führt dort alle test.sh Scripte aus, bis eins
davon nicht funktioniert. Dieses Script hat zwei optionale Argumente:
`-v' druckt jeden gerade ausgeführten Test aus. Außerdem kann ein
optionaler Testname angegeben werden: Ist dieser gegeben, werden alle
anderen Tests ausgelassen, bis dieser eine gefunden wird.




\subsection{Einen Test schreiben}

Erstelle in dem betreffenden Verzeichnis eine neue Datei: Versuche, Deinen
Test so zu numerieren, dass er zur richtigen Zeit ausgeführt wird. Um
zum Beispiel ICMP Reply Tracking zu testen (02conntrack/02reply.sh),
müssen wir zuerst sicher sein, dass ausgehende ICMP Pakete sauber
getrackt werden (02conntrack/01simple.sh).



Gewöhnlich ist es besser, mehrere kleine Dateien zu erstellen, von
denen jede einen Bereich abdeckt, da das hilft, Probleme für die
Leute, die auf der Testsuite arbeiten, sofort zu isolieren.



Wenn etwas in dem Test schief läuft, gib ein `exit 1', das verursacht
einen Fehler; wenn dies etwas ist, das Du schon erwartet hast, solltest
Du eine eindeutige Meldung hinterlassen. Dein Test sollte mit `exit 0'
enden, wenn alles gut läuft. Du solltest den Erfolg eines {\bfseries jeden} 
einzelnen Befehls überprüfen, indem an den Anfang des Scripts die Option
`set -e' setzt oder indem Du ein `$|$$|$ exit 1' an jeden Befehl anhängst.



Die Hilfsfunktionen `load\_module' und `remove\_module' können verwendet
werden, um Module zu laden: Du solltest Dich nicht auf das automatische
Laden von Modulen in der Testsuite verlassen, es sei denn, das ist es
spezifisch, was Du testen willst.




\subsection{Variablen und Umgebung}

Du hast zwei Schnittstellen zum Spielen: tap0 und tap1. Ihre Schnittstellenadressen
stehen jeweils in {\ttfamily \$TAP0} und {\ttfamily \$TAP1}. Beide haben 
die Netzmaske 255.255.255.0; ihre Netzwerke stehen jeweils in \$TAP0NET 
und \$TAP1NET.



Es gibt eine leere Temporary Datei in \$TMPFILE. Sie wird am Ende Deines
Tests gelöscht.



Dein Script wird aus dem /testsuite Verzeichnis heraus gestartet werden,
woimmer das auch sein mag. Also solltest Du Tools (wie iptables) mit
Pfadnamen wie `../userspace' verwenden.



Dein Script kann mehr Informationen ausdrucken, wenn \$VERBOSE gesetzt
ist (was bedeutet, dass der User ein `-v' auf der Kommandozeile
angegeben hat.




\subsection{Nützliche Tools}

Es gibt verschiedene nützliche Testsuite-Tools im Unterverzeichnis
`Tools': Jedes davon endet mit einem Nicht-Null Exit Status, wenn es
ein Problem gibt.




\subsubsection{gen\_ip}

Du kannst IP-Pakete mit `gen\_ip' generieren, was IP-Pakete an Standard-%
Input liefert. Du kannst IP-Pakete an tap0 und tap1 liefern, indem
Du Standard-Output an /dev/tap0 oder /dev/tap1 sendest (Falls diese
nicht existieren sollten, werden sie beim ersten Verwenden der Testsuite
erstellt).



gen\_ip ist ein einfaches Programm, das zur Zeit sehr penibel mit der
Reihenfolge seiner Argumente ist. Zuerst gibt es die allgemeinen
optionalen Argumente:

\begin{description}
\item[FRAG=offset,length] \mbox{}

Erstelle ein Paket, zerlege es dann in Fragmente von folgendem
Offset und folgender Länge.



\item[MF] \mbox{}

Setz das `More Fragments' Bit in dem Paket.



\item[MAC=xx:xx:xx:xx:xx:xx] \mbox{}

Setz die Quell-MAC-Adresse des Pakets.



\item[TOS=tos] \mbox{}

Setz das TOS-Feld im Paket (0 - 255).

\end{description}


Als nächstes kommen die erforderlichen Argumente:

\begin{description}
\item[source ip] \mbox{}

Quell-IP-Adresse des Pakets.



\item[dest ip] \mbox{}

Ziel-IP-Adresse des Pakets.



\item[length] \mbox{}

Gesamtlänge des Pakets, einschließlich Header.



\item[protocol] \mbox{}

Protokollnummer des Pakets, 17 ist zum Beispiel UDP.

\end{description}


Die Argumente hängen dann vom Protokoll ab: für UDP (17) gibt es Quell-
und Ziel-Portnummer. Für ICMP (1) gibt es Typ und Code der ICMP Meldung:
ist der Typ 0 oder 8 (ping-Antwort oder ping), werden zwei zusätzliche
Argumente (das ID und das Sequenzfeld) benötigt. Für TCP werden Quell-
und Zielport, sowie Flags (``SYN'', ``SYN/ACK'', ``ACK'', ``RST'' or ``FIN'')
benötigt. Hier gibt es drei optionale Argumente: ``OPT='', gefolgt von
einer durch Kommata getrennten Liste von Optionen, ``SYN='' gefolgt von
einer Sequenznummer, und ``ACK='' gefolgt von einer Sequenznummer.
Schließlich sagt das optionale Argument ``DATA'', dass der Payload des
TCP-Pakets mit den Inhalt von Standard-Input gefüllt werden soll.




\subsubsection{rcv\_ip}

Du kannst Dir IP-Pakete mit rcv\_ip ansehen, was eine Kommandozeile ausgibt,
die so nah wie möglich an die Originalwerte herankommt, die an gen\_ip
übergeben wurden (mit Ausnahme von Fragmenten).



Das ist extrem nützlich für das Analysieren von Paketen. Es erfordert
zwei Argumente:

\begin{description}
\item[wait time] \mbox{}

Die maximale Zeit in Sekunden, um auf ein Paket von Standard Input
zu warten.



\item[iterations] \mbox{}

Die Anzahl der zu empfangenden Pakete.

\end{description}


Es gibt ein optionales Argument ``DATA'', was den Payload des Pakets
nach dem Paketheader auf Standard-Output ausdruckt.



 Der Standardweg, rcv\_ip in einem Shellscript zu verwenden, ist wie folgt:

\begin{verbatim}
# Jobkontrolle aufsetzen, damit mir $ in Shellscripten verwenden können.
set -m

# Zwei Sekunden auf ein Paket von tap0 warten.
../tools/rcv_ip 2 1 < /dev/tap0 > $TMPFILE &

# Sichergehen, dass rvp_ip läuft.
sleep 1

# Ein ping-Paket senden
../tools/gen_ip $TAP1NET.2 $TAP0NET.2 100 1 8 0 55 57 > /dev/tap1 || exit 1

# Auf rvp_ip warten
if wait %../tools/rcv_ip; then :
else
    echo rcv_ip failed:
    cat $TMPFILE
    exit 1
fi
\end{verbatim}





\subsubsection{gen\_err}

Dieses Programm nimmt ein Paket (wie zum Beispiel von ip\_gen generiert)
und wandelt es in einen ICMP Fehler um.



Es hat drei Argumente: Die Quell-IP-Adresse, einen Typ und einen Code.
Die Ziel-IP-Adresse wird auf die Quell-IP-Adresse des von Standard-Input übergebenen Pakets gesetzt.




\subsubsection{local\_ip}

Dies nimmt ein Paket von Standard-Input und injiziert es von einem einfachen
Socket ins System. Dies gibt ihm den Anschein eines lokal-generierten
Pakets (anders, als ein Paket an eines der Ethertap Devices zu übergeben,
was wie ein Paket aussehen würde, das woanders generiert wurde).




\subsection{Einige Ratschläge}

All diese Tools nehmen an, dass sie alles mit einem Lesen und Schreiben
tun können: Dies ist wahr für die Ethertap Devices, könnte aber nicht
wahr sein, wenn Du etwas Trickreiches mit Pipes machst.



dd can verwendet werden, um Pakete auszuschneiden: dd hat eine obs
(output block size) Option, die verwendet werden kann, um Pakete mit
einem einzigen Schreiben auszugeben.



Teste zuerst auf Erfolg: z.B. ein Test darauf, ob Pakete erfolgreich
geblockt werden. Teste zuerst, ob Pakete normal durchgehen, teste
{\bfseries anschließend}, ob Pakete geblockt werden. Andernfalls könnte 
ein Fehler, der nichts damit zu tun hat, die Pakete aufhalten...



Versuche, exakte Tests zu schreiben, keine `Schreib zufälliges Zeug
und warte ab, was passiert' Tests. Wenn ein exakter Tests nicht
funktioniert, ist es sehr nützlich, das zu wissen. Wenn ein zufälliger
Test nicht funktioniert, hilft das nicht viel.



Wenn ein Tests ohne ein Meldung nicht gelingt, kannst Du ein `-x' (ich
meine `\#! /bin/sh -x') in die erste Zeile des Scripts setzen, um zu
sehen, welches Kommando gerade ausgeführt wird.



Wenn ein Test mal gelingt, mal nicht, überprüfe zufälligen Netzwerkverkehr, der dazwischenkommen könnte 
(versuche, alle externen Schnittstellen zu deaktivieren). Da ich an das gleiche Netzwerk wie Andrew
Trigell angeschlossen bin, werde ich zum Beispiel ständig von
Windows Broadcasts geplagt.




\section{Motivation}

Als ich ipchains entwickelte, merkte ich (in einem dieser Flash-Momente-während-man-auf-Einlaß-wartet in einem chinesischen Restaurant), dass
Paketfiltern an der falschen Stelle ansetzte. Ich kann es jetzt nicht
mehr finden, aber ich erinnere mich daran, dass ich eine Mail an Alan
Cox schrieb, der mir antwortete `Warum beendest Du nicht erst das,
was Du im Moment tust, obwohl Du wahrscheinlich recht hast'. Um es
kurz zu sagen, sollte Pragmatismus über die Richtige Sache siegen.



Als ich ipchains, was ursprüngliche eine kleine Modifikation des
Kernelteils von ipfwadm war, und dann doch eine größere Neu-Überarbeitung 
wurde, beendete und das HOWTO schrieb, bemerkte ich die Verwirrung,
die in der großen Linux-Community über Dinge wie Paketfiltern, Masquerading, Portforwarding und ähnliches herrscht.



Das ist das Gute daran, wenn Du Dein eigener Support bist: Du bekommst
ein besseres Gefühl dafür, was die User versuchen zu tun, und womit
sie noch straucheln. Es ist die größte Auszeichnung für Freie
Software, wenn sie von vielen Usern eingesetzt wird (darum geht es,
richtig?), und das bedeutet, dass man es leicht machen muss. Die
Architektur, und nicht die Dokumenation, ist der Schlüssel hierzu.



Ich hatte also die Erfahrung, sowohl mit dem ipchains-Code, als auch
mit einer guten Vorstellung davon, was die Leute dort draußen taten.
Es gab nur zwei Probleme:



Zuerst wollte ich nicht in den Security-Bereich zurückgehen. Ein
Security-Consultant zu sein ist ein konstantes moralischen Tauziehen
zwischen Deinem Gewissen und Deinem Konto. Auf einem fundamentalen
Level verkaufst Du das Gefühl von Sicherheit, was alles andere ist
als wirkliche Sicherheit. Vielleicht wäre es anders, in einer
Militäreinrichtung zu arbeiten, wo man Sicherheit versteht.



Das zweite Problem ist, dass es nicht nur um Newbies geht; eine wachsende
Anzahl von großen Unternehmen und ISPs benutzen dieses Zeug. Ich brauchte
zuverlässige Informationen von dieser Klasse von Benutzern, wenn es sich
auf die Home-User von Morgen ausbreiten sollte.



Diese Probleme wurden gelöst, als ich auf der Usenix 1998 auf David
Bonn von Watchguard traf. Sie suchten einen Linux-Kernel Programmierer;
Im Ende einigten wir uns darauf, dass ich für einen Monat zu ihrer
Niederlassung in Seattle kommen würde, wo wir sehen würde, ob wir
eine Vereinbarung rausschlagen könnten, in der sie meinen neuen Code
und meine jetzige Support-Arbeit sponsorn würden. Das Gehalt, das sie
mir anboten, war mehr als das, nachdem ich gefragt hatte, ich hatte also
keine Einnahmeverluste. Das bedeutet, dass ich mir für eine Weile keine
Sorgen um Geld zu machen brauche.



Die Nähe zu Watchguard gab mir die Nähe zu den großen Clients, die
ich brauchte, und dass ich unabhänging von ihnen war, erlaubte mir,
alle User (z.b. Watchguard Competitors) gleich zu supporten.



Ich hätte also einfach Netfilter schreiben und ipchains portieren können
und wäre fertig damit gewesen. Leider hätte das den ganzen Masquerading-%
Code im Kernel gelassen: Die Unabhängigkeit zwischen Paketfiltern und
Masquerading ist ein wichtiger Punkt beim Paketfiltern, aber Masquerading
muß auch auf das Netfilter-Rahmenwerk übertragen werden.



Meine Erfahrung mit dem ipfwadm `Schnittstellen-Adressierungs' Feature
hat mich auch gelehrt, dass es keine Hoffnung dafür gab, den Masquera%
ding Code einfach auszulassen und zu erwarten, dass ihn jemand brauchte
und somit selbst die Arbeit einer Portierung zu Netfilter übernehmen
würde.



Ich brauchte also mindestens soviele Features wie im damaligen Code;
vorzugsweise ein paar mehr, um mutige und kreative User zu ermutigen,
es mir nachzumachen. Dies bedeutete, transparente Proxies (zum Glück!),
Masquerading und Port-Forwarding zu ersetzen. Mit anderen Worten, ein
komplettes NAT-Layer.



Sogar, wenn ich mich dazu entschieden hätte, das existierenden
Masquerading Layer zu Portieren, anstatt ein generisches NAT-System
zu schreiben, zeigte der Masquerading Code doch sein Alter und den
Mangel an Wartung. Es gab keinen Masquerading Maintainer, und das
zeigte sich jetzt. Es scheint, dass ernsthafte User Masquerading
generell nicht verwenden und dass es nicht viele Home-User gibt, die
die Arbeit der Wartung übernehmen würden. Tapfere Leute wie Juan
Ciarlante schrieben Fixes, aber der Punkt (der immer weiter nach hinten
verschoben wurde) war erreicht, an dem eine komplette Überarbeitung
nötig war.



Beachte bitte, das nicht ich die Person war, die NAT überarbeitet hat:
Ich verwendete Masquerading nicht mehr und hatte den existierenden Code
zu der Zeit nicht studiert. Das ist wahrscheinlich der Grund, warum es
länger dauerte, als es dauern sollte. Aber das Resultat ist ziemlich
gut, meiner Meinung nach, und ich habe sicher verdammt viel gelernt.
Ohne Zweifel wird die zweite Version noch besser werden, wenn wir sehen,
wie die Leute es einsetzen.




\section{Danke}

Danke denen, die geholfen haben.



\end{document}

