This is the lowest layer of hooking into the kernel stack
You can write your own kernel modules which hook directly into the IP stack (or others)
/* Rusty's Dumb netfilter hook example */ #include <linux/config.h> #include <linux/module.h> #include <linux/netfilter_ipv4.h> #include <linux/ip.h> /* The work comes in here from netfilter.c. */ static unsigned int stupid_hook(unsigned int hook, struct sk_buff **pskb, const struct net_device *indev, const struct net_device *outdev, int (*okfn)(struct sk_buff *)) { if ((*pskb)->len == 200) return NF_DROP; return NF_ACCEPT; } static struct nf_hook_ops stupid_ops = { { NULL, NULL }, stupid_hook, PF_INET, NF_IP_POST_ROUTING, 0 }; static int __init init(void) { return nf_register_hook(&stupid_ops); } static void __exit fini(void) { nf_unregister_hook(&linuxmag_ops); } module_init(init); module_exit(fini);
You can use the QUEUE target to pass packets to an asynchronous handler
The most common handler is James Morris' ip_queue handler
/* Stupidity in userspace */ #include <sys/types.h> #include <limits.h> #include <net/if.h> #include <netinet/ip.h> #include <linux/netfilter_ipv4.h> #include "libipq/libipq.h" int main(void) { struct ipq_handle *h; unsigned char packet[65536]; if ((h = ipq_create_handle(0)) == NULL) exit(1); /* Tell the queue to send packets up to size BUFSIZE */ rval = ipq_set_mode(h, IPQ_COPY_META, 0); if (rval < 0) goto die; while ((rval = ipq_read(h, packet, sizeof(packet), 0)) >= 0) { if (ipq_message_type(packet) == IPQM_PACKET) { ipq_packet_msg_t *msg = ipq_get_packet(packet); int verdict = NF_ACCEPT; if (m->data_len == 200) verdict = NF_DROP; ipq_set_verdict(h, m->packet_id, verdict, m->data_len, m->payload); } else { fprintf(stderr, "Received error message %d\n", ipq_get_msgerr(packet)); break; } } die: ipq_destroy_handle(h); exit(1); }
We've seen the limited REJECT target extension.
Let's write a `length' match extension (James Morris)
#ifndef _IPT_LENGTH_H #define _IPT_LENGTH_H struct ipt_length_info { u_int16_t length; }; #endif /*_IPT_LENGTH_H*/ /* Kernel module to match packet length. */ #include <linux/module.h> #include <linux/skbuff.h> #include <linux/netfilter_ipv4/ipt_length.h> #include <linux/netfilter_ipv4/ip_tables.h> 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) { const struct ipt_length_info *info = matchinfo; return skb->len == info->length; } static int checkentry(const char *tablename, const struct ipt_ip *ip, void *matchinfo, unsigned int matchsize, unsigned int hook_mask) { if (matchsize != IPT_ALIGN(sizeof(struct ipt_length_info))) return 0; return 1; } static struct ipt_match length_match = { { NULL, NULL }, "length", &match, &checkentry, NULL, THIS_MODULE }; static int __init init(void) { return ipt_register_match(&length_match); } static void __exit fini(void) { ipt_unregister_match(&length_match); } module_init(init); module_exit(fini);
We need a shared library to supplement command line:
/* Shared library add-on to iptables to add packet length matching support. */ #include <stdio.h> #include <netdb.h> #include <string.h> #include <stdlib.h> #include <getopt.h> #include <iptables.h> #include <linux/netfilter_ipv4/ipt_length.h> /* Function which prints out usage message. */ static void help(void) { printf("length v%s options:\n" "--length length Match this packet length\n", NETFILTER_VERSION); } static struct option opts[] = { { "length", 1, 0, '1' }, {0} }; /* Initialize the match. */ static void init(struct ipt_entry_match *m, unsigned int *nfcache) { *nfcache |= NFC_UNKNOWN; } /* Function which parses command options; returns true if it ate an option */ 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) { struct ipt_length_info *info = (struct ipt_length_info *)(*match)->data; int len; switch (c) { case '1': if (*flags) exit_error(PARAMETER_PROBLEM, "length: `--length' may only be " "specified once"); len = string_to_number(argv[optind-1], 0, 0xFFFF); if (len == -1) exit_error(PARAMETER_PROBLEM, "length invalid: `%s'\n", s); *flags = 1; break; default: return 0; } return 1; } /* Final check; must have specified --length. */ static void final_check(unsigned int flags) { if (!flags) exit_error(PARAMETER_PROBLEM, "length: You must specify `--length'"); } /* Common match printing code. */ static void print_length(struct ipt_length_info *info) { printf("%u ", info->length); } /* Prints out the matchinfo. */ static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric) { printf("length "); print_length((struct ipt_length_info *)match->data); } /* Saves the union ipt_matchinfo in parsable form to stdout. */ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) { printf("--length "); print_length((struct ipt_length_info *)match->data); } struct iptables_match length = { NULL, "length", NETFILTER_VERSION, sizeof(struct ipt_length_info), sizeof(struct ipt_length_info), &help, &init, &parse, &final_check, &print, &save, opts }; void _init(void) { register_match(&length); }
Using it is now simple a matter of
iptables -m length --length 100 -j DROP
Both the connection tracking code and NAT code can be taught about new transport protocols (eg. PPTP)
Both can also have `helpers' to assist with particular application protocols (eg. ftp, irc, etc).
The framework is very extensible
Even without altering the core code, alot of flexibility is given at various levels
Documentation is fairly good (netfilter hacking HOWTO)
Flexibility should increase design life