Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / net / ipv4 / netfilter / ip_conntrack_proto_gre.c
index 2694e95..5679479 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * ip_conntrack_proto_gre.c - Version 2.0 
+ * ip_conntrack_proto_gre.c - Version 3.0 
  *
  * Connection tracking protocol helper module for GRE.
  *
@@ -17,7 +17,7 @@
  *
  * Documentation about PPTP can be found in RFC 2637
  *
- * (C) 2000-2004 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  *
 #include <linux/ip.h>
 #include <linux/in.h>
 #include <linux/list.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.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)
+static DEFINE_RWLOCK(ip_ct_gre_lock);
+#define ASSERT_READ_LOCK(x)
+#define ASSERT_WRITE_LOCK(x)
 
 #include <linux/netfilter_ipv4/listhelp.h>
 #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
@@ -55,11 +55,10 @@ 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)
@@ -81,82 +80,95 @@ static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km,
 static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t)
 {
        struct ip_ct_gre_keymap *km;
-       u_int32_t key;
+       u_int32_t key = 0;
 
-       READ_LOCK(&ip_ct_gre_lock);
+       read_lock_bh(&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);
+       if (km)
+               key = km->tuple.src.u.gre.key;
+       read_unlock_bh(&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, *old;
 
-       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));
+       if (!reply) 
+               exist_km = &ct->help.ct_pptp_info.keymap_orig;
+       else
+               exist_km = &ct->help.ct_pptp_info.keymap_reply;
+
+       if (*exist_km) {
+               /* check whether it's a retransmission */
+               old = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
+                               struct ip_ct_gre_keymap *, t);
+               if (old == *exist_km) {
+                       DEBUGP("retransmission\n");
+                       return 0;
+               }
 
-       memcpy(&km->tuple, t, sizeof(*t));
+               DEBUGP("trying to override keymap_%s for ct %p\n", 
+                       reply? "reply":"orig", ct);
+               return -EEXIST;
+       }
 
-       if (!reply)
-               exp->proto.gre.keymap_orig = km;
-       else
-               exp->proto.gre.keymap_reply = km;
+       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);
+       write_lock_bh(&ip_ct_gre_lock);
        list_append(&gre_keymap_list, km);
-       WRITE_UNLOCK(&ip_ct_gre_lock);
+       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 +189,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;
+       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;
@@ -226,8 +224,8 @@ 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));
+                         ntohs(tuple->src.u.gre.key),
+                         ntohs(tuple->dst.u.gre.key));
 }
 
 /* print private data for conntrack */
@@ -251,6 +249,7 @@ 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);
@@ -277,16 +276,13 @@ 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 */
@@ -300,43 +296,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); 
 }
 
 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);