|
par Nicolas Bouliane <nib(at)cookinglinux!org> L´auteur: Nicolas est un jeune guerrier dans la communauté du logiciel libre. Il est accro à GNU/Linux depuis le jour où il l'a installé sur son ordinateur en 1998. Il passe son temps à étudier la couche réseau de linux, à écrire des logiciels libres et à assister à des conférences sur linux, comme la OLS. Quant il n'est pas devant son ordinateur, il aime regarder des films de science-fiction, jouer aux échecs et écouter les discours de Richard Stallman. |
Ecris tes propres règles pour NetFilterRésumé:
L'interface du couple iptables/netfilter nous permet d'ajouter des fonctionnalités.
Pour ce faire, nous écrivons des modules du noyau qui s'enregistrent par dessus cette interface.
Donc, suivant la catégorie de la fonctionnalité, nous écrivons un module pour iptables.
En écrivant notre nouveau module, nous pouvons détecter, transformer, accepter et suivre un
paquet donné. En fait, tu peux faire presque tout ce que tu veux, dans ce monde du filtrage.
Attention, une petite erreur dans un module du noyau peu endommager sévèremment ton ordinateur.
|
/* 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' est le nom de fichier de ta bibliothèque (ie: libipt_ipaddr).
.name = "ipaddr",Le champ suivant, 'version', est la version d'iptables. Les deux suivants sont utilisés pour la corrélation entre la taille de la structure partagée entre l'espace utilisateur et l'espace noyau.
.version = IPTABLES_VERSION, .size = IPT_ALIGN(sizeof(struct ipt_ipaddr_info)), .userspacesize = IPT_ALIGN(sizeof(struct ipt_ipaddr_info)),'Help' est appelée quand l'utilisateur tape 'iptables -m module -h'. 'Parse' est appelée quand tu entres une nouvelle règle, son rôle est de valider les arguments. Quant à 'print', elle est appelée lorsque la commande 'iptables -L' est tapée pour afficher les règles précédemment définies.
.help = &help, .init = &init, .parse = &parse, .final_check = &final_check, .print = &print, .save = &save, .extra_opts = opts };L'infrastructure iptables peut supporteer de multiples bibliothèques partagées. Chacune doit s'enregistrer auprès d'iptables en appelant 'register_match()', qui est définie dans <iptables/iptables.c>. Cette fonction est appelée lorsque le module est chargé par iptables. Pour obtenir plus d'informations: '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;Nous affichons l'adresse source si elle fait partie de la règle.
if (info->flags & IPADDR_SRC) { if (info->flags & IPADDR_SRC_INV) printf("! "); printf("--ipsrc "); print_ipaddr((u_int32_t *)&info->ipaddr.src); }Nous affichons l'adresse de destination si elle fait partie de la règle.
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) {Nous utilisons cette structure spéciale pour stocker les informations que nous voulons partager avec la partie noyau. Le pointeur 'Match' est passé à plusieurs fonctions donc nous travaillons sur la même structure de données. Une fois que la règle est chargée, ce pointeur est copié dans la partie noyau. Comme ça, le module du noyau sait ce que l'utilisateur demande à analyser (et c'est le but, non?).
struct ipt_ipaddr_info *info = (struct ipt_ipaddr_info *)(*match)->data;Chaque arguments correspond à une simple valeur, donc nous pouvons faire des actions spécifiques suivant les arguments passés. Nous verrons plus tard dans cet article comment faire correspondre des valeurs aux arguments.
switch(c) {Tout d'abord, nous testons si l'argument a été utilisé plus d'une fois. Si c'est le cas, nous appelons 'exit_error()' définie dans <iptables/iptables.c>, qui sort immédiatement avec le code de retour 'PARAMETER_PROBLEM' défini dans <iptables/include/iptables_common.h> Autrement, nous initialisons 'flags' et 'info->flags' à la valeur 'IPADDR_SRC' définie dans notre fichier d'en-tête. Nous verrons ce fichier d'en-tête plus tard.
case '1': if (*flags & IPADDR_SRC) exit_error(PARAMETER_PROBLEM, "ipt_ipaddr: Only use --ipsrc once!"); *flags |= IPADDR_SRC; info->flags |= IPADDR_SRC;Nous vérifions si le drapeau inverseur, '!', a été introduit, et nous mettons l'information adéquate dans 'info->flags'.
if (invert) info->flags |= IPADDR_SRC_INV; parse_ipaddr(argv[optind-1], &info->ipaddr.src); break;Dans la même idée, nous vérifions une utilisation multiple et mettons les drapeaux idoines.
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\t\t The incoming ip addr matches.\n" "[!] --ipdst \t\t The outgoing ip addr matches.\n" "\n", IPTABLES_VERSION ); }
#ifndef _IPT_IPADDR_H #define _IPT_IPADDR_HNous avons vu plutôt que nous définissons des drapeaux pour certaines valeurs.
#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 */La structure 'ipt_ipaddr_info' est celle qui sera copiée dans la partie noyau.
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; };
Nous initialisons les champs communs de la structure 'ipt_match'.
static struct ipt_match ipaddr_match = {'Name' est la chaîne du nom de fichier de ton module (ie: ipt_ipaddr).
.name = "ipaddr",Les champs suivant sont les fonction de rappel que l'interface utilisera. 'Match' est appelé lorsque le paquet est passé à ton module.
.match = match, .checkentry = checkentry, .me = THIS_MODULE, };La fonction de votre module noyau init doit appeler 'ipt_register_match()' avec un pointeur sur une structure 'struct ipt_match' pour s'enregistrer auprès de l'interface de netfilter. Cette fonction est appelée au chargement du module.
static int __init init(void) { printk(KERN_INFO "ipt_ipaddr: init!\n"); return ipt_register_match(&ipaddr_match); }La fonction suivante est appelée au déchargement du module. C'est ici que l'on efface nos règles.
static void __exit fini(void) { printk(KERN_INFO "ipt_ipaddr: exit!\n"); ipt_unregister_match(&ipaddr_match); }Nous passons les fonctions qui seront appelées au chargemenr et au déchargement du module.
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) {J'espère que tu te rappelles que nous avons fait la partie utilisateur ! :) Maintenant nous associons la structure copiée dans l'espace utilisateur à la notre.
const struct ipt_skeleton_info *info = matchinfo;'skb' contient le paquet que nous voulons examiner. Pour plus d'information sur cette structure puissante utilisée partout dans la couche tcp/ip de linux, Harald Welte a écrit un excellent article (ftp://ftp.gnumonks.org/pub/doc/skb-doc.html) dessus.
struct iphdr *iph = skb->nh.iph;Ici, nous affichons juste des choses amusantes pour voir à quoi elles ressemblent. La macro 'NIPQUAD', utilisée pour afficher une adresse IP dans un format lisible, est définie dans <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) );Si l'argument '--ipsrc' a été fourni nous regardons si l'adresse source correspond avec celle spécifiée dans la règle. Nous n'oublions pas de prendre en considération le drapeau inverseur: '!'. Si ça ne correspond pas, nous retournons le verdict; 0.
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; } }Ici, nous faisons de même, sauf que nous regardons l'adresse de destination si l'argument '--ipdst' a été fourni.
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; } }Si les deux échouent, nous retournons le verdict 1, qui signifie que le paquet correspond.
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_IPTABLESEnsuite, édites <linux/Documentation/Configure.help> et ajoutes le texte en gras. J'ai copié plus de texte afin de te permettre de trouver où ajouter le tien.
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.Finalement, tu dois ajouter cette ligne en gras dans <linux/net/ipv4/netfilter/Makefile>.
# 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.oMaintenant pour 2.6, les fichiers à éditer sont <linux/net/ipv4/netfilter/Kconfig> et <linux/net/ipv4/netfilter/Makefile>.
Site Web maintenu par l´équipe d´édition LinuxFocus
© Nicolas Bouliane "some rights reserved" see linuxfocus.org/license/ http://www.LinuxFocus.org |
Translation information:
|
2005-04-28, generated by lfparser_pdf version 2.51