|
von Nicolas Bouliane <nib(at)cookinglinux!org> Über den Autor: Nicolas ist ein junger Kämpfer in der Gemeinschaft freier Software. Er ist ein GNU/Linux-Abhängiger, seit dem er es im Jahre 1998 auf seinem Rechner installierte. Er verbringt seine Zeit damit, den Linux Netzwerkstack zu untersuchen, freie Software zu schreiben und Linux-bezogene Konferenzen wie OLS zu besuchen. Wenn er nicht vor seinem Computer sitzt, schaut er SciFi-Filme an, spielt Schach und hört Richard Stallmans Reden. Übersetzt ins Deutsche von: Hermann J. Beckers <hj.beckers/at/onlinehome.de> |
Schreiben Ihres eigenen netfilter-TestsZusammenfassung:
Das iptables/netfilter-Rahmenwerk gibt uns die Möglichkeit, Eigenschaften hinzuzufügen.
Dazu schreibt man Kernel-Module, die sich bei diesem Rahmenwerk registrieren. Abhängig von der
Kategorie dieser neuen Eigenschaft schreiben wir auch ein iptables-Modul. Durch das
Schreiben Ihrer neuen Erweiterung können Sie ein bestimmtes Paket testen, verändern,
akzeptieren und verfolgen. Tatsächlich können Sie im Bereich des Filterns fast alles tun,
was Sie möchten.
Beachten Sie, dass ein kleiner Fehler in einem Kernel-Modul Ihren Computer abstürzen
lassen kann. |
/* Include file for additions: new matches and targets. */ struct iptables_match { struct iptables_match *next; ipt_chainlabel name; const char *version; /* Size of match data. */ size_t size; /* Size of match data relevent for userspace comparison purposes */ size_t userspacesize; /* Function which prints out usage message. */ void (*help)(void); /* Initialize the match. */ void (*init)(struct ipt_entry_match *m, unsigned int *nfcache); /* Function which parses command options; returns true if it ate an option */ int (*parse)(int c, char **argv, int invert, unsigned int *flags, const struct ipt_entry *entry, unsigned int *nfcache, struct ipt_entry_match **match); /* Final check; exit if not ok. */ void (*final_check)(unsigned int flags); /* Prints out the match iff non-NULL: put space at end */ void (*print)(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric); /* Saves the match info in parsable form to stdout. */ void (*save)(const struct ipt_ip *ip, const struct ipt_entry_match *match); /* Pointer to list of extra command-line options */ const struct option *extra_opts; /* Ignore these men behind the curtain: */ unsigned int option_offset; struct ipt_entry_match *m; unsigned int mflags; #ifdef NO_SHARED_LIBS unsigned int loaded; /* simulate loading so options are merged properly */ #endif }; |
static struct iptables_match ipaddr = {'Name' ist die Zeichenkette mit dem Namen Ihrer Bibliothek (z. B. libipt_ipaddr).
.name = "ipaddr",Das nächste Feld 'version' ist die Version von iptables. Die beiden nächsten Felder werden benutzt, um eine Korelation zwischen der Größe der zwischen dem Benutzerbereich und dem Kernelbereich gemeinsam genutzten Struktur zu erhalten.
.version = IPTABLES_VERSION, .size = IPT_ALIGN(sizeof(struct ipt_ipaddr_info)), .userspacesize = IPT_ALIGN(sizeof(struct ipt_ipaddr_info)),'Help' wird aufgerufen, wenn der Benutzer 'iptables -m module -h' eingibt. 'Parse' wird aufgerufen, wenn Sie eine neue Regel eingeben, es dient zur Überprüfung der Argumente. 'print' wird von 'iptables -L' aufgerufen, um die vorher eingegebenen Regeln anzuzeigen.
.help = &help, .init = &init, .parse = &parse, .final_check = &final_check, .print = &print, .save = &save, .extra_opts = opts };Die iptables-Infrastruktur kann mehrere gemeinsam genutzte Bibliotheken unterstützen. Jede Bibliothek muss sich bei iptables durch Aufruf von 'register_match()' registrieren, welche in <iptables/iptables.c> definiert ist. Diese Funktion wird aufgerufen, wenn das Modul von iptables geladen wird. Zu weiteren Informationen hierzu siehe 'man dlopen'.
void _init(void) { register_match(&ipaddr); }
static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) { const struct ipt_ipaddr_info *info = (const struct ipt_ipaddr_info *)match->data;Wir geben die Quell-Adresse aus, wenn sie Teil der Regel ist.
if (info->flags & IPADDR_SRC) { if (info->flags & IPADDR_SRC_INV) printf("! "); printf("--ipsrc "); print_ipaddr((u_int32_t *)&info->ipaddr.src); }Wir geben die Ziel-Adresse aus, wenn sie Teil der Regel ist.
if (info->flags & IPADDR_DST) { if (info->flags & IPADDR_DST_INV) printf("! "); printf("--ipdst "); print_ipaddr((u_int32_t *)&info->ipaddr.dst); } }
static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric) { const struct ipt_ipaddr_info *info = (const struct ipt_ipaddr_info *)match->data; if (info->flags & IPADDR_SRC) { printf("src IP "); if (info->flags & IPADDR_SRC_INV) printf("! "); print_ipaddr((u_int32_t *)&info->ipaddr.src); } if (info->flags & IPADDR_DST) { printf("dst IP "); if (info->flags & IPADDR_DST_INV) printf("! "); print_ipaddr((u_int32_t *)&info->ipaddr.dst); } }
static void final_check(unsigned int flags) { if (!flags) exit_error(PARAMETER_PROBLEM, "ipt_ipaddr: Invalid parameters."); }
static int parse(int c, char **argv, int invert, unsigned int *flags, const struct ipt_entry *entry, unsigned int *nfcache, struct ipt_entry_match **match) {Wir benutzten diese spezielle Struktur, um Informationen zu erhalten, die wir mit dem Kernel-Teil teilen. Der 'Match'-Zeiger wird an einige Funktionen weitergereicht, damit wir mit der gleichen Datenstruktur arbeiten können. Sobald die Regel geladen ist, wird dieser Zeiger in den Kernel-Bereich geladen. Auf diese Weise weiss das Kernel-Modul, um welche Analyse der Anwender gebeten hat (und darum geht es doch, oder?).
struct ipt_ipaddr_info *info = (struct ipt_ipaddr_info *)(*match)->data;Jedes Argument korrespondiert mit einem einzelnen Wert, so dass wir spezielle Aktionen entsprechend den eingegebenen Argumenten durchführen können . Wir werden später im Text sehen, wie wir Argumente auf Werte abbilden.
switch(c) {Zunächst testen wir, ob das Argument mehr als einmal benutzt wurde. Wenn dies der Fall zu sein scheint, rufen wir das in <iptables/iptables.c> definierte 'exit_error()', das unmittelbar mit dem Status-Flag 'PARAMETER_PROBLEM' (definiert in <iptables/include/iptables_common.h>), zurückkehrt. Ansonsten setzen wir 'flags' und 'info->flags' auf den in unserer Header-Datei definierten Wert 'IPADDR_SRC'. Diese Header-Datei werden wir später sehen.
case '1': if (*flags & IPADDR_SRC) exit_error(PARAMETER_PROBLEM, "ipt_ipaddr: Only use --ipsrc once!"); *flags |= IPADDR_SRC; info->flags |= IPADDR_SRC;Wir verifizieren, ob das Invert-Flag, '!', eingegeben wurde und setzen entsprechende Informationen in 'info->flags'.
if (invert) info->flags |= IPADDR_SRC_INV; parse_ipaddr(argv[optind-1], &info->ipaddr.src); break;Auf die gleiche Art testen wir auf mehrfache Verwendung und setzen entsprechende Flags.
case '2': if (*flags & IPADDR_DST) exit_error(PARAMETER_PROBLEM, "ipt_ipaddr: Only use --ipdst once!"); *flags |= IPADDR_DST; info->flags |= IPADDR_DST; if (invert) info->flags |= IPADDR_DST_INV; parse_ipaddr(argv[optind-1], &info->ipaddr.dst); break; default: return 0; } return 1; }
static struct option opts[] = { { .name = "ipsrc", .has_arg = 1, .flag = 0, .val = '1' }, { .name = "ipdst", .has_arg = 1, .flag = 0, .val = '2' }, { .name = 0 } };
static void init(struct ipt_entry_match *m, unsigned int *nfcache) { /* Can't cache this */ *nfcache |= NFC_UNKNOWN; }
static void help(void) { printf ( "IPADDR v%s options:\n" "[!] --ipsrc <ip>\t\t The incoming ip addr matches.\n" "[!] --ipdst <ip>\t\t The outgoing ip addr matches.\n" "\n", IPTABLES_VERSION ); }
#ifndef _IPT_IPADDR_H #define _IPT_IPADDR_HWir haben bereits gesehen, dass wir Flags auf bestimmte Werte setzen.
#define IPADDR_SRC 0x01 /* Match source IP addr */ #define IPADDR_DST 0x02 /* Match destination IP addr */ #define IPADDR_SRC_INV 0x10 /* Negate the condition */ #define IPADDR_DST_INV 0x20 /* Negate the condition */Die Struktur 'ipt_ipaddr_info' ist diejenige, welche in den Kernel-Teil kopiert wird.
struct ipt_ipaddr { u_int32_t src, dst; }; struct ipt_ipaddr_info { struct ipt_ipaddr ipaddr; /* Flags from above */ u_int8_t flags; }; #endif
struct ipt_match { struct list_head list; const char name[IPT_FUNCTION_MAXNAMELEN]; /* Return true or false: return FALSE and set *hotdrop = 1 to force immediate packet drop. */ /* Arguments changed since 2.4, as this must now handle non-linear skbs, using skb_copy_bits and skb_ip_make_writable. */ int (*match)(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchinfo, int offset, int *hotdrop); /* Called when user tries to insert an entry of this type. */ /* Should return true or false. */ int (*checkentry)(const char *tablename, const struct ipt_ip *ip, void *matchinfo, unsigned int matchinfosize, unsigned int hook_mask); /* Called when entry of this type deleted. */ void (*destroy)(void *matchinfo, unsigned int matchinfosize); /* Set this to THIS_MODULE. */ struct module *me; };
Wir initialisieren die allgemeinen Felder in der Struktur 'ipt_match'.
static struct ipt_match ipaddr_match = {'Name' ist die Zeichenkette mit dem Dateinamen Ihres Moduls (z. B. ipt_ipaddr).
.name = "ipaddr",Die nächsten Felder enthalten Rücksprungfunktionen, die das Rahmenwerk benutzen wird. 'Match' wird aufgerufen, wenn ein Paket an Ihr Modul weitergereicht wird.
.match = match, .checkentry = checkentry, .me = THIS_MODULE, };Die Init-Funktion Ihres Kernelmoduls muss 'ipt_register_match()' aufrufen mit einem Zeiger auf eine Struktur 'struct ipt_match', um sich beim netfiler-Rahmenwerk zu registrieren. Diese Funktion wird beim Laden des Moduls aufgerufen.
static int __init init(void) { printk(KERN_INFO "ipt_ipaddr: init!\n"); return ipt_register_match(&ipaddr_match); }Beim Entladen des Moduls wird diese Funktion aufgerufen. Hier deregistrieren wir unser Match-Modul.
static void __exit fini(void) { printk(KERN_INFO "ipt_ipaddr: exit!\n"); ipt_unregister_match(&ipaddr_match); }Wir übergeben ihnen Funktionen, die beim Laden und Entladen des Moduls aufgerufen werden.
module_init(init); module_exit(fini);
static int match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchinfo, int offset, const void *hdr, u_int16_t datalen, int *hotdrop) {Sie erinnern sich hoffentlich daran, was wir im Benutzerbereich getan haben! :). Nun übertragen wir die vom Benutzerbereich kopierte Struktur in unsere eigene.
const struct ipt_skeleton_info *info = matchinfo;'skb' enthält das Paket, das wir untersuchen wollen. Zu weiteren Informationen über diese mächtige Struktur, die überall im LInux-TCP/IP-Stack benutzt wird, hat Harald Welte einen exzellenten Artikel (ftp://ftp.gnumonks.org/pub/doc/skb-doc.html) geschrieben.
struct iphdr *iph = skb->nh.iph;Hier geben wir nur einige lustige Sachen aus, um zu sehen, wie sie aussehen. Das Makro 'NIPQUAD' wird benutzt, um eine IP-Adresse in lesbarer Form auszugeben, definiert in <linux/include/linux/kernel.h>.
printk(KERN_INFO "ipt_ipaddr: IN=%s OUT=%s TOS=0x%02X " "TTL=%x SRC=%u.%u.%u.%u DST=%u.%u.%u.%u " "ID=%u IPSRC=%u.%u.%u.%u IPDST=%u.%u.%u.%u\n", in ? (char *)in : "", out ? (char *)out : "", iph->tos, iph->ttl, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr), ntohs(iph->id), NIPQUAD(info->ipaddr.src), NIPQUAD(info->ipaddr.dst) );Wenn das Argument '--ipsrc' übergeben wurde, schauen wir, ob die Quelladresse mit der in der Regel angegebenen übereinstimmt. Wir vergessen nicht, das Invert-Flag '!' zu berücksichtigen. Wenn es keine Übereinstimmung gibt, geben wir das Urteil 0 zurück.
if (info->flags & IPADDR_SRC) { if ( (ntohl(iph->saddr) != ntohl(info->ipaddr.src)) ^ !!(info->flags & IPADDR_SRC_INV) ) { printk(KERN_NOTICE "src IP %u.%u.%u.%u is not matching %s.\n", NIPQUAD(info->ipaddr.src), info->flags & IPADDR_SRC_INV ? " (INV)" : ""); return 0; } }Hier machen wir das gleiche mit der Ausnahme, das wir auf die Zieladresse schauen, wenn '--ipdst' eingegeben wurde.
if (info->flags & IPADDR_DST) { if ( (ntohl(iph->daddr) != ntohl(info->ipaddr.dst)) ^ !!(info->flags & IPADDR_DST_INV) ) { printk(KERN_NOTICE "dst IP %u.%u.%u.%u is not matching%s.\n", NIPQUAD(info->ipaddr.dst), info->flags & IPADDR_DST_INV ? " (INV)" : ""); return 0; } }Wenn beides nicht zutrifft, geben wir das Urteil 1 zurück, was bedeutet, dass das Paket übereinstimmt.
return 1; }
static int checkentry(const char *tablename, const struct ipt_ip *ip, void *matchinfo, unsigned int matchsize, unsigned int hook_mask) { const struct ipt_skeleton_info *info = matchinfo; if (matchsize != IPT_ALIGN(sizeof(struct ipt_skeleton_info))) { printk(KERN_ERR "ipt_skeleton: matchsize differ, you may have forgotten to recompile me.\n"); return 0; } printk(KERN_INFO "ipt_skeleton: Registered in the %s table, hook=%x, proto=%u\n", tablename, hook_mask, ip->proto); return 1; }
PF_EXT_SLIB:=ah addrtype comment connlimit connmark conntrack dscp ecn esp hashlimit helper icmp iprange length limit ipaddr mac mark multiport owner physdev pkttype realm rpc sctp standard state tcp tcpmss tos ttl udp unclean CLASSIFY CONNMARK DNAT DSCP ECN LOG MARK MASQUERADE MIRROR NETMAP NOTRACK REDIRECT REJECT SAME SNAT TARPIT TCPMSS TOS TRACE TTL ULOG
# The simple matches. dep_tristate ' limit match support' CONFIG_IP_NF_MATCH_LIMIT $CONFIG_IP_NF_IPTABLES dep_tristate ' ipaddr match support' CONFIG_IP_NF_MATCH_IPADDR $CONFIG_IP_NF_IPTABLESDann editieren Sie <linux/Documentation/Configure.help> und fügen den fett dargestellten Text hinzu. Ich habe etwas Text kopiert, um Ihnen dabei zu helfen, wo Sie Ihren hinzufügen können.
limit match support CONFIG_IP_NF_MATCH_LIMIT limit matching allows you to control the rate at which a rule can be ... ipaddr match support CONFIG_IP_NF_MATCH_IPADDR ipaddr matching. etc etc.Zum Schluss müssen Sie diese fett dargestellte Zeile in <linux/net/ipv4/netfilter/Makefile> einfügen.
# matches obj-$(CONFIG_IP_NF_MATCH_HELPER) += ipt_helper.o obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o obj-$(CONFIG_IP_NF_MATCH_IPADDR) += ipt_ipaddr.oFür 2.6 sind die zu editierenden Dateien <linux/net/ipv4/netfilter/Kconfig> und <linux/net/ipv4/netfilter/Makefile>.
Der LinuxFocus Redaktion schreiben
© Nicolas Bouliane "some rights reserved" see linuxfocus.org/license/ http://www.LinuxFocus.org |
Autoren und Übersetzer:
|
2005-07-23, generated by lfparser_pdf version 2.51