X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fnetfilter%2Fip_conntrack_proto_gre.c;h=ac1c49ef36a9540efcb4a26e392f112ba992a2d7;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=2694e9571b87c0a71d09ee1cbe1348bbd9d7e146;hpb=a2c21200f1c81b08cb55e417b68150bba439b646;p=linux-2.6.git diff --git a/net/ipv4/netfilter/ip_conntrack_proto_gre.c b/net/ipv4/netfilter/ip_conntrack_proto_gre.c index 2694e9571..ac1c49ef3 100644 --- a/net/ipv4/netfilter/ip_conntrack_proto_gre.c +++ b/net/ipv4/netfilter/ip_conntrack_proto_gre.c @@ -1,15 +1,15 @@ /* - * ip_conntrack_proto_gre.c - Version 2.0 + * ip_conntrack_proto_gre.c - Version 3.0 * * 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 + * 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 + * 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 @@ -17,13 +17,12 @@ * * Documentation about PPTP can be found in RFC 2637 * - * (C) 2000-2004 by Harald Welte + * (C) 2000-2005 by Harald Welte * * Development of this code funded by Astaro AG (http://www.astaro.com/) * */ -#include #include #include #include @@ -31,14 +30,11 @@ #include #include #include +#include +#include -#include +static DEFINE_RWLOCK(ip_ct_gre_lock); -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 #include #include #include @@ -55,16 +51,15 @@ MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE"); #define GRE_STREAM_TIMEOUT (180*HZ) #if 0 -#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ - ": " format, ## args) -#define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x:%u:0x%x\n", \ - NIPQUAD((x)->src.ip), ntohl((x)->src.u.gre.key), \ - NIPQUAD((x)->dst.ip), ntohl((x)->dst.u.gre.key)) +#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args) +#define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x\n", \ + NIPQUAD((x)->src.ip), ntohs((x)->src.u.gre.key), \ + NIPQUAD((x)->dst.ip), ntohs((x)->dst.u.gre.key)) #else #define DEBUGP(x, args...) #define DUMP_TUPLE_GRE(x) #endif - + /* GRE KEYMAP HANDLING FUNCTIONS */ static LIST_HEAD(gre_keymap_list); @@ -78,85 +73,97 @@ static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km, } /* look up the source key for a given tuple */ -static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t) +static __be16 gre_keymap_lookup(struct ip_conntrack_tuple *t) { struct ip_ct_gre_keymap *km; - u_int32_t key; + __be16 key = 0; - 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; + read_lock_bh(&ip_ct_gre_lock); + list_for_each_entry(km, &gre_keymap_list, list) { + if (gre_key_cmpfn(km, t)) { + key = km->tuple.src.u.gre.key; + break; + } } + read_unlock_bh(&ip_ct_gre_lock); - key = km->tuple.src.u.gre.key; - READ_UNLOCK(&ip_ct_gre_lock); + DEBUGP("lookup src key 0x%x up key for ", key); + DUMP_TUPLE_GRE(t); 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) +/* add a single keymap entry, associate with specified master ct */ +int +ip_ct_gre_keymap_add(struct ip_conntrack *ct, + struct ip_conntrack_tuple *t, int reply) { - struct ip_ct_gre_keymap *km; + struct ip_ct_gre_keymap **exist_km, *km; - km = kmalloc(sizeof(*km), GFP_ATOMIC); - if (!km) + if (!ct->helper || strcmp(ct->helper->name, "pptp")) { + DEBUGP("refusing to add GRE keymap to non-pptp session\n"); return -1; - - /* initializing list head should be sufficient */ - memset(km, 0, sizeof(*km)); - - memcpy(&km->tuple, t, sizeof(*t)); + } if (!reply) - exp->proto.gre.keymap_orig = km; + exist_km = &ct->help.ct_pptp_info.keymap_orig; else - exp->proto.gre.keymap_reply = km; + exist_km = &ct->help.ct_pptp_info.keymap_reply; + + if (*exist_km) { + /* check whether it's a retransmission */ + list_for_each_entry(km, &gre_keymap_list, list) { + if (gre_key_cmpfn(km, t) && km == *exist_km) + return 0; + } + DEBUGP("trying to override keymap_%s for ct %p\n", + reply? "reply":"orig", ct); + return -EEXIST; + } + + km = kmalloc(sizeof(*km), GFP_ATOMIC); + if (!km) + return -ENOMEM; + + memcpy(&km->tuple, t, sizeof(*t)); + *exist_km = 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); + write_lock_bh(&ip_ct_gre_lock); + list_add_tail(&km->list, &gre_keymap_list); + write_unlock_bh(&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) +/* destroy the keymap entries associated with specified master ct */ +void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct) { - DEBUGP("changing entry %p to: ", km); - DUMP_TUPLE_GRE(t); + DEBUGP("entering for ct %p\n", ct); - WRITE_LOCK(&ip_ct_gre_lock); - memcpy(&km->tuple, t, sizeof(km->tuple)); - WRITE_UNLOCK(&ip_ct_gre_lock); -} + if (!ct->helper || strcmp(ct->helper->name, "pptp")) { + DEBUGP("refusing to destroy GRE keymap to non-pptp session\n"); + return; + } -/* destroy the keymap entries associated with specified expect */ -void ip_ct_gre_keymap_destroy(struct ip_conntrack_expect *exp) -{ - DEBUGP("entering for exp %p\n", exp); - WRITE_LOCK(&ip_ct_gre_lock); - if (exp->proto.gre.keymap_orig) { - DEBUGP("removing %p from list\n", exp->proto.gre.keymap_orig); - list_del(&exp->proto.gre.keymap_orig->list); - kfree(exp->proto.gre.keymap_orig); - exp->proto.gre.keymap_orig = NULL; + write_lock_bh(&ip_ct_gre_lock); + if (ct->help.ct_pptp_info.keymap_orig) { + DEBUGP("removing %p from list\n", + ct->help.ct_pptp_info.keymap_orig); + list_del(&ct->help.ct_pptp_info.keymap_orig->list); + kfree(ct->help.ct_pptp_info.keymap_orig); + ct->help.ct_pptp_info.keymap_orig = NULL; } - if (exp->proto.gre.keymap_reply) { - DEBUGP("removing %p from list\n", exp->proto.gre.keymap_reply); - list_del(&exp->proto.gre.keymap_reply->list); - kfree(exp->proto.gre.keymap_reply); - exp->proto.gre.keymap_reply = NULL; + if (ct->help.ct_pptp_info.keymap_reply) { + DEBUGP("removing %p from list\n", + ct->help.ct_pptp_info.keymap_reply); + list_del(&ct->help.ct_pptp_info.keymap_reply->list); + kfree(ct->help.ct_pptp_info.keymap_reply); + ct->help.ct_pptp_info.keymap_reply = NULL; } - WRITE_UNLOCK(&ip_ct_gre_lock); + write_unlock_bh(&ip_ct_gre_lock); } @@ -177,45 +184,31 @@ static int gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, struct ip_conntrack_tuple *tuple) { - struct gre_hdr _grehdr, *grehdr; struct gre_hdr_pptp _pgrehdr, *pgrehdr; - u_int32_t srckey; + __be16 srckey; + struct gre_hdr _grehdr, *grehdr; + /* first only delinearize old RFC1701 GRE header */ grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr); - pgrehdr = skb_header_pointer(skb, dataoff, sizeof(_pgrehdr), &_pgrehdr); + if (!grehdr || grehdr->version != GRE_VERSION_PPTP) { + /* try to behave like "ip_conntrack_proto_generic" */ + tuple->src.u.all = 0; + tuple->dst.u.all = 0; + return 1; + } - if (!grehdr || !pgrehdr) - return 0; + /* PPTP header is variable length, only need up to the call_id field */ + pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr); + if (!pgrehdr) + return 1; - 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", - grehdr->version); - return 0; + if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) { + DEBUGP("GRE_VERSION_PPTP but unknown proto\n"); + return 0; } + tuple->dst.u.gre.key = pgrehdr->call_id; srckey = gre_keymap_lookup(tuple); - -#if 0 - DEBUGP("found src key %x for tuple ", ntohl(srckey)); - DUMP_TUPLE_GRE(tuple); -#endif tuple->src.u.gre.key = srckey; return 1; @@ -225,9 +218,9 @@ static int gre_pkt_to_tuple(const struct sk_buff *skb, static int gre_print_tuple(struct seq_file *s, const struct ip_conntrack_tuple *tuple) { - return seq_printf(s, "srckey=0x%x dstkey=0x%x ", - ntohl(tuple->src.u.gre.key), - ntohl(tuple->dst.u.gre.key)); + return seq_printf(s, "srckey=0x%x dstkey=0x%x ", + ntohs(tuple->src.u.gre.key), + ntohs(tuple->dst.u.gre.key)); } /* print private data for conntrack */ @@ -251,17 +244,18 @@ static int gre_packet(struct ip_conntrack *ct, ct->proto.gre.stream_timeout); /* Also, more likely to be important, and not a probe. */ set_bit(IPS_ASSURED_BIT, &ct->status); + ip_conntrack_event_cache(IPCT_STATUS, skb); } else ip_ct_refresh_acct(ct, conntrackinfo, skb, ct->proto.gre.timeout); - + return NF_ACCEPT; } /* Called when a new connection for this protocol found. */ static int gre_new(struct ip_conntrack *ct, const struct sk_buff *skb) -{ +{ DEBUGP(": "); DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); @@ -277,22 +271,19 @@ static int gre_new(struct ip_conntrack *ct, * and is about to be deleted from memory */ static void gre_destroy(struct ip_conntrack *ct) { - struct ip_conntrack_expect *master = ct->master; - + struct ip_conntrack *master = ct->master; DEBUGP(" entering\n"); - if (!master) { - DEBUGP("no master exp for ct %p\n", ct); - return; - } - - ip_ct_gre_keymap_destroy(master); + if (!master) + DEBUGP("no master !?!\n"); + else + ip_ct_gre_keymap_destroy(master); } /* protocol helper struct */ -static struct ip_conntrack_protocol gre = { +static struct ip_conntrack_protocol gre = { .proto = IPPROTO_GRE, - .name = "gre", + .name = "gre", .pkt_to_tuple = gre_pkt_to_tuple, .invert_tuple = gre_invert_tuple, .print_tuple = gre_print_tuple, @@ -300,43 +291,38 @@ static struct ip_conntrack_protocol gre = { .packet = gre_packet, .new = gre_new, .destroy = gre_destroy, - .exp_matches_pkt = NULL, - .me = THIS_MODULE + .me = THIS_MODULE, +#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ + defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) + .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr, + .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple, +#endif }; /* ip_conntrack_proto_gre initialization */ -static int __init init(void) +int __init ip_ct_proto_gre_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; + return ip_conntrack_protocol_register(&gre); } -static void __exit fini(void) +/* This cannot be __exit, as it is invoked from ip_conntrack_helper_pptp.c's + * init() code on errors. + */ +void ip_ct_proto_gre_fini(void) { struct list_head *pos, *n; /* delete all keymap entries */ - WRITE_LOCK(&ip_ct_gre_lock); + write_lock_bh(&ip_ct_gre_lock); list_for_each_safe(pos, n, &gre_keymap_list) { DEBUGP("deleting keymap %p at module unload time\n", pos); list_del(pos); kfree(pos); } - WRITE_UNLOCK(&ip_ct_gre_lock); + write_unlock_bh(&ip_ct_gre_lock); - ip_conntrack_protocol_unregister(&gre); + ip_conntrack_protocol_unregister(&gre); } EXPORT_SYMBOL(ip_ct_gre_keymap_add); -EXPORT_SYMBOL(ip_ct_gre_keymap_change); EXPORT_SYMBOL(ip_ct_gre_keymap_destroy); - -module_init(init); -module_exit(fini);