diff options
author | Andreas Baumann <mail@andreasbaumann.cc> | 2015-01-03 12:04:58 +0100 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2015-01-03 12:04:58 +0100 |
commit | 008d0be72b2f160382c6e880765e96b64a050c65 (patch) | |
tree | 36f48a98a3815a408e2ce1693dd182af90f80305 /release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_proto_gre.c | |
parent | 611becfb8726c60cb060368541ad98191d4532f5 (diff) | |
download | tomato-008d0be72b2f160382c6e880765e96b64a050c65.tar.gz tomato-008d0be72b2f160382c6e880765e96b64a050c65.tar.bz2 |
imported original firmware WRT54GL_v4.30.11_11_US
Diffstat (limited to 'release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_proto_gre.c')
-rw-r--r-- | release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_proto_gre.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_proto_gre.c b/release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_proto_gre.c new file mode 100644 index 00000000..4ff06dcb --- /dev/null +++ b/release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_proto_gre.c @@ -0,0 +1,320 @@ +/* + * ip_conntrack_proto_gre.c - Version 1.11 + * + * Connection tracking protocol helper module for GRE. + * + * GRE is a generic encapsulation protocol, which is generally not very + * suited for NAT, as it has no protocol-specific part as port numbers. + * + * It has an optional key field, which may help us distinguishing two + * connections between the same two hosts. + * + * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 + * + * PPTP is built on top of a modified version of GRE, and has a mandatory + * field called "CallID", which serves us for the same purpose as the key + * field in plain GRE. + * + * Documentation about PPTP can be found in RFC 2637 + * + * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org> + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/timer.h> +#include <linux/netfilter.h> +#include <linux/ip.h> +#include <linux/in.h> +#include <linux/list.h> + +#include <linux/netfilter_ipv4/lockhelp.h> + +DECLARE_RWLOCK(ip_ct_gre_lock); +#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_ct_gre_lock) +#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_ct_gre_lock) + +#include <linux/netfilter_ipv4/listhelp.h> +#include <linux/netfilter_ipv4/ip_conntrack_protocol.h> +#include <linux/netfilter_ipv4/ip_conntrack_helper.h> +#include <linux/netfilter_ipv4/ip_conntrack_core.h> + +#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> +#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); +MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE"); + +/* shamelessly stolen from ip_conntrack_proto_udp.c */ +#define GRE_TIMEOUT (30*HZ) +#define GRE_STREAM_TIMEOUT (180*HZ) + +#define DEBUGP(x, args...) +#define DUMP_TUPLE_GRE(x) + +/* GRE KEYMAP HANDLING FUNCTIONS */ +static LIST_HEAD(gre_keymap_list); + +static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km, + const struct ip_conntrack_tuple *t) +{ + return ((km->tuple.src.ip == t->src.ip) && + (km->tuple.dst.ip == t->dst.ip) && + (km->tuple.dst.protonum == t->dst.protonum) && + (km->tuple.dst.u.all == t->dst.u.all)); +} + +/* look up the source key for a given tuple */ +static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t) +{ + struct ip_ct_gre_keymap *km; + u_int32_t key; + + READ_LOCK(&ip_ct_gre_lock); + km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn, + struct ip_ct_gre_keymap *, t); + if (!km) { + READ_UNLOCK(&ip_ct_gre_lock); + return 0; + } + + key = km->tuple.src.u.gre.key; + READ_UNLOCK(&ip_ct_gre_lock); + + return key; +} + +/* add a single keymap entry, associate with specified expect */ +int ip_ct_gre_keymap_add(struct ip_conntrack_expect *exp, + struct ip_conntrack_tuple *t, int reply) +{ + struct ip_ct_gre_keymap *km; + + km = kmalloc(sizeof(*km), GFP_ATOMIC); + if (!km) + return -1; + + /* initializing list head should be sufficient */ + memset(km, 0, sizeof(*km)); + + memcpy(&km->tuple, t, sizeof(*t)); + km->master = exp; + + if (!reply) + exp->proto.gre.keymap_orig = km; + else + exp->proto.gre.keymap_reply = km; + + DEBUGP("adding new entry %p: ", km); + DUMP_TUPLE_GRE(&km->tuple); + + WRITE_LOCK(&ip_ct_gre_lock); + list_append(&gre_keymap_list, km); + WRITE_UNLOCK(&ip_ct_gre_lock); + + return 0; +} + +/* change the tuple of a keymap entry (used by nat helper) */ +void ip_ct_gre_keymap_change(struct ip_ct_gre_keymap *km, + struct ip_conntrack_tuple *t) +{ + DEBUGP("changing entry %p to: ", km); + DUMP_TUPLE_GRE(t); + + WRITE_LOCK(&ip_ct_gre_lock); + memcpy(&km->tuple, t, sizeof(km->tuple)); + WRITE_UNLOCK(&ip_ct_gre_lock); +} + + +/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */ + +/* invert gre part of tuple */ +static int gre_invert_tuple(struct ip_conntrack_tuple *tuple, + const struct ip_conntrack_tuple *orig) +{ + tuple->dst.u.gre.protocol = orig->dst.u.gre.protocol; + tuple->dst.u.gre.version = orig->dst.u.gre.version; + + tuple->dst.u.gre.key = orig->src.u.gre.key; + tuple->src.u.gre.key = orig->dst.u.gre.key; + + return 1; +} + +/* gre hdr info to tuple */ +static int gre_pkt_to_tuple(const void *datah, size_t datalen, + struct ip_conntrack_tuple *tuple) +{ + struct gre_hdr *grehdr = (struct gre_hdr *) datah; + struct gre_hdr_pptp *pgrehdr = (struct gre_hdr_pptp *) datah; + u_int32_t srckey; + + /* core guarantees 8 protocol bytes, no need for size check */ + + tuple->dst.u.gre.version = grehdr->version; + tuple->dst.u.gre.protocol = grehdr->protocol; + + switch (grehdr->version) { + case GRE_VERSION_1701: + if (!grehdr->key) { + DEBUGP("Can't track GRE without key\n"); + return 0; + } + tuple->dst.u.gre.key = *(gre_key(grehdr)); + break; + + case GRE_VERSION_PPTP: + if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) { + DEBUGP("GRE_VERSION_PPTP but unknown proto\n"); + return 0; + } + tuple->dst.u.gre.key = htonl(ntohs(pgrehdr->call_id)); + break; + + default: + printk(KERN_WARNING "unknown GRE version %hu\n", + tuple->dst.u.gre.version); + return 0; + } + + srckey = gre_keymap_lookup(tuple); + + tuple->src.u.gre.key = srckey; + + return 1; +} + +/* print gre part of tuple */ +static unsigned int gre_print_tuple(char *buffer, + const struct ip_conntrack_tuple *tuple) +{ + return sprintf(buffer, "version=%d protocol=0x%04x srckey=0x%x dstkey=0x%x ", + tuple->dst.u.gre.version, + ntohs(tuple->dst.u.gre.protocol), + ntohl(tuple->src.u.gre.key), + ntohl(tuple->dst.u.gre.key)); +} + +/* print private data for conntrack */ +static unsigned int gre_print_conntrack(char *buffer, + const struct ip_conntrack *ct) +{ + return sprintf(buffer, "timeout=%u, stream_timeout=%u ", + (ct->proto.gre.timeout / HZ), + (ct->proto.gre.stream_timeout / HZ)); +} + +/* Returns verdict for packet, and may modify conntrack */ +static int gre_packet(struct ip_conntrack *ct, + struct iphdr *iph, size_t len, + enum ip_conntrack_info conntrackinfo) +{ + /* If we've seen traffic both ways, this is a GRE connection. + * Extend timeout. */ + if (ct->status & IPS_SEEN_REPLY) { + ip_ct_refresh(ct, ct->proto.gre.stream_timeout); + /* Also, more likely to be important, and not a probe. */ + set_bit(IPS_ASSURED_BIT, &ct->status); + } else + ip_ct_refresh(ct, ct->proto.gre.timeout); + + return NF_ACCEPT; +} + +/* Called when a new connection for this protocol found. */ +static int gre_new(struct ip_conntrack *ct, + struct iphdr *iph, size_t len) +{ + DEBUGP(": "); + DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + + /* initialize to sane value. Ideally a conntrack helper + * (e.g. in case of pptp) is increasing them */ + ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT; + ct->proto.gre.timeout = GRE_TIMEOUT; + + return 1; +} + +/* Called when a conntrack entry has already been removed from the hashes + * and is about to be deleted from memory */ +static void gre_destroy(struct ip_conntrack *ct) +{ + struct ip_conntrack_expect *master = ct->master; + + DEBUGP(" entering\n"); + + if (!master) { + DEBUGP("no master exp for ct %p\n", ct); + return; + } + + WRITE_LOCK(&ip_ct_gre_lock); + if (master->proto.gre.keymap_orig) { + DEBUGP("removing %p from list\n", master->proto.gre.keymap_orig); + list_del(&master->proto.gre.keymap_orig->list); + kfree(master->proto.gre.keymap_orig); + } + if (master->proto.gre.keymap_reply) { + DEBUGP("removing %p from list\n", master->proto.gre.keymap_reply); + list_del(&master->proto.gre.keymap_reply->list); + kfree(master->proto.gre.keymap_reply); + } + WRITE_UNLOCK(&ip_ct_gre_lock); +} + +/* protocol helper struct */ +static struct ip_conntrack_protocol gre = { { NULL, NULL }, IPPROTO_GRE, + "gre", + gre_pkt_to_tuple, + gre_invert_tuple, + gre_print_tuple, + gre_print_conntrack, + gre_packet, + gre_new, + gre_destroy, + NULL, + THIS_MODULE }; + +/* ip_conntrack_proto_gre initialization */ +static int __init init(void) +{ + int retcode; + + if ((retcode = ip_conntrack_protocol_register(&gre))) { + printk(KERN_ERR "Unable to register conntrack protocol " + "helper for gre: %d\n", retcode); + return -EIO; + } + + return 0; +} + +static void __exit fini(void) +{ + struct list_head *pos, *n; + + /* delete all keymap entries */ + WRITE_LOCK(&ip_ct_gre_lock); + list_for_each_safe(pos, n, &gre_keymap_list) { + DEBUGP("deleting keymap %p\n", pos); + list_del(pos); + kfree(pos); + } + WRITE_UNLOCK(&ip_ct_gre_lock); + + ip_conntrack_protocol_unregister(&gre); +} + +EXPORT_SYMBOL(ip_ct_gre_keymap_add); +EXPORT_SYMBOL(ip_ct_gre_keymap_change); + +module_init(init); +module_exit(fini); |