Merge to kernel-2.6.20-1.2949.fc6.vs2.2.0.1
[linux-2.6.git] / net / ipv4 / netfilter / ip_conntrack_proto_gre.c
index c386e57..ac1c49e 100644 (file)
@@ -1,15 +1,15 @@
 /*
- * ip_conntrack_proto_gre.c - Version 3.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
@@ -23,7 +23,6 @@
  *
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/timer.h>
 #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>
+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 <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>
@@ -63,7 +59,7 @@ MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE");
 #define DEBUGP(x, args...)
 #define DUMP_TUPLE_GRE(x)
 #endif
-                               
+
 /* GRE KEYMAP HANDLING FUNCTIONS */
 static LIST_HEAD(gre_keymap_list);
 
@@ -77,18 +73,20 @@ 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 = 0;
-
-       READ_LOCK(&ip_ct_gre_lock);
-       km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
-                       struct ip_ct_gre_keymap *, t);
-       if (km)
-               key = km->tuple.src.u.gre.key;
-       READ_UNLOCK(&ip_ct_gre_lock);
-       
+       __be16 key = 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);
+
        DEBUGP("lookup src key 0x%x up key for ", key);
        DUMP_TUPLE_GRE(t);
 
@@ -100,64 +98,42 @@ int
 ip_ct_gre_keymap_add(struct ip_conntrack *ct,
                     struct ip_conntrack_tuple *t, int reply)
 {
-       struct ip_ct_gre_keymap *km, *old;
+       struct ip_ct_gre_keymap **exist_km, *km;
 
        if (!ct->helper || strcmp(ct->helper->name, "pptp")) {
                DEBUGP("refusing to add GRE keymap to non-pptp session\n");
                return -1;
        }
 
-       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));
-
-       if (!reply) {
-               if (ct->help.ct_pptp_info.keymap_orig) {
-                       kfree(km);
-
-                       /* check whether it's a retransmission */
-                       old = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
-                                       struct ip_ct_gre_keymap *, t);
-                       if (old == ct->help.ct_pptp_info.keymap_orig) {
-                               DEBUGP("retransmission\n");
-                               return 0;
-                       }
+       if (!reply)
+               exist_km = &ct->help.ct_pptp_info.keymap_orig;
+       else
+               exist_km = &ct->help.ct_pptp_info.keymap_reply;
 
-                       DEBUGP("trying to override keymap_orig for ct %p\n",
-                               ct);
-                       return -2;
-               }
-               ct->help.ct_pptp_info.keymap_orig = km;
-       } else {
-               if (ct->help.ct_pptp_info.keymap_reply) {
-                       kfree(km);
-
-                       /* check whether it's a retransmission */
-                       old = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
-                                       struct ip_ct_gre_keymap *, t);
-                       if (old == ct->help.ct_pptp_info.keymap_reply) {
-                               DEBUGP("retransmission\n");
+       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_reply for ct %p\n",
-                               ct);
-                       return -2;
                }
-               ct->help.ct_pptp_info.keymap_reply = km;
+               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;
 }
@@ -172,9 +148,9 @@ void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct)
                return;
        }
 
-       WRITE_LOCK(&ip_ct_gre_lock);
+       write_lock_bh(&ip_ct_gre_lock);
        if (ct->help.ct_pptp_info.keymap_orig) {
-               DEBUGP("removing %p from list\n", 
+               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);
@@ -187,7 +163,7 @@ void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct)
                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);
 }
 
 
@@ -208,47 +184,32 @@ 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);
+       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;
+       }
+
        /* 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;
 
-       if (!grehdr || !pgrehdr)
+       if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) {
+               DEBUGP("GRE_VERSION_PPTP but unknown proto\n");
                return 0;
-
-       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 = pgrehdr->call_id;
-                       break;
-
-               default:
-                       printk(KERN_WARNING "unknown GRE version %hu\n",
-                               grehdr->version);
-                       return 0;
        }
 
+       tuple->dst.u.gre.key = pgrehdr->call_id;
        srckey = gre_keymap_lookup(tuple);
-
        tuple->src.u.gre.key = srckey;
-#if 0
-       DEBUGP("found src key %x for tuple ", ntohs(srckey));
-       DUMP_TUPLE_GRE(tuple);
-#endif
 
        return 1;
 }
@@ -257,7 +218,7 @@ 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 ", 
+       return seq_printf(s, "srckey=0x%x dstkey=0x%x ",
                          ntohs(tuple->src.u.gre.key),
                          ntohs(tuple->dst.u.gre.key));
 }
@@ -283,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);
 
@@ -319,9 +281,9 @@ static void gre_destroy(struct ip_conntrack *ct)
 }
 
 /* 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,
@@ -329,41 +291,38 @@ static struct ip_conntrack_protocol gre = {
        .packet          = gre_packet,
        .new             = gre_new,
        .destroy         = gre_destroy,
-       .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_destroy);
-
-module_init(init);
-module_exit(fini);