add pptp nat fix (should fix #571, needs to be tested)
authorFelix Fietkau <nbd@openwrt.org>
Tue, 5 Dec 2006 23:51:29 +0000 (23:51 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Tue, 5 Dec 2006 23:51:29 +0000 (23:51 +0000)
SVN-Revision: 5695

target/linux/generic-2.4/patches/603-netfilter_nat_pptp.patch

index 6a7a16fb9a1a978c090f3ead0338369e763515aa..ce2838f76a3ff72ea51db63817328b799211d85d 100644 (file)
@@ -1,6 +1,6 @@
-diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack.h
---- linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack.h       2005-04-02 19:04:11.000000000 +0200
-+++ linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack.h       2005-04-02 19:41:05.000000000 +0200
+diff -urN linux.old/include/linux/netfilter_ipv4/ip_conntrack.h linux.dev/include/linux/netfilter_ipv4/ip_conntrack.h
+--- linux.old/include/linux/netfilter_ipv4/ip_conntrack.h      2006-12-06 00:31:46.000000000 +0100
++++ linux.dev/include/linux/netfilter_ipv4/ip_conntrack.h      2006-12-06 00:34:27.000000000 +0100
 @@ -50,16 +50,19 @@
  
  #include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
@@ -55,9 +55,9 @@ diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4
  };
  #endif
  
-diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_pptp.h linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_pptp.h
---- linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_pptp.h  1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_pptp.h  2005-04-02 19:09:07.000000000 +0200
+diff -urN linux.old/include/linux/netfilter_ipv4/ip_conntrack_pptp.h linux.dev/include/linux/netfilter_ipv4/ip_conntrack_pptp.h
+--- linux.old/include/linux/netfilter_ipv4/ip_conntrack_pptp.h 1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/include/linux/netfilter_ipv4/ip_conntrack_pptp.h 2006-12-06 00:34:27.000000000 +0100
 @@ -0,0 +1,313 @@
 +/* PPTP constants and structs */
 +#ifndef _CONNTRACK_PPTP_H
@@ -372,9 +372,9 @@ diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_pptp.h linu
 +
 +#endif /* __KERNEL__ */
 +#endif /* _CONNTRACK_PPTP_H */
-diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h
---- linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h     1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h     2005-04-02 19:10:45.000000000 +0200
+diff -urN linux.old/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h linux.dev/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h
+--- linux.old/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h    1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h    2006-12-06 00:34:27.000000000 +0100
 @@ -0,0 +1,123 @@
 +#ifndef _CONNTRACK_PROTO_GRE_H
 +#define _CONNTRACK_PROTO_GRE_H
@@ -499,9 +499,9 @@ diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h
 +#endif /* __KERNEL__ */
 +
 +#endif /* _CONNTRACK_PROTO_GRE_H */
-diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_tuple.h linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
---- linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2003-11-28 19:26:21.000000000 +0100
-+++ linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2005-04-02 19:07:18.000000000 +0200
+diff -urN linux.old/include/linux/netfilter_ipv4/ip_conntrack_tuple.h linux.dev/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
+--- linux.old/include/linux/netfilter_ipv4/ip_conntrack_tuple.h        2003-11-28 19:26:21.000000000 +0100
++++ linux.dev/include/linux/netfilter_ipv4/ip_conntrack_tuple.h        2006-12-06 00:34:27.000000000 +0100
 @@ -14,7 +14,7 @@
  union ip_conntrack_manip_proto
  {
@@ -560,9 +560,9 @@ diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_tuple.h lin
  
  #define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL)
  
-diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_nat_pptp.h linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_nat_pptp.h
---- linux-2.4.29-old/include/linux/netfilter_ipv4/ip_nat_pptp.h        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_nat_pptp.h        2005-04-02 19:07:18.000000000 +0200
+diff -urN linux.old/include/linux/netfilter_ipv4/ip_nat_pptp.h linux.dev/include/linux/netfilter_ipv4/ip_nat_pptp.h
+--- linux.old/include/linux/netfilter_ipv4/ip_nat_pptp.h       1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/include/linux/netfilter_ipv4/ip_nat_pptp.h       2006-12-06 00:34:27.000000000 +0100
 @@ -0,0 +1,11 @@
 +/* PPTP constants and structs */
 +#ifndef _NAT_PPTP_H
@@ -575,9 +575,9 @@ diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_nat_pptp.h linux-2.4.
 +};
 +
 +#endif /* _NAT_PPTP_H */
-diff -urN linux-2.4.29-old/net/ipv4/netfilter/Config.in linux-2.4.29-dev/net/ipv4/netfilter/Config.in
---- linux-2.4.29-old/net/ipv4/netfilter/Config.in      2005-04-02 19:04:11.000000000 +0200
-+++ linux-2.4.29-dev/net/ipv4/netfilter/Config.in      2005-04-02 19:07:18.000000000 +0200
+diff -urN linux.old/net/ipv4/netfilter/Config.in linux.dev/net/ipv4/netfilter/Config.in
+--- linux.old/net/ipv4/netfilter/Config.in     2006-12-06 00:31:46.000000000 +0100
++++ linux.dev/net/ipv4/netfilter/Config.in     2006-12-06 00:34:27.000000000 +0100
 @@ -10,6 +10,8 @@
    dep_tristate '  Amanda protocol support' CONFIG_IP_NF_AMANDA $CONFIG_IP_NF_CONNTRACK
    dep_tristate '  TFTP protocol support' CONFIG_IP_NF_TFTP $CONFIG_IP_NF_CONNTRACK
@@ -587,7 +587,7 @@ diff -urN linux-2.4.29-old/net/ipv4/netfilter/Config.in linux-2.4.29-dev/net/ipv
  fi
  
  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-@@ -67,6 +69,20 @@
+@@ -64,6 +66,20 @@
        define_bool CONFIG_IP_NF_NAT_NEEDED y
        dep_tristate '    MASQUERADE target support' CONFIG_IP_NF_TARGET_MASQUERADE $CONFIG_IP_NF_NAT
        dep_tristate '    REDIRECT target support' CONFIG_IP_NF_TARGET_REDIRECT $CONFIG_IP_NF_NAT
@@ -608,10 +608,10 @@ diff -urN linux-2.4.29-old/net/ipv4/netfilter/Config.in linux-2.4.29-dev/net/ipv
        if [ "$CONFIG_IP_NF_AMANDA" = "m" ]; then
          define_tristate CONFIG_IP_NF_NAT_AMANDA m
        else
-diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_core.c
---- linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_core.c    2005-04-02 19:04:11.000000000 +0200
-+++ linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_core.c    2005-04-02 19:07:18.000000000 +0200
-@@ -142,6 +142,8 @@
+diff -urN linux.old/net/ipv4/netfilter/ip_conntrack_core.c linux.dev/net/ipv4/netfilter/ip_conntrack_core.c
+--- linux.old/net/ipv4/netfilter/ip_conntrack_core.c   2006-12-06 00:31:46.000000000 +0100
++++ linux.dev/net/ipv4/netfilter/ip_conntrack_core.c   2006-12-06 00:34:27.000000000 +0100
+@@ -143,6 +143,8 @@
        tuple->dst.ip = iph->daddr;
        tuple->dst.protonum = iph->protocol;
  
@@ -620,7 +620,7 @@ diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.29-d
        ret = protocol->pkt_to_tuple((u_int32_t *)iph + iph->ihl,
                                     len - 4*iph->ihl,
                                     tuple);
-@@ -157,6 +159,8 @@
+@@ -158,6 +160,8 @@
        inverse->dst.ip = orig->src.ip;
        inverse->dst.protonum = orig->dst.protonum;
  
@@ -629,7 +629,7 @@ diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.29-d
        return protocol->invert_tuple(inverse, orig);
  }
  
-@@ -954,8 +958,8 @@
+@@ -970,8 +974,8 @@
         * so there is no need to use the tuple lock too */
  
        DEBUGP("ip_conntrack_expect_related %p\n", related_to);
@@ -640,7 +640,7 @@ diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.29-d
  
        old = LIST_FIND(&ip_conntrack_expect_list, resent_expect,
                        struct ip_conntrack_expect *, &expect->tuple, 
-@@ -1072,15 +1076,14 @@
+@@ -1089,15 +1093,14 @@
  
        MUST_BE_READ_LOCKED(&ip_conntrack_lock);
        WRITE_LOCK(&ip_conntrack_expect_tuple_lock);
@@ -660,882 +660,679 @@ diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.29-d
                    && LIST_FIND(&ip_conntrack_expect_list, expect_clash,
                                 struct ip_conntrack_expect *, newtuple, &expect->mask)) {
                        /* Force NAT to find an unused tuple */
-diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_pptp_priv.h linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_pptp_priv.h
---- linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_pptp_priv.h       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_pptp_priv.h       2005-04-02 19:14:10.000000000 +0200
-@@ -0,0 +1,24 @@
-+#ifndef _IP_CT_PPTP_PRIV_H
-+#define _IP_CT_PPTP_PRIV_H
-+
-+/* PptpControlMessageType names */
-+static const char *strMName[] = {
-+      "UNKNOWN_MESSAGE",
-+      "START_SESSION_REQUEST",
-+      "START_SESSION_REPLY",
-+      "STOP_SESSION_REQUEST",
-+      "STOP_SESSION_REPLY",
-+      "ECHO_REQUEST",
-+      "ECHO_REPLY",
-+      "OUT_CALL_REQUEST",
-+      "OUT_CALL_REPLY",
-+      "IN_CALL_REQUEST",
-+      "IN_CALL_REPLY",
-+      "IN_CALL_CONNECT",
-+      "CALL_CLEAR_REQUEST",
-+      "CALL_DISCONNECT_NOTIFY",
-+      "WAN_ERROR_NOTIFY",
-+      "SET_LINK_INFO"
-+};
-+
-+#endif
-diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_proto_gre.c linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_proto_gre.c
---- linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_proto_gre.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_proto_gre.c       2005-04-02 19:14:31.000000000 +0200
-@@ -0,0 +1,333 @@
+diff -urN linux.old/net/ipv4/netfilter/ip_conntrack_pptp.c linux.dev/net/ipv4/netfilter/ip_conntrack_pptp.c
+--- linux.old/net/ipv4/netfilter/ip_conntrack_pptp.c   1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/net/ipv4/netfilter/ip_conntrack_pptp.c   2006-12-06 00:34:27.000000000 +0100
+@@ -0,0 +1,633 @@
 +/*
-+ * ip_conntrack_proto_gre.c - Version 1.2 
-+ *
-+ * 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.
++ * ip_conntrack_pptp.c        - Version 1.9
 + *
-+ * It has an optional key field, which may help us distinguishing two 
-+ * connections between the same two hosts.
++ * Connection tracking support for PPTP (Point to Point Tunneling Protocol).
++ * PPTP is a a protocol for creating virtual private networks.
++ * It is a specification defined by Microsoft and some vendors
++ * working with Microsoft.  PPTP is built on top of a modified
++ * version of the Internet Generic Routing Encapsulation Protocol.
++ * GRE is defined in RFC 1701 and RFC 1702.  Documentation of
++ * PPTP can be found in RFC 2637
 + *
-+ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 
++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org>
 + *
-+ * 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.
++ * Development of this code funded by Astaro AG (http://www.astaro.com/)
 + *
-+ * Documentation about PPTP can be found in RFC 2637
++ * Limitations:
++ *     - We blindly assume that control connections are always
++ *       established in PNS->PAC direction.  This is a violation
++ *       of RFFC2673
 + *
-+ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org>
++ * TODO: - finish support for multiple calls within one session
++ *       (needs expect reservations in newnat)
++ *     - testing of incoming PPTP calls 
 + *
-+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
++ * Changes: 
++ *    2002-02-05 - Version 1.3
++ *      - Call ip_conntrack_unexpect_related() from 
++ *        pptp_timeout_related() to destroy expectations in case
++ *        CALL_DISCONNECT_NOTIFY or tcp fin packet was seen
++ *        (Philip Craig <philipc@snapgear.com>)
++ *      - Add Version information at module loadtime
++ *    2002-02-10 - Version 1.6
++ *      - move to C99 style initializers
++ *      - remove second expectation if first arrives
 + *
 + */
 +
 +#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 <net/checksum.h>
++#include <net/tcp.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>
 +
++#define IP_CT_PPTP_VERSION "1.9"
++
 +MODULE_LICENSE("GPL");
 +MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
-+MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE");
++MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP");
 +
-+/* shamelessly stolen from ip_conntrack_proto_udp.c */
-+#define GRE_TIMEOUT           (30*HZ)
-+#define GRE_STREAM_TIMEOUT    (180*HZ)
++DECLARE_LOCK(ip_pptp_lock);
 +
 +#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))
++#include "ip_conntrack_pptp_priv.h"
++#define DEBUGP(format, args...)       printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
++                                      ": " format, ## args)
 +#else
-+#define DEBUGP(x, args...)
-+#define DUMP_TUPLE_GRE(x)
++#define DEBUGP(format, args...)
 +#endif
-+                              
-+/* 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));
-+}
++#define SECS *HZ
++#define MINS * 60 SECS
++#define HOURS * 60 MINS
++#define DAYS * 24 HOURS
 +
-+/* look up the source key for a given tuple */
-+static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t)
++#define PPTP_GRE_TIMEOUT              (10 MINS)
++#define PPTP_GRE_STREAM_TIMEOUT       (5 DAYS)
++
++static int pptp_expectfn(struct ip_conntrack *ct)
 +{
-+      struct ip_ct_gre_keymap *km;
-+      u_int32_t key;
++      struct ip_conntrack *master;
++      struct ip_conntrack_expect *exp;
 +
-+      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);
++      DEBUGP("increasing timeouts\n");
++      /* increase timeout of GRE data channel conntrack entry */
++      ct->proto.gre.timeout = PPTP_GRE_TIMEOUT;
++      ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT;
++
++      master = master_ct(ct);
++      if (!master) {
++              DEBUGP(" no master!!!\n");
 +              return 0;
 +      }
 +
-+      key = km->tuple.src.u.gre.key;
-+      READ_UNLOCK(&ip_ct_gre_lock);
-+
-+      return key;
-+}
++      exp = ct->master;
++      if (!exp) {
++              DEBUGP("no expectation!!\n");
++              return 0;
++      }
 +
-+/* 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;
++      DEBUGP("completing tuples with ct info\n");
++      /* we can do this, since we're unconfirmed */
++      if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == 
++              htonl(master->help.ct_pptp_info.pac_call_id)) { 
++              /* assume PNS->PAC */
++              ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = 
++                      htonl(master->help.ct_pptp_info.pns_call_id);
++              ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
++                      htonl(master->help.ct_pptp_info.pns_call_id);
++      } else {
++              /* assume PAC->PNS */
++              ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
++                      htonl(master->help.ct_pptp_info.pac_call_id);
++              ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
++                      htonl(master->help.ct_pptp_info.pac_call_id);
++      }
++      
++      /* delete other expectation */
++      if (exp->expected_list.next != &exp->expected_list) {
++              struct ip_conntrack_expect *other_exp;
++              struct list_head *cur_item, *next;
 +
-+      km = kmalloc(sizeof(*km), GFP_ATOMIC);
-+      if (!km)
-+              return -1;
++              for (cur_item = master->sibling_list.next;
++                   cur_item != &master->sibling_list; cur_item = next) {
++                      next = cur_item->next;
++                      other_exp = list_entry(cur_item,
++                                             struct ip_conntrack_expect,
++                                             expected_list);
++                      /* remove only if occurred at same sequence number */
++                      if (other_exp != exp && other_exp->seq == exp->seq) {
++                              DEBUGP("unexpecting other direction\n");
++                              ip_ct_gre_keymap_destroy(other_exp);
++                              ip_conntrack_unexpect_related(other_exp);
++                      }
++              }
++      }
 +
-+      /* initializing list head should be sufficient */
-+      memset(km, 0, sizeof(*km));
++      return 0;
++}
 +
-+      memcpy(&km->tuple, t, sizeof(*t));
++/* timeout GRE data connections */
++static int pptp_timeout_related(struct ip_conntrack *ct)
++{
++      struct list_head *cur_item, *next;
++      struct ip_conntrack_expect *exp;
 +
-+      if (!reply)
-+              exp->proto.gre.keymap_orig = km;
-+      else
-+              exp->proto.gre.keymap_reply = km;
++      /* FIXME: do we have to lock something ? */
++      for (cur_item = ct->sibling_list.next;
++          cur_item != &ct->sibling_list; cur_item = next) {
++              next = cur_item->next;
++              exp = list_entry(cur_item, struct ip_conntrack_expect,
++                               expected_list);
 +
-+      DEBUGP("adding new entry %p: ", km);
-+      DUMP_TUPLE_GRE(&km->tuple);
++              ip_ct_gre_keymap_destroy(exp);
++              if (!exp->sibling) {
++                      ip_conntrack_unexpect_related(exp);
++                      continue;
++              }
 +
-+      WRITE_LOCK(&ip_ct_gre_lock);
-+      list_append(&gre_keymap_list, km);
-+      WRITE_UNLOCK(&ip_ct_gre_lock);
++              DEBUGP("setting timeout of conntrack %p to 0\n",
++                      exp->sibling);
++              exp->sibling->proto.gre.timeout = 0;
++              exp->sibling->proto.gre.stream_timeout = 0;
++              ip_ct_refresh(exp->sibling, 0);
++      }
 +
 +      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)
++/* expect GRE connections (PNS->PAC and PAC->PNS direction) */
++static inline int
++exp_gre(struct ip_conntrack *master,
++      u_int32_t seq,
++      u_int16_t callid,
++      u_int16_t peer_callid)
 +{
-+      DEBUGP("changing entry %p to: ", km);
-+      DUMP_TUPLE_GRE(t);
++      struct ip_conntrack_expect exp;
++      struct ip_conntrack_tuple inv_tuple;
 +
-+      WRITE_LOCK(&ip_ct_gre_lock);
-+      memcpy(&km->tuple, t, sizeof(km->tuple));
-+      WRITE_UNLOCK(&ip_ct_gre_lock);
-+}
++      memset(&exp, 0, sizeof(exp));
++      /* tuple in original direction, PNS->PAC */
++      exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++      exp.tuple.src.u.gre.key = htonl(ntohs(peer_callid));
++      exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
++      exp.tuple.dst.u.gre.key = htonl(ntohs(callid));
++      exp.tuple.dst.protonum = IPPROTO_GRE;
 +
-+/* 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;
++      exp.mask.src.ip = 0xffffffff;
++      exp.mask.src.u.all = 0;
++      exp.mask.dst.u.all = 0;
++      exp.mask.dst.u.gre.key = 0xffffffff;
++      exp.mask.dst.ip = 0xffffffff;
++      exp.mask.dst.protonum = 0xffff;
++                      
++      exp.seq = seq;
++      exp.expectfn = pptp_expectfn;
++
++      exp.help.exp_pptp_info.pac_call_id = ntohs(callid);
++      exp.help.exp_pptp_info.pns_call_id = ntohs(peer_callid);
++
++      DEBUGP("calling expect_related ");
++      DUMP_TUPLE_RAW(&exp.tuple);
++      
++      /* Add GRE keymap entries */
++      if (ip_ct_gre_keymap_add(&exp, &exp.tuple, 0) != 0)
++              return 1;
++
++      invert_tuplepr(&inv_tuple, &exp.tuple);
++      if (ip_ct_gre_keymap_add(&exp, &inv_tuple, 1) != 0) {
++              ip_ct_gre_keymap_destroy(&exp);
++              return 1;
 +      }
-+      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 (ip_conntrack_expect_related(master, &exp) != 0) {
++              ip_ct_gre_keymap_destroy(&exp);
++              DEBUGP("cannot expect_related()\n");
++              return 1;
 +      }
-+      WRITE_UNLOCK(&ip_ct_gre_lock);
-+}
 +
++      /* tuple in reply direction, PAC->PNS */
++      exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
++      exp.tuple.src.u.gre.key = htonl(ntohs(callid));
++      exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++      exp.tuple.dst.u.gre.key = htonl(ntohs(peer_callid));
 +
-+/* 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.key = orig->src.u.gre.key;
-+      tuple->src.u.gre.key = orig->dst.u.gre.key;
++      DEBUGP("calling expect_related ");
++      DUMP_TUPLE_RAW(&exp.tuple);
++      
++      /* Add GRE keymap entries */
++      ip_ct_gre_keymap_add(&exp, &exp.tuple, 0);
++      invert_tuplepr(&inv_tuple, &exp.tuple);
++      ip_ct_gre_keymap_add(&exp, &inv_tuple, 1);
++      /* FIXME: cannot handle error correctly, since we need to free
++       * the above keymap :( */
++      
++      if (ip_conntrack_expect_related(master, &exp) != 0) {
++              /* free the second pair of keypmaps */
++              ip_ct_gre_keymap_destroy(&exp);
++              DEBUGP("cannot expect_related():\n");
++              return 1;
++      }
 +
-+      return 1;
++      return 0;
 +}
 +
-+/* gre hdr info to tuple */
-+static int gre_pkt_to_tuple(const void *datah, size_t datalen,
-+                          struct ip_conntrack_tuple *tuple)
++static inline int 
++pptp_inbound_pkt(struct tcphdr *tcph,
++               struct pptp_pkt_hdr *pptph, 
++               size_t datalen,
++               struct ip_conntrack *ct,
++               enum ip_conntrack_info ctinfo)
 +{
-+      struct gre_hdr *grehdr = (struct gre_hdr *) datah;
-+      struct gre_hdr_pptp *pgrehdr = (struct gre_hdr_pptp *) datah;
-+      u_int32_t srckey;
++      struct PptpControlHeader *ctlh;
++        union pptp_ctrl_union pptpReq;
++      
++      struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
++      u_int16_t msg, *cid, *pcid;
++      u_int32_t seq;  
 +
-+      /* core guarantees 8 protocol bytes, no need for size check */
++      ctlh = (struct PptpControlHeader *) 
++              ((char *) pptph + sizeof(struct pptp_pkt_hdr));
++      pptpReq.rawreq = (void *) 
++              ((char *) ctlh + sizeof(struct PptpControlHeader));
 +
-+      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));
++      msg = ntohs(ctlh->messageType);
++      DEBUGP("inbound control message %s\n", strMName[msg]);
++
++      switch (msg) {
++      case PPTP_START_SESSION_REPLY:
++              /* server confirms new control session */
++              if (info->sstate < PPTP_SESSION_REQUESTED) {
++                      DEBUGP("%s without START_SESS_REQUEST\n",
++                              strMName[msg]);
 +                      break;
++              }
++              if (pptpReq.srep->resultCode == PPTP_START_OK)
++                      info->sstate = PPTP_SESSION_CONFIRMED;
++              else 
++                      info->sstate = PPTP_SESSION_ERROR;
++              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));
++      case PPTP_STOP_SESSION_REPLY:
++              /* server confirms end of control session */
++              if (info->sstate > PPTP_SESSION_STOPREQ) {
++                      DEBUGP("%s without STOP_SESS_REQUEST\n",
++                              strMName[msg]);
 +                      break;
++              }
++              if (pptpReq.strep->resultCode == PPTP_STOP_OK)
++                      info->sstate = PPTP_SESSION_NONE;
++              else
++                      info->sstate = PPTP_SESSION_ERROR;
++              break;
 +
-+              default:
-+                      printk(KERN_WARNING "unknown GRE version %hu\n",
-+                              grehdr->version);
-+                      return 0;
-+      }
++      case PPTP_OUT_CALL_REPLY:
++              /* server accepted call, we now expect GRE frames */
++              if (info->sstate != PPTP_SESSION_CONFIRMED) {
++                      DEBUGP("%s but no session\n", strMName[msg]);
++                      break;
++              }
++              if (info->cstate != PPTP_CALL_OUT_REQ &&
++                  info->cstate != PPTP_CALL_OUT_CONF) {
++                      DEBUGP("%s without OUTCALL_REQ\n", strMName[msg]);
++                      break;
++              }
++              if (pptpReq.ocack->resultCode != PPTP_OUTCALL_CONNECT) {
++                      info->cstate = PPTP_CALL_NONE;
++                      break;
++              }
 +
-+      srckey = gre_keymap_lookup(tuple);
++              cid = &pptpReq.ocack->callID;
++              pcid = &pptpReq.ocack->peersCallID;
 +
-+#if 0
-+      DEBUGP("found src key %x for tuple ", ntohl(srckey));
-+      DUMP_TUPLE_GRE(tuple);
-+#endif
-+      tuple->src.u.gre.key = srckey;
++              info->pac_call_id = ntohs(*cid);
++              
++              if (htons(info->pns_call_id) != *pcid) {
++                      DEBUGP("%s for unknown callid %u\n",
++                              strMName[msg], ntohs(*pcid));
++                      break;
++              }
 +
-+      return 1;
-+}
++              DEBUGP("%s, CID=%X, PCID=%X\n", strMName[msg], 
++                      ntohs(*cid), ntohs(*pcid));
++              
++              info->cstate = PPTP_CALL_OUT_CONF;
 +
-+/* print gre part of tuple */
-+static unsigned int gre_print_tuple(char *buffer,
-+                                  const struct ip_conntrack_tuple *tuple)
-+{
-+      return sprintf(buffer, "srckey=0x%x dstkey=0x%x ", 
-+                      ntohl(tuple->src.u.gre.key),
-+                      ntohl(tuple->dst.u.gre.key));
-+}
++              seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph);
++              if (exp_gre(ct, seq, *cid, *pcid) != 0)
++                      printk("ip_conntrack_pptp: error during exp_gre\n");
++              break;
 +
-+/* 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));
-+}
++      case PPTP_IN_CALL_REQUEST:
++              /* server tells us about incoming call request */
++              if (info->sstate != PPTP_SESSION_CONFIRMED) {
++                      DEBUGP("%s but no session\n", strMName[msg]);
++                      break;
++              }
++              pcid = &pptpReq.icack->peersCallID;
++              DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
++              info->cstate = PPTP_CALL_IN_REQ;
++              info->pac_call_id= ntohs(*pcid);
++              break;
 +
-+/* 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_acct(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_acct(ct, ct->proto.gre.timeout);
-+      
-+      return NF_ACCEPT;
-+}
++      case PPTP_IN_CALL_CONNECT:
++              /* server tells us about incoming call established */
++              if (info->sstate != PPTP_SESSION_CONFIRMED) {
++                      DEBUGP("%s but no session\n", strMName[msg]);
++                      break;
++              }
++              if (info->sstate != PPTP_CALL_IN_REP
++                  && info->sstate != PPTP_CALL_IN_CONF) {
++                      DEBUGP("%s but never sent IN_CALL_REPLY\n",
++                              strMName[msg]);
++                      break;
++              }
 +
-+/* 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);
++              pcid = &pptpReq.iccon->peersCallID;
++              cid = &info->pac_call_id;
 +
-+      /* 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;
++              if (info->pns_call_id != ntohs(*pcid)) {
++                      DEBUGP("%s for unknown CallID %u\n", 
++                              strMName[msg], ntohs(*cid));
++                      break;
++              }
 +
-+      return 1;
-+}
++              DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
++              info->cstate = PPTP_CALL_IN_CONF;
 +
-+/* 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;
-+      }
-+
-+      ip_ct_gre_keymap_destroy(master);
-+}
-+
-+/* 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 at module unload time\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);
-+EXPORT_SYMBOL(ip_ct_gre_keymap_destroy);
-+
-+module_init(init);
-+module_exit(fini);
-diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_nat_core.c linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_core.c
---- linux-2.4.29-old/net/ipv4/netfilter/ip_nat_core.c  2005-01-19 15:10:13.000000000 +0100
-+++ linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_core.c  2005-04-02 19:07:18.000000000 +0200
-@@ -424,7 +424,7 @@
-       *tuple = *orig_tuple;
-       while ((rptr = find_best_ips_proto_fast(tuple, mr, conntrack, hooknum))
-              != NULL) {
--              DEBUGP("Found best for "); DUMP_TUPLE(tuple);
-+              DEBUGP("Found best for "); DUMP_TUPLE_RAW(tuple);
-               /* 3) The per-protocol part of the manip is made to
-                  map into the range to make a unique tuple. */
-@@ -564,9 +564,9 @@
-                      HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST",
-                      conntrack);
-               DEBUGP("Original: ");
--              DUMP_TUPLE(&orig_tp);
-+              DUMP_TUPLE_RAW(&orig_tp);
-               DEBUGP("New: ");
--              DUMP_TUPLE(&new_tuple);
-+              DUMP_TUPLE_RAW(&new_tuple);
- #endif
-               /* We now have two tuples (SRCIP/SRCPT/DSTIP/DSTPT):
-diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_nat_pptp.c linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_pptp.c
---- linux-2.4.29-old/net/ipv4/netfilter/ip_nat_pptp.c  1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_pptp.c  2005-04-02 19:14:52.000000000 +0200
-@@ -0,0 +1,475 @@
-+/*
-+ * ip_nat_pptp.c      - Version 1.5
-+ *
-+ * NAT support for PPTP (Point to Point Tunneling Protocol).
-+ * PPTP is a a protocol for creating virtual private networks.
-+ * It is a specification defined by Microsoft and some vendors
-+ * working with Microsoft.  PPTP is built on top of a modified
-+ * version of the Internet Generic Routing Encapsulation Protocol.
-+ * GRE is defined in RFC 1701 and RFC 1702.  Documentation of
-+ * PPTP can be found in RFC 2637
-+ *
-+ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org>
-+ *
-+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
-+ *
-+ * TODO: - Support for multiple calls within one session
-+ *       (needs netfilter newnat code)
-+ *     - NAT to a unique tuple, not to TCP source port
-+ *       (needs netfilter tuple reservation)
-+ *
-+ * Changes:
-+ *     2002-02-10 - Version 1.3
-+ *       - Use ip_nat_mangle_tcp_packet() because of cloned skb's
-+ *       in local connections (Philip Craig <philipc@snapgear.com>)
-+ *       - add checks for magicCookie and pptp version
-+ *       - make argument list of pptp_{out,in}bound_packet() shorter
-+ *       - move to C99 style initializers
-+ *       - print version number at module loadtime
-+ *     2003-09-22 - Version 1.5
-+ *       - use SNATed tcp sourceport as callid, since we get called before
-+ *         TCP header is mangled (Philip Craig <philipc@snapgear.com>)
-+ * 
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/module.h>
-+#include <linux/ip.h>
-+#include <linux/tcp.h>
-+#include <net/tcp.h>
-+#include <linux/netfilter_ipv4/ip_nat.h>
-+#include <linux/netfilter_ipv4/ip_nat_rule.h>
-+#include <linux/netfilter_ipv4/ip_nat_helper.h>
-+#include <linux/netfilter_ipv4/ip_nat_pptp.h>
-+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
-+#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
-+
-+#define IP_NAT_PPTP_VERSION "1.5"
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
-+MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
-+
-+
-+#if 0
-+#include "ip_conntrack_pptp_priv.h"
-+#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
-+                                     ": " format, ## args)
-+#else
-+#define DEBUGP(format, args...)
-+#endif
++              /* we expect a GRE connection from PAC to PNS */
++              seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph);
++              if (exp_gre(ct, seq, *cid, *pcid) != 0)
++                      printk("ip_conntrack_pptp: error during exp_gre\n");
 +
-+static unsigned int
-+pptp_nat_expected(struct sk_buff **pskb,
-+                unsigned int hooknum,
-+                struct ip_conntrack *ct,
-+                struct ip_nat_info *info)
-+{
-+      struct ip_conntrack *master = master_ct(ct);
-+      struct ip_nat_multi_range mr;
-+      struct ip_ct_pptp_master *ct_pptp_info;
-+      struct ip_nat_pptp *nat_pptp_info;
-+      u_int32_t newip, newcid;
-+      int ret;
++              break;
 +
-+      IP_NF_ASSERT(info);
-+      IP_NF_ASSERT(master);
-+      IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
++      case PPTP_CALL_DISCONNECT_NOTIFY:
++              /* server confirms disconnect */
++              cid = &pptpReq.disc->callID;
++              DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid));
++              info->cstate = PPTP_CALL_NONE;
 +
-+      DEBUGP("we have a connection!\n");
++              /* untrack this call id, unexpect GRE packets */
++              pptp_timeout_related(ct);
++              break;
 +
-+      LOCK_BH(&ip_pptp_lock);
-+      ct_pptp_info = &master->help.ct_pptp_info;
-+      nat_pptp_info = &master->nat.help.nat_pptp_info;
++      case PPTP_WAN_ERROR_NOTIFY:
++              break;
 +
-+      /* need to alter GRE tuple because conntrack expectfn() used 'wrong'
-+       * (unmanipulated) values */
-+      if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
-+              DEBUGP("completing tuples with NAT info \n");
-+              /* we can do this, since we're unconfirmed */
-+              if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key ==
-+                      htonl(ct_pptp_info->pac_call_id)) {     
-+                      /* assume PNS->PAC */
-+                      ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
-+                              htonl(nat_pptp_info->pns_call_id);
-+                      ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
-+                              htonl(nat_pptp_info->pns_call_id);
-+                      newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
-+                      newcid = htonl(nat_pptp_info->pac_call_id);
-+              } else {
-+                      /* assume PAC->PNS */
-+                      ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
-+                              htonl(nat_pptp_info->pac_call_id);
-+                      ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
-+                              htonl(nat_pptp_info->pac_call_id);
-+                      newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
-+                      newcid = htonl(nat_pptp_info->pns_call_id);
-+              }
-+      } else {
-+              if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key ==
-+                      htonl(ct_pptp_info->pac_call_id)) {     
-+                      /* assume PNS->PAC */
-+                      newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
-+                      newcid = htonl(ct_pptp_info->pns_call_id);
-+              }
-+              else {
-+                      /* assume PAC->PNS */
-+                      newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
-+                      newcid = htonl(ct_pptp_info->pac_call_id);
-+              }
++      case PPTP_ECHO_REQUEST:
++      case PPTP_ECHO_REPLY:
++              /* I don't have to explain these ;) */
++              break;
++      default:
++              DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)
++                      ? strMName[msg]:strMName[0], msg);
++              break;
 +      }
 +
-+      mr.rangesize = 1;
-+      mr.range[0].flags = IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED;
-+      mr.range[0].min_ip = mr.range[0].max_ip = newip;
-+      mr.range[0].min = mr.range[0].max = 
-+              ((union ip_conntrack_manip_proto ) { newcid }); 
-+      DEBUGP("change ip to %u.%u.%u.%u\n", 
-+              NIPQUAD(newip));
-+      DEBUGP("change key to 0x%x\n", ntohl(newcid));
-+      ret = ip_nat_setup_info(ct, &mr, hooknum);
-+
-+      UNLOCK_BH(&ip_pptp_lock);
-+
-+      return ret;
++      return NF_ACCEPT;
 +
 +}
 +
-+/* outbound packets == from PNS to PAC */
-+static inline unsigned int
-+pptp_outbound_pkt(struct sk_buff **pskb,
++static inline int
++pptp_outbound_pkt(struct tcphdr *tcph,
++                struct pptp_pkt_hdr *pptph,
++                size_t datalen,
 +                struct ip_conntrack *ct,
-+                enum ip_conntrack_info ctinfo,
-+                struct ip_conntrack_expect *exp)
-+
-+{
-+      struct iphdr *iph = (*pskb)->nh.iph;
-+      struct tcphdr *tcph = (void *) iph + iph->ihl*4;
-+      struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *) 
-+                                      ((void *)tcph + tcph->doff*4);
-+
-+      struct PptpControlHeader *ctlh;
-+      union pptp_ctrl_union pptpReq;
-+      struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
-+      struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
-+
-+      u_int16_t msg, *cid = NULL, new_callid;
-+
-+      /* FIXME: size checks !!! */
-+      ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
-+      pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
-+
-+      new_callid = htons(ct_pptp_info->pns_call_id);
-+      
-+      switch (msg = ntohs(ctlh->messageType)) {
-+              case PPTP_OUT_CALL_REQUEST:
-+                      cid = &pptpReq.ocreq->callID;
-+                      /* FIXME: ideally we would want to reserve a call ID
-+                       * here.  current netfilter NAT core is not able to do
-+                       * this :( For now we use TCP source port. This breaks
-+                       * multiple calls within one control session */
-+
-+                      /* save original call ID in nat_info */
-+                      nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id;
-+
-+                      /* don't use tcph->source since we are at a DSTmanip
-+                       * hook (e.g. PREROUTING) and pkt is not mangled yet */
-+                      new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port;
-+
-+                      /* save new call ID in ct info */
-+                      ct_pptp_info->pns_call_id = ntohs(new_callid);
-+                      break;
-+              case PPTP_IN_CALL_REPLY:
-+                      cid = &pptpReq.icreq->callID;
-+                      break;
-+              case PPTP_CALL_CLEAR_REQUEST:
-+                      cid = &pptpReq.clrreq->callID;
-+                      break;
-+              default:
-+                      DEBUGP("unknown outbound packet 0x%04x:%s\n", msg,
-+                            (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
-+                      /* fall through */
-+
-+              case PPTP_SET_LINK_INFO:
-+                      /* only need to NAT in case PAC is behind NAT box */
-+              case PPTP_START_SESSION_REQUEST:
-+              case PPTP_START_SESSION_REPLY:
-+              case PPTP_STOP_SESSION_REQUEST:
-+              case PPTP_STOP_SESSION_REPLY:
-+              case PPTP_ECHO_REQUEST:
-+              case PPTP_ECHO_REPLY:
-+                      /* no need to alter packet */
-+                      return NF_ACCEPT;
-+      }
-+
-+      IP_NF_ASSERT(cid);
-+
-+      DEBUGP("altering call id from 0x%04x to 0x%04x\n",
-+              ntohs(*cid), ntohs(new_callid));
-+
-+      /* mangle packet */
-+      ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)cid - (void *)pptph,
-+                               sizeof(new_callid), (char *)&new_callid,
-+                               sizeof(new_callid));
-+
-+      return NF_ACCEPT;
-+}
-+
-+/* inbound packets == from PAC to PNS */
-+static inline unsigned int
-+pptp_inbound_pkt(struct sk_buff **pskb,
-+               struct ip_conntrack *ct,
-+               enum ip_conntrack_info ctinfo,
-+               struct ip_conntrack_expect *oldexp)
++                enum ip_conntrack_info ctinfo)
 +{
-+      struct iphdr *iph = (*pskb)->nh.iph;
-+      struct tcphdr *tcph = (void *) iph + iph->ihl*4;
-+      struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *) 
-+                                      ((void *)tcph + tcph->doff*4);
-+
 +      struct PptpControlHeader *ctlh;
-+      union pptp_ctrl_union pptpReq;
-+      struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
-+      struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
-+
-+      u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL;
-+      u_int32_t old_dst_ip;
-+
-+      struct ip_conntrack_tuple t, inv_t;
-+      struct ip_conntrack_tuple *orig_t, *reply_t;
++        union pptp_ctrl_union pptpReq;
++      struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
++      u_int16_t msg, *cid, *pcid;
 +
-+      /* FIXME: size checks !!! */
 +      ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
 +      pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
 +
-+      new_pcid = htons(nat_pptp_info->pns_call_id);
-+
-+      switch (msg = ntohs(ctlh->messageType)) {
-+      case PPTP_OUT_CALL_REPLY:
-+              pcid = &pptpReq.ocack->peersCallID;     
-+              cid = &pptpReq.ocack->callID;
-+              if (!oldexp) {
-+                      DEBUGP("outcall but no expectation\n");
-+                      break;
-+              }
-+              old_dst_ip = oldexp->tuple.dst.ip;
-+              t = oldexp->tuple;
-+              invert_tuplepr(&inv_t, &t);
-+
-+              /* save original PAC call ID in nat_info */
-+              nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
++      msg = ntohs(ctlh->messageType);
++      DEBUGP("outbound control message %s\n", strMName[msg]);
 +
-+              /* alter expectation */
-+              orig_t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
-+              reply_t = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
-+              if (t.src.ip == orig_t->src.ip && t.dst.ip == orig_t->dst.ip) {
-+                      /* expectation for PNS->PAC direction */
-+                      t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id);
-+                      t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id);
-+                      inv_t.src.ip = reply_t->src.ip;
-+                      inv_t.dst.ip = reply_t->dst.ip;
-+                      inv_t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id);
-+                      inv_t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id);
-+              } else {
-+                      /* expectation for PAC->PNS direction */
-+                      t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id);
-+                      t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id);
-+                      inv_t.src.ip = orig_t->src.ip;
-+                      inv_t.dst.ip = orig_t->dst.ip;
-+                      inv_t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id);
-+                      inv_t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id);
++      switch (msg) {
++      case PPTP_START_SESSION_REQUEST:
++              /* client requests for new control session */
++              if (info->sstate != PPTP_SESSION_NONE) {
++                      DEBUGP("%s but we already have one",
++                              strMName[msg]);
 +              }
++              info->sstate = PPTP_SESSION_REQUESTED;
++              break;
++      case PPTP_STOP_SESSION_REQUEST:
++              /* client requests end of control session */
++              info->sstate = PPTP_SESSION_STOPREQ;
++              break;
 +
-+              if (!ip_conntrack_change_expect(oldexp, &t)) {
-+                      DEBUGP("successfully changed expect\n");
-+              } else {
-+                      DEBUGP("can't change expect\n");
++      case PPTP_OUT_CALL_REQUEST:
++              /* client initiating connection to server */
++              if (info->sstate != PPTP_SESSION_CONFIRMED) {
++                      DEBUGP("%s but no session\n",
++                              strMName[msg]);
++                      break;
 +              }
-+              ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig, &t);
-+              ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, &inv_t);
++              info->cstate = PPTP_CALL_OUT_REQ;
++              /* track PNS call id */
++              cid = &pptpReq.ocreq->callID;
++              DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid));
++              info->pns_call_id = ntohs(*cid);
 +              break;
-+      case PPTP_IN_CALL_CONNECT:
-+              pcid = &pptpReq.iccon->peersCallID;
-+              if (!oldexp)
++      case PPTP_IN_CALL_REPLY:
++              /* client answers incoming call */
++              if (info->cstate != PPTP_CALL_IN_REQ
++                  && info->cstate != PPTP_CALL_IN_REP) {
++                      DEBUGP("%s without incall_req\n", 
++                              strMName[msg]);
 +                      break;
-+              old_dst_ip = oldexp->tuple.dst.ip;
-+              t = oldexp->tuple;
-+
-+              /* alter expectation, no need for callID */
-+              if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) {
-+                      /* expectation for PNS->PAC direction */
-+                      t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
-+              } else {
-+                      /* expectation for PAC->PNS direction */
-+                      t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
 +              }
-+
-+              if (!ip_conntrack_change_expect(oldexp, &t)) {
-+                      DEBUGP("successfully changed expect\n");
-+              } else {
-+                      DEBUGP("can't change expect\n");
++              if (pptpReq.icack->resultCode != PPTP_INCALL_ACCEPT) {
++                      info->cstate = PPTP_CALL_NONE;
++                      break;
 +              }
++              pcid = &pptpReq.icack->peersCallID;
++              if (info->pac_call_id != ntohs(*pcid)) {
++                      DEBUGP("%s for unknown call %u\n", 
++                              strMName[msg], ntohs(*pcid));
++                      break;
++              }
++              DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*pcid));
++              /* part two of the three-way handshake */
++              info->cstate = PPTP_CALL_IN_REP;
++              info->pns_call_id = ntohs(pptpReq.icack->callID);
 +              break;
-+      case PPTP_IN_CALL_REQUEST:
-+              /* only need to nat in case PAC is behind NAT box */
-+              break;
-+      case PPTP_WAN_ERROR_NOTIFY:
-+              pcid = &pptpReq.wanerr->peersCallID;
++
++      case PPTP_CALL_CLEAR_REQUEST:
++              /* client requests hangup of call */
++              if (info->sstate != PPTP_SESSION_CONFIRMED) {
++                      DEBUGP("CLEAR_CALL but no session\n");
++                      break;
++              }
++              /* FUTURE: iterate over all calls and check if
++               * call ID is valid.  We don't do this without newnat,
++               * because we only know about last call */
++              info->cstate = PPTP_CALL_CLEAR_REQ;
 +              break;
-+      case PPTP_CALL_DISCONNECT_NOTIFY:
-+              pcid = &pptpReq.disc->callID;
++      case PPTP_SET_LINK_INFO:
 +              break;
-+
-+      default:
-+              DEBUGP("unknown inbound packet %s\n",
-+                      (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
-+              /* fall through */
-+
-+      case PPTP_START_SESSION_REQUEST:
-+      case PPTP_START_SESSION_REPLY:
-+      case PPTP_STOP_SESSION_REQUEST:
-+      case PPTP_STOP_SESSION_REPLY:
 +      case PPTP_ECHO_REQUEST:
 +      case PPTP_ECHO_REPLY:
-+              /* no need to alter packet */
-+              return NF_ACCEPT;
-+      }
-+
-+      /* mangle packet */
-+      IP_NF_ASSERT(pcid);
-+      DEBUGP("altering peer call id from 0x%04x to 0x%04x\n",
-+              ntohs(*pcid), ntohs(new_pcid));
-+      ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)pcid - (void *)pptph,
-+                               sizeof(new_pcid), (char *)&new_pcid, 
-+                               sizeof(new_pcid));
-+
-+      if (new_cid) {
-+              IP_NF_ASSERT(cid);
-+              DEBUGP("altering call id from 0x%04x to 0x%04x\n",
-+                      ntohs(*cid), ntohs(new_cid));
-+              ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, 
-+                                       (void *)cid - (void *)pptph, 
-+                                       sizeof(new_cid), (char *)&new_cid, 
-+                                       sizeof(new_cid));
++              /* I don't have to explain these ;) */
++              break;
++      default:
++              DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)? 
++                      strMName[msg]:strMName[0], msg);
++              /* unknown: no need to create GRE masq table entry */
++              break;
 +      }
 +
-+      /* great, at least we don't need to resize packets */
 +      return NF_ACCEPT;
 +}
 +
 +
-+static unsigned int tcp_help(struct ip_conntrack *ct,
-+                           struct ip_conntrack_expect *exp,
-+                           struct ip_nat_info *info,
-+                           enum ip_conntrack_info ctinfo,
-+                           unsigned int hooknum, struct sk_buff **pskb)
++/* track caller id inside control connection, call expect_related */
++static int 
++conntrack_pptp_help(const struct iphdr *iph, size_t len,
++                  struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
++
 +{
-+      struct iphdr *iph = (*pskb)->nh.iph;
-+      struct tcphdr *tcph = (void *) iph + iph->ihl*4;
-+      unsigned int datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4;
 +      struct pptp_pkt_hdr *pptph;
++      
++      struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
++      u_int32_t tcplen = len - iph->ihl * 4;
++      u_int32_t datalen = tcplen - tcph->doff * 4;
++      void *datalimit;
++      int dir = CTINFO2DIR(ctinfo);
++      struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
 +
-+      int dir;
-+
-+      DEBUGP("entering\n");
++      int oldsstate, oldcstate;
++      int ret;
 +
-+      /* Only mangle things once: DST for original direction
-+         and SRC for reply direction. */
-+      dir = CTINFO2DIR(ctinfo);
-+      if (!((HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
-+           && dir == IP_CT_DIR_ORIGINAL)
-+            || (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST
-+                && dir == IP_CT_DIR_REPLY))) {
-+              DEBUGP("Not touching dir %s at hook %s\n",
-+                     dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
-+                     hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
-+                     : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
-+                     : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT"
-+                     : hooknum == NF_IP_LOCAL_IN ? "INPUT" : "???");
++      /* don't do any tracking before tcp handshake complete */
++      if (ctinfo != IP_CT_ESTABLISHED 
++          && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
++              DEBUGP("ctinfo = %u, skipping\n", ctinfo);
++              return NF_ACCEPT;
++      }
++      
++      /* not a complete TCP header? */
++      if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
++              DEBUGP("tcplen = %u\n", tcplen);
 +              return NF_ACCEPT;
 +      }
 +
-+      /* if packet is too small, just skip it */
-+      if (datalen < sizeof(struct pptp_pkt_hdr)+
-+                    sizeof(struct PptpControlHeader)) {
-+              DEBUGP("pptp packet too short\n");
-+              return NF_ACCEPT;       
++      /* checksum invalid? */
++      if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
++                      csum_partial((char *) tcph, tcplen, 0))) {
++              printk(KERN_NOTICE __FILE__ ": bad csum\n");
++              /* W2K PPTP server sends TCP packets with wrong checksum :(( */
++              //return NF_ACCEPT;
 +      }
 +
-+      pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4);
++      if (tcph->fin || tcph->rst) {
++              DEBUGP("RST/FIN received, timeouting GRE\n");
++              /* can't do this after real newnat */
++              info->cstate = PPTP_CALL_NONE;
 +
-+      /* if it's not a control message, we can't handle it */
-+      if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
++              /* untrack this call id, unexpect GRE packets */
++              pptp_timeout_related(ct);
++      }
++
++
++      pptph = (struct pptp_pkt_hdr *) ((void *) tcph + tcph->doff * 4);
++      datalimit = (void *) pptph + datalen;
++
++      /* not a full pptp packet header? */
++      if ((void *) pptph+sizeof(*pptph) >= datalimit) {
++              DEBUGP("no full PPTP header, can't track\n");
++              return NF_ACCEPT;
++      }
++      
++      /* if it's not a control message we can't do anything with it */
++        if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
 +          ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
-+              DEBUGP("not a pptp control packet\n");
++              DEBUGP("not a control packet\n");
 +              return NF_ACCEPT;
 +      }
 +
-+      LOCK_BH(&ip_pptp_lock);
++      oldsstate = info->sstate;
++      oldcstate = info->cstate;
 +
-+      if (dir == IP_CT_DIR_ORIGINAL) {
-+              /* reuqests sent by client to server (PNS->PAC) */
-+              pptp_outbound_pkt(pskb, ct, ctinfo, exp);
-+      } else {
-+              /* response from the server to the client (PAC->PNS) */
-+              pptp_inbound_pkt(pskb, ct, ctinfo, exp);
-+      }
++      LOCK_BH(&ip_pptp_lock);
 +
++      /* FIXME: We just blindly assume that the control connection is always
++       * established from PNS->PAC.  However, RFC makes no guarantee */
++      if (dir == IP_CT_DIR_ORIGINAL)
++              /* client -> server (PNS -> PAC) */
++              ret = pptp_outbound_pkt(tcph, pptph, datalen, ct, ctinfo);
++      else
++              /* server -> client (PAC -> PNS) */
++              ret = pptp_inbound_pkt(tcph, pptph, datalen, ct, ctinfo);
++      DEBUGP("sstate: %d->%d, cstate: %d->%d\n",
++              oldsstate, info->sstate, oldcstate, info->cstate);
 +      UNLOCK_BH(&ip_pptp_lock);
 +
-+      return NF_ACCEPT;
++      return ret;
 +}
 +
-+/* nat helper struct for control connection */
-+static struct ip_nat_helper pptp_tcp_helper = { 
++/* control protocol helper */
++static struct ip_conntrack_helper pptp = { 
 +      .list = { NULL, NULL },
 +      .name = "pptp", 
-+      .flags = IP_NAT_HELPER_F_ALWAYS, 
++      .flags = IP_CT_HELPER_F_REUSE_EXPECT,
 +      .me = THIS_MODULE,
++      .max_expected = 2,
++      .timeout = 0,
 +      .tuple = { .src = { .ip = 0, 
-+                          .u = { .tcp = { .port = 
-+                                      __constant_htons(PPTP_CONTROL_PORT) } 
-+                               } 
-+                        },
-+                 .dst = { .ip = 0, 
-+                          .u = { .all = 0 }, 
-+                          .protonum = IPPROTO_TCP 
-+                        } 
++                          .u = { .tcp = { .port =  
++                                  __constant_htons(PPTP_CONTROL_PORT) } } 
++                        }, 
++                 .dst = { .ip = 0, 
++                          .u = { .all = 0 },
++                          .protonum = IPPROTO_TCP
++                        } 
 +               },
-+
 +      .mask = { .src = { .ip = 0, 
-+                         .u = { .tcp = { .port = 0xFFFF } } 
-+                       },
++                         .u = { .tcp = { .port = 0xffff } } 
++                       }, 
 +                .dst = { .ip = 0, 
-+                         .u = { .all = 0 }, 
-+                         .protonum = 0xFFFF 
-+                       } 
++                         .u = { .all = 0 },
++                         .protonum = 0xffff 
++                       } 
 +              },
-+      .help = tcp_help, 
-+      .expect = pptp_nat_expected 
++      .help = conntrack_pptp_help
 +};
 +
-+                        
++/* ip_conntrack_pptp initialization */
 +static int __init init(void)
 +{
-+      DEBUGP("%s: registering NAT helper\n", __FILE__);
-+      if (ip_nat_helper_register(&pptp_tcp_helper)) {
-+              printk(KERN_ERR "Unable to register NAT application helper "
-+                              "for pptp\n");
++      int retcode;
++
++      DEBUGP(__FILE__ ": registering helper\n");
++      if ((retcode = ip_conntrack_helper_register(&pptp))) {
++                printk(KERN_ERR "Unable to register conntrack application "
++                              "helper for pptp: %d\n", retcode);
 +              return -EIO;
 +      }
 +
-+      printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION);
++      printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION);
 +      return 0;
 +}
 +
 +static void __exit fini(void)
 +{
-+      DEBUGP("cleanup_module\n" );
-+      ip_nat_helper_unregister(&pptp_tcp_helper);
-+      printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION);
++      ip_conntrack_helper_unregister(&pptp);
++      printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION);
 +}
 +
 +module_init(init);
 +module_exit(fini);
-diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_nat_proto_gre.c linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_proto_gre.c
---- linux-2.4.29-old/net/ipv4/netfilter/ip_nat_proto_gre.c     1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_proto_gre.c     2005-04-02 19:15:10.000000000 +0200
-@@ -0,0 +1,202 @@
++
++EXPORT_SYMBOL(ip_pptp_lock);
+diff -urN linux.old/net/ipv4/netfilter/ip_conntrack_pptp_priv.h linux.dev/net/ipv4/netfilter/ip_conntrack_pptp_priv.h
+--- linux.old/net/ipv4/netfilter/ip_conntrack_pptp_priv.h      1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/net/ipv4/netfilter/ip_conntrack_pptp_priv.h      2006-12-06 00:34:27.000000000 +0100
+@@ -0,0 +1,24 @@
++#ifndef _IP_CT_PPTP_PRIV_H
++#define _IP_CT_PPTP_PRIV_H
++
++/* PptpControlMessageType names */
++static const char *strMName[] = {
++      "UNKNOWN_MESSAGE",
++      "START_SESSION_REQUEST",
++      "START_SESSION_REPLY",
++      "STOP_SESSION_REQUEST",
++      "STOP_SESSION_REPLY",
++      "ECHO_REQUEST",
++      "ECHO_REPLY",
++      "OUT_CALL_REQUEST",
++      "OUT_CALL_REPLY",
++      "IN_CALL_REQUEST",
++      "IN_CALL_REPLY",
++      "IN_CALL_CONNECT",
++      "CALL_CLEAR_REQUEST",
++      "CALL_DISCONNECT_NOTIFY",
++      "WAN_ERROR_NOTIFY",
++      "SET_LINK_INFO"
++};
++
++#endif
+diff -urN linux.old/net/ipv4/netfilter/ip_conntrack_proto_gre.c linux.dev/net/ipv4/netfilter/ip_conntrack_proto_gre.c
+--- linux.old/net/ipv4/netfilter/ip_conntrack_proto_gre.c      1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/net/ipv4/netfilter/ip_conntrack_proto_gre.c      2006-12-06 00:34:27.000000000 +0100
+@@ -0,0 +1,333 @@
 +/*
-+ * ip_nat_proto_gre.c - Version 1.2
++ * ip_conntrack_proto_gre.c - Version 1.2 
 + *
-+ * NAT protocol helper module for GRE.
++ * 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.
@@ -1559,226 +1356,344 @@ diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_nat_proto_gre.c linux-2.4.29-de
 +
 +#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/netfilter_ipv4/ip_nat.h>
-+#include <linux/netfilter_ipv4/ip_nat_rule.h>
-+#include <linux/netfilter_ipv4/ip_nat_protocol.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 NAT protocol helper module for GRE");
++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)
 +
 +#if 0
 +#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
-+                                     ": " format, ## args)
++                                     ": " 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))
 +#else
 +#define DEBUGP(x, args...)
++#define DUMP_TUPLE_GRE(x)
 +#endif
++                              
++/* GRE KEYMAP HANDLING FUNCTIONS */
++static LIST_HEAD(gre_keymap_list);
 +
-+/* is key in given range between min and max */
-+static int
-+gre_in_range(const struct ip_conntrack_tuple *tuple,
-+           enum ip_nat_manip_type maniptype,
-+           const union ip_conntrack_manip_proto *min,
-+           const union ip_conntrack_manip_proto *max)
++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;
 +
-+      if (maniptype == IP_NAT_MANIP_SRC)
-+              key = tuple->src.u.gre.key;
-+      else
-+              key = tuple->dst.u.gre.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;
++      }
 +
-+      return ntohl(key) >= ntohl(min->gre.key)
-+              && ntohl(key) <= ntohl(max->gre.key);
++      key = km->tuple.src.u.gre.key;
++      READ_UNLOCK(&ip_ct_gre_lock);
++
++      return key;
 +}
 +
-+/* generate unique tuple ... */
-+static int 
-+gre_unique_tuple(struct ip_conntrack_tuple *tuple,
-+               const struct ip_nat_range *range,
-+               enum ip_nat_manip_type maniptype,
-+               const struct ip_conntrack *conntrack)
++/* 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)
 +{
-+      u_int32_t min, i, range_size;
-+      u_int32_t key = 0, *keyptr;
++      struct ip_ct_gre_keymap *km;
 +
-+      if (maniptype == IP_NAT_MANIP_SRC)
-+              keyptr = &tuple->src.u.gre.key;
++      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)
++              exp->proto.gre.keymap_orig = km;
 +      else
-+              keyptr = &tuple->dst.u.gre.key;
++              exp->proto.gre.keymap_reply = km;
 +
-+      if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
++      DEBUGP("adding new entry %p: ", km);
++      DUMP_TUPLE_GRE(&km->tuple);
 +
-+              DEBUGP("%p: NATing GRE PPTP\n", conntrack);
-+              min = 1;
-+              range_size = 0xffff;
++      WRITE_LOCK(&ip_ct_gre_lock);
++      list_append(&gre_keymap_list, km);
++      WRITE_UNLOCK(&ip_ct_gre_lock);
 +
-+      } else {
-+              min = ntohl(range->min.gre.key);
-+              range_size = ntohl(range->max.gre.key) - min + 1;
-+      }
++      return 0;
++}
 +
-+      DEBUGP("min = %u, range_size = %u\n", min, range_size); 
++/* 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);
 +
-+      for (i = 0; i < range_size; i++, key++) {
-+              *keyptr = htonl(min + key % range_size);
-+              if (!ip_nat_used_tuple(tuple, conntrack))
-+                      return 1;
++      WRITE_LOCK(&ip_ct_gre_lock);
++      memcpy(&km->tuple, t, sizeof(km->tuple));
++      WRITE_UNLOCK(&ip_ct_gre_lock);
++}
++
++/* 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;
++      }
++      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;
 +      }
++      WRITE_UNLOCK(&ip_ct_gre_lock);
++}
 +
-+      DEBUGP("%p: no NAT mapping\n", conntrack);
 +
-+      return 0;
++/* 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.key = orig->src.u.gre.key;
++      tuple->src.u.gre.key = orig->dst.u.gre.key;
++
++      return 1;
 +}
 +
-+/* manipulate a GRE packet according to maniptype */
-+static void 
-+gre_manip_pkt(struct iphdr *iph, size_t len, 
-+            const struct ip_conntrack_manip *manip,
-+            enum ip_nat_manip_type maniptype)
++/* 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 *greh = (struct gre_hdr *)((u_int32_t *)iph+iph->ihl);
-+      struct gre_hdr_pptp *pgreh = (struct gre_hdr_pptp *) greh;
++      struct gre_hdr *grehdr = (struct gre_hdr *) datah;
++      struct gre_hdr_pptp *pgrehdr = (struct gre_hdr_pptp *) datah;
++      u_int32_t srckey;
 +
-+      /* we only have destination manip of a packet, since 'source key' 
-+       * is not present in the packet itself */
-+      if (maniptype == IP_NAT_MANIP_DST) {
-+              /* key manipulation is always dest */
-+              switch (greh->version) {
-+              case 0:
-+                      if (!greh->key) {
-+                              DEBUGP("can't nat GRE w/o key\n");
-+                              break;
-+                      }
-+                      if (greh->csum) {
-+                              /* FIXME: Never tested this code... */
-+                              *(gre_csum(greh)) = 
-+                                      ip_nat_cheat_check(~*(gre_key(greh)),
-+                                                      manip->u.gre.key,
-+                                                      *(gre_csum(greh)));
++      /* core guarantees 8 protocol bytes, no need for size check */
++
++      switch (grehdr->version) {
++              case GRE_VERSION_1701:
++                      if (!grehdr->key) {
++                              DEBUGP("Can't track GRE without key\n");
++                              return 0;
 +                      }
-+                      *(gre_key(greh)) = manip->u.gre.key;
++                      tuple->dst.u.gre.key = *(gre_key(grehdr));
 +                      break;
++
 +              case GRE_VERSION_PPTP:
-+                      DEBUGP("call_id -> 0x%04x\n", 
-+                              ntohl(manip->u.gre.key));
-+                      pgreh->call_id = htons(ntohl(manip->u.gre.key));
++                      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:
-+                      DEBUGP("can't nat unknown GRE version\n");
-+                      break;
-+              }
++                      printk(KERN_WARNING "unknown GRE version %hu\n",
++                              grehdr->version);
++                      return 0;
 +      }
++
++      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;
 +}
 +
-+/* print out a nat tuple */
-+static unsigned int 
-+gre_print(char *buffer, 
-+        const struct ip_conntrack_tuple *match,
-+        const struct ip_conntrack_tuple *mask)
++/* print gre part of tuple */
++static unsigned int gre_print_tuple(char *buffer,
++                                  const struct ip_conntrack_tuple *tuple)
 +{
-+      unsigned int len = 0;
++      return sprintf(buffer, "srckey=0x%x dstkey=0x%x ", 
++                      ntohl(tuple->src.u.gre.key),
++                      ntohl(tuple->dst.u.gre.key));
++}
 +
-+      if (mask->src.u.gre.key)
-+              len += sprintf(buffer + len, "srckey=0x%x ", 
-+                              ntohl(match->src.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));
++}
 +
-+      if (mask->dst.u.gre.key)
-+              len += sprintf(buffer + len, "dstkey=0x%x ",
-+                              ntohl(match->src.u.gre.key));
++/* 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;
++}
 +
-+      return len;
++/* 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;
 +}
 +
-+/* print a range of keys */
-+static unsigned int 
-+gre_print_range(char *buffer, const struct ip_nat_range *range)
++/* 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)
 +{
-+      if (range->min.gre.key != 0 
-+          || range->max.gre.key != 0xFFFF) {
-+              if (range->min.gre.key == range->max.gre.key)
-+                      return sprintf(buffer, "key 0x%x ",
-+                                      ntohl(range->min.gre.key));
-+              else
-+                      return sprintf(buffer, "keys 0x%u-0x%u ",
-+                                      ntohl(range->min.gre.key),
-+                                      ntohl(range->max.gre.key));
-+      } else
-+              return 0;
++      struct ip_conntrack_expect *master = ct->master;
++
++      DEBUGP(" entering\n");
++
++      if (!master) {
++              DEBUGP("no master exp for ct %p\n", ct);
++              return;
++      }
++
++      ip_ct_gre_keymap_destroy(master);
 +}
 +
-+/* nat helper struct */
-+static struct ip_nat_protocol gre = 
-+      { { NULL, NULL }, "GRE", IPPROTO_GRE,
-+        gre_manip_pkt,
-+        gre_in_range,
-+        gre_unique_tuple,
-+        gre_print,
-+        gre_print_range 
-+      };
-+                                
++/* 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)
 +{
-+        if (ip_nat_protocol_register(&gre))
-+                return -EIO;
++      int retcode;
 +
-+        return 0;
++      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)
 +{
-+        ip_nat_protocol_unregister(&gre);
++      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 at module unload time\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);
++EXPORT_SYMBOL(ip_ct_gre_keymap_destroy);
++
 +module_init(init);
 +module_exit(fini);
-diff -urN linux-2.4.29-old/net/ipv4/netfilter/Makefile linux-2.4.29-dev/net/ipv4/netfilter/Makefile
---- linux-2.4.29-old/net/ipv4/netfilter/Makefile       2005-04-02 19:04:11.000000000 +0200
-+++ linux-2.4.29-dev/net/ipv4/netfilter/Makefile       2005-04-02 19:07:18.000000000 +0200
-@@ -36,23 +36,32 @@
- ifdef CONFIG_IP_NF_AMANDA
-       export-objs += ip_conntrack_amanda.o
- endif
--
- obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o
- obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o
- ifdef CONFIG_IP_NF_FTP
-       export-objs += ip_conntrack_ftp.o
- endif
--
- obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o
- ifdef CONFIG_IP_NF_IRC
-       export-objs += ip_conntrack_irc.o
- endif
-+obj-$(CONFIG_IP_NF_CT_PROTO_GRE) += ip_conntrack_proto_gre.o
-+ifdef CONFIG_IP_NF_CT_PROTO_GRE
-+      export-objs += ip_conntrack_proto_gre.o
-+endif
-+obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o
-+ifdef CONFIG_IP_NF_NAT_PPTP
-+      export-objs += ip_conntrack_pptp.o
-+endif
-+
+diff -urN linux.old/net/ipv4/netfilter/ip_nat_core.c linux.dev/net/ipv4/netfilter/ip_nat_core.c
+--- linux.old/net/ipv4/netfilter/ip_nat_core.c 2005-04-04 03:42:20.000000000 +0200
++++ linux.dev/net/ipv4/netfilter/ip_nat_core.c 2006-12-06 00:34:27.000000000 +0100
+@@ -424,7 +424,7 @@
+       *tuple = *orig_tuple;
+       while ((rptr = find_best_ips_proto_fast(tuple, mr, conntrack, hooknum))
+              != NULL) {
+-              DEBUGP("Found best for "); DUMP_TUPLE(tuple);
++              DEBUGP("Found best for "); DUMP_TUPLE_RAW(tuple);
+               /* 3) The per-protocol part of the manip is made to
+                  map into the range to make a unique tuple. */
  
- # NAT helpers 
- obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o
- obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o
- obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o
- obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o
-+obj-$(CONFIG_IP_NF_NAT_PROTO_GRE) += ip_nat_proto_gre.o
-+obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o
+@@ -564,9 +564,9 @@
+                      HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST",
+                      conntrack);
+               DEBUGP("Original: ");
+-              DUMP_TUPLE(&orig_tp);
++              DUMP_TUPLE_RAW(&orig_tp);
+               DEBUGP("New: ");
+-              DUMP_TUPLE(&new_tuple);
++              DUMP_TUPLE_RAW(&new_tuple);
+ #endif
  
- # generic IP tables 
- obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o
---- linux-2.4.32/net/ipv4/netfilter/ip_conntrack_pptp.c        1970-01-01 01:00:00.000000000 +0100
-+++ /home/florian//openwrt/trunk/openwrt/build_mipsel/linux/net/ipv4/netfilter/ip_conntrack_pptp.c     2006-02-04 18:23:27.000000000 +0100
-@@ -0,0 +1,635 @@
+               /* We now have two tuples (SRCIP/SRCPT/DSTIP/DSTPT):
+diff -urN linux.old/net/ipv4/netfilter/ip_nat_pptp.c linux.dev/net/ipv4/netfilter/ip_nat_pptp.c
+--- linux.old/net/ipv4/netfilter/ip_nat_pptp.c 1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/net/ipv4/netfilter/ip_nat_pptp.c 2006-12-06 00:34:38.000000000 +0100
+@@ -0,0 +1,482 @@
 +/*
-+ * ip_conntrack_pptp.c        - Version 1.9
++ * ip_nat_pptp.c      - Version 1.5
 + *
-+ * Connection tracking support for PPTP (Point to Point Tunneling Protocol).
++ * NAT support for PPTP (Point to Point Tunneling Protocol).
 + * PPTP is a a protocol for creating virtual private networks.
 + * It is a specification defined by Microsoft and some vendors
 + * working with Microsoft.  PPTP is built on top of a modified
@@ -1790,623 +1705,714 @@ diff -urN linux-2.4.29-old/net/ipv4/netfilter/Makefile linux-2.4.29-dev/net/ipv4
 + *
 + * Development of this code funded by Astaro AG (http://www.astaro.com/)
 + *
-+ * Limitations:
-+ *     - We blindly assume that control connections are always
-+ *       established in PNS->PAC direction.  This is a violation
-+ *       of RFFC2673
-+ *
-+ * TODO: - finish support for multiple calls within one session
-+ *       (needs expect reservations in newnat)
-+ *     - testing of incoming PPTP calls 
-+ *
-+ * Changes: 
-+ *    2002-02-05 - Version 1.3
-+ *      - Call ip_conntrack_unexpect_related() from 
-+ *        pptp_timeout_related() to destroy expectations in case
-+ *        CALL_DISCONNECT_NOTIFY or tcp fin packet was seen
-+ *        (Philip Craig <philipc@snapgear.com>)
-+ *      - Add Version information at module loadtime
-+ *    2002-02-10 - Version 1.6
-+ *      - move to C99 style initializers
-+ *      - remove second expectation if first arrives
++ * TODO: - Support for multiple calls within one session
++ *       (needs netfilter newnat code)
++ *     - NAT to a unique tuple, not to TCP source port
++ *       (needs netfilter tuple reservation)
 + *
++ * Changes:
++ *     2002-02-10 - Version 1.3
++ *       - Use ip_nat_mangle_tcp_packet() because of cloned skb's
++ *       in local connections (Philip Craig <philipc@snapgear.com>)
++ *       - add checks for magicCookie and pptp version
++ *       - make argument list of pptp_{out,in}bound_packet() shorter
++ *       - move to C99 style initializers
++ *       - print version number at module loadtime
++ *     2003-09-22 - Version 1.5
++ *       - use SNATed tcp sourceport as callid, since we get called before
++ *         TCP header is mangled (Philip Craig <philipc@snapgear.com>)
++ * 
 + */
 +
 +#include <linux/config.h>
 +#include <linux/module.h>
-+#include <linux/netfilter.h>
 +#include <linux/ip.h>
-+#include <net/checksum.h>
-+#include <net/tcp.h>
-+
-+#include <linux/netfilter_ipv4/lockhelp.h>
++#include <linux/tcp.h>
++#include <net/tcp.h>
++#include <linux/netfilter_ipv4/ip_nat.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++#include <linux/netfilter_ipv4/ip_nat_helper.h>
++#include <linux/netfilter_ipv4/ip_nat_pptp.h>
 +#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
 +#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
 +#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
 +
-+#define IP_CT_PPTP_VERSION "1.9"
++#define IP_NAT_PPTP_VERSION "1.5"
 +
 +MODULE_LICENSE("GPL");
 +MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
-+MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP");
++MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
 +
-+DECLARE_LOCK(ip_pptp_lock);
 +
 +#if 0
 +#include "ip_conntrack_pptp_priv.h"
-+#define DEBUGP(format, args...)       printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
-+                                      ": " format, ## args)
++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
++                                     ": " format, ## args)
 +#else
 +#define DEBUGP(format, args...)
 +#endif
 +
-+#define SECS *HZ
-+#define MINS * 60 SECS
-+#define HOURS * 60 MINS
-+#define DAYS * 24 HOURS
-+
-+#define PPTP_GRE_TIMEOUT              (10 MINS)
-+#define PPTP_GRE_STREAM_TIMEOUT       (5 DAYS)
-+
-+static int pptp_expectfn(struct ip_conntrack *ct)
++static unsigned int
++pptp_nat_expected(struct sk_buff **pskb,
++                unsigned int hooknum,
++                struct ip_conntrack *ct,
++                struct ip_nat_info *info)
 +{
-+      struct ip_conntrack *master;
-+      struct ip_conntrack_expect *exp;
++      struct ip_conntrack *master = master_ct(ct);
++      struct ip_nat_multi_range mr;
++      struct ip_ct_pptp_master *ct_pptp_info;
++      struct ip_nat_pptp *nat_pptp_info;
++      u_int32_t newip, newcid;
++      int ret;
 +
-+      DEBUGP("increasing timeouts\n");
-+      /* increase timeout of GRE data channel conntrack entry */
-+      ct->proto.gre.timeout = PPTP_GRE_TIMEOUT;
-+      ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT;
++      IP_NF_ASSERT(info);
++      IP_NF_ASSERT(master);
++      IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
 +
-+      master = master_ct(ct);
-+      if (!master) {
-+              DEBUGP(" no master!!!\n");
-+              return 0;
-+      }
++      DEBUGP("we have a connection!\n");
 +
-+      exp = ct->master;
-+      if (!exp) {
-+              DEBUGP("no expectation!!\n");
-+              return 0;
-+      }
++      LOCK_BH(&ip_pptp_lock);
++      ct_pptp_info = &master->help.ct_pptp_info;
++      nat_pptp_info = &master->nat.help.nat_pptp_info;
 +
-+      DEBUGP("completing tuples with ct info\n");
-+      /* we can do this, since we're unconfirmed */
-+      if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == 
-+              htonl(master->help.ct_pptp_info.pac_call_id)) { 
-+              /* assume PNS->PAC */
-+              ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = 
-+                      htonl(master->help.ct_pptp_info.pns_call_id);
-+              ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
-+                      htonl(master->help.ct_pptp_info.pns_call_id);
++      /* need to alter GRE tuple because conntrack expectfn() used 'wrong'
++       * (unmanipulated) values */
++      if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
++              DEBUGP("completing tuples with NAT info \n");
++              /* we can do this, since we're unconfirmed */
++              if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key ==
++                      htonl(ct_pptp_info->pac_call_id)) {     
++                      /* assume PNS->PAC */
++                      ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
++                              htonl(nat_pptp_info->pns_call_id);
++                      ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
++                              htonl(nat_pptp_info->pns_call_id);
++                      newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
++                      newcid = htonl(nat_pptp_info->pac_call_id);
++              } else {
++                      /* assume PAC->PNS */
++                      ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
++                              htonl(nat_pptp_info->pac_call_id);
++                      ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
++                              htonl(nat_pptp_info->pac_call_id);
++                      newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
++                      newcid = htonl(nat_pptp_info->pns_call_id);
++              }
 +      } else {
-+              /* assume PAC->PNS */
-+              ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
-+                      htonl(master->help.ct_pptp_info.pac_call_id);
-+              ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
-+                      htonl(master->help.ct_pptp_info.pac_call_id);
-+      }
-+      
-+      /* delete other expectation */
-+      if (exp->expected_list.next != &exp->expected_list) {
-+              struct ip_conntrack_expect *other_exp;
-+              struct list_head *cur_item, *next;
-+
-+              for (cur_item = master->sibling_list.next;
-+                   cur_item != &master->sibling_list; cur_item = next) {
-+                      next = cur_item->next;
-+                      other_exp = list_entry(cur_item,
-+                                             struct ip_conntrack_expect,
-+                                             expected_list);
-+                      /* remove only if occurred at same sequence number */
-+                      if (other_exp != exp && other_exp->seq == exp->seq) {
-+                              DEBUGP("unexpecting other direction\n");
-+                              ip_ct_gre_keymap_destroy(other_exp);
-+                              ip_conntrack_unexpect_related(other_exp);
-+                      }
++              if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key ==
++                      htonl(ct_pptp_info->pac_call_id)) {     
++                      /* assume PNS->PAC */
++                      newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++                      newcid = htonl(ct_pptp_info->pns_call_id);
++              }
++              else {
++                      /* assume PAC->PNS */
++                      newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
++                      newcid = htonl(ct_pptp_info->pac_call_id);
 +              }
 +      }
 +
-+      return 0;
-+}
-+
-+/* timeout GRE data connections */
-+static int pptp_timeout_related(struct ip_conntrack *ct)
-+{
-+      struct list_head *cur_item, *next;
-+      struct ip_conntrack_expect *exp;
-+      enum ip_conntrack_info ctinfo;
-+      struct iphdr *iph;
-+
-+      /* FIXME: do we have to lock something ? */
-+      for (cur_item = ct->sibling_list.next;
-+          cur_item != &ct->sibling_list; cur_item = next) {
-+              next = cur_item->next;
-+              exp = list_entry(cur_item, struct ip_conntrack_expect,
-+                               expected_list);
++      mr.rangesize = 1;
++      mr.range[0].flags = IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED;
++      mr.range[0].min_ip = mr.range[0].max_ip = newip;
++      mr.range[0].min = mr.range[0].max = 
++              ((union ip_conntrack_manip_proto ) { newcid }); 
++      DEBUGP("change ip to %u.%u.%u.%u\n", 
++              NIPQUAD(newip));
++      DEBUGP("change key to 0x%x\n", ntohl(newcid));
++      ret = ip_nat_setup_info(ct, &mr, hooknum);
 +
-+              ip_ct_gre_keymap_destroy(exp);
-+              if (!exp->sibling) {
-+                      ip_conntrack_unexpect_related(exp);
-+                      continue;
-+              }
++      UNLOCK_BH(&ip_pptp_lock);
 +
-+              DEBUGP("setting timeout of conntrack %p to 0\n",
-+                      exp->sibling);
-+              exp->sibling->proto.gre.timeout = 0;
-+              exp->sibling->proto.gre.stream_timeout = 0;
-+              ip_ct_refresh_acct(exp->sibling, ctinfo, iph, 0);
-+      }
++      return ret;
 +
-+      return 0;
 +}
 +
-+/* expect GRE connections (PNS->PAC and PAC->PNS direction) */
-+static inline int
-+exp_gre(struct ip_conntrack *master,
-+      u_int32_t seq,
-+      u_int16_t callid,
-+      u_int16_t peer_callid)
++/* outbound packets == from PNS to PAC */
++static inline unsigned int
++pptp_outbound_pkt(struct sk_buff **pskb,
++                struct ip_conntrack *ct,
++                enum ip_conntrack_info ctinfo,
++                struct ip_conntrack_expect *exp)
++
 +{
-+      struct ip_conntrack_expect exp;
-+      struct ip_conntrack_tuple inv_tuple;
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct tcphdr *tcph = (void *) iph + iph->ihl*4;
++      struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *) 
++                                      ((void *)tcph + tcph->doff*4);
 +
-+      memset(&exp, 0, sizeof(exp));
-+      /* tuple in original direction, PNS->PAC */
-+      exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
-+      exp.tuple.src.u.gre.key = htonl(ntohs(peer_callid));
-+      exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
-+      exp.tuple.dst.u.gre.key = htonl(ntohs(callid));
-+      exp.tuple.dst.protonum = IPPROTO_GRE;
++      struct PptpControlHeader *ctlh;
++      union pptp_ctrl_union pptpReq;
++      struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
++      struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
 +
-+      exp.mask.src.ip = 0xffffffff;
-+      exp.mask.src.u.all = 0;
-+      exp.mask.dst.u.all = 0;
-+      exp.mask.dst.u.gre.key = 0xffffffff;
-+      exp.mask.dst.ip = 0xffffffff;
-+      exp.mask.dst.protonum = 0xffff;
-+                      
-+      exp.seq = seq;
-+      exp.expectfn = pptp_expectfn;
++      u_int16_t msg, *cid = NULL, new_callid;
 +
-+      exp.help.exp_pptp_info.pac_call_id = ntohs(callid);
-+      exp.help.exp_pptp_info.pns_call_id = ntohs(peer_callid);
++      /* FIXME: size checks !!! */
++      ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
++      pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
 +
-+      DEBUGP("calling expect_related ");
-+      DUMP_TUPLE_RAW(&exp.tuple);
++      new_callid = htons(ct_pptp_info->pns_call_id);
 +      
-+      /* Add GRE keymap entries */
-+      if (ip_ct_gre_keymap_add(&exp, &exp.tuple, 0) != 0)
-+              return 1;
++      switch (msg = ntohs(ctlh->messageType)) {
++              case PPTP_OUT_CALL_REQUEST:
++                      cid = &pptpReq.ocreq->callID;
++                      /* FIXME: ideally we would want to reserve a call ID
++                       * here.  current netfilter NAT core is not able to do
++                       * this :( For now we use TCP source port. This breaks
++                       * multiple calls within one control session */
 +
-+      invert_tuplepr(&inv_tuple, &exp.tuple);
-+      if (ip_ct_gre_keymap_add(&exp, &inv_tuple, 1) != 0) {
-+              ip_ct_gre_keymap_destroy(&exp);
-+              return 1;
-+      }
-+      
-+      if (ip_conntrack_expect_related(master, &exp) != 0) {
-+              ip_ct_gre_keymap_destroy(&exp);
-+              DEBUGP("cannot expect_related()\n");
-+              return 1;
++                      /* save original call ID in nat_info */
++                      nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id;
++
++                      /* don't use tcph->source since we are at a DSTmanip
++                       * hook (e.g. PREROUTING) and pkt is not mangled yet */
++                      new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port;
++
++                      /* save new call ID in ct info */
++                      ct_pptp_info->pns_call_id = ntohs(new_callid);
++                      break;
++              case PPTP_IN_CALL_REPLY:
++                      cid = &pptpReq.icreq->callID;
++                      break;
++              case PPTP_CALL_CLEAR_REQUEST:
++                      cid = &pptpReq.clrreq->callID;
++                      break;
++              default:
++                      DEBUGP("unknown outbound packet 0x%04x:%s\n", msg,
++                            (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
++                      /* fall through */
++
++              case PPTP_SET_LINK_INFO:
++                      /* only need to NAT in case PAC is behind NAT box */
++              case PPTP_START_SESSION_REQUEST:
++              case PPTP_START_SESSION_REPLY:
++              case PPTP_STOP_SESSION_REQUEST:
++              case PPTP_STOP_SESSION_REPLY:
++              case PPTP_ECHO_REQUEST:
++              case PPTP_ECHO_REPLY:
++                      /* no need to alter packet */
++                      return NF_ACCEPT;
 +      }
 +
-+      /* tuple in reply direction, PAC->PNS */
-+      exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
-+      exp.tuple.src.u.gre.key = htonl(ntohs(callid));
-+      exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
-+      exp.tuple.dst.u.gre.key = htonl(ntohs(peer_callid));
++      IP_NF_ASSERT(cid);
 +
-+      DEBUGP("calling expect_related ");
-+      DUMP_TUPLE_RAW(&exp.tuple);
-+      
-+      /* Add GRE keymap entries */
-+      ip_ct_gre_keymap_add(&exp, &exp.tuple, 0);
-+      invert_tuplepr(&inv_tuple, &exp.tuple);
-+      ip_ct_gre_keymap_add(&exp, &inv_tuple, 1);
-+      /* FIXME: cannot handle error correctly, since we need to free
-+       * the above keymap :( */
-+      
-+      if (ip_conntrack_expect_related(master, &exp) != 0) {
-+              /* free the second pair of keypmaps */
-+              ip_ct_gre_keymap_destroy(&exp);
-+              DEBUGP("cannot expect_related():\n");
-+              return 1;
-+      }
++      DEBUGP("altering call id from 0x%04x to 0x%04x\n",
++              ntohs(*cid), ntohs(new_callid));
 +
-+      return 0;
++      /* mangle packet */
++      ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)cid - (void *)pptph,
++                               sizeof(new_callid), (char *)&new_callid,
++                               sizeof(new_callid));
++
++      return NF_ACCEPT;
 +}
 +
-+static inline int 
-+pptp_inbound_pkt(struct tcphdr *tcph,
-+               struct pptp_pkt_hdr *pptph, 
-+               size_t datalen,
++/* inbound packets == from PAC to PNS */
++static inline unsigned int
++pptp_inbound_pkt(struct sk_buff **pskb,
 +               struct ip_conntrack *ct,
-+               enum ip_conntrack_info ctinfo)
++               enum ip_conntrack_info ctinfo,
++               struct ip_conntrack_expect *oldexp)
 +{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct tcphdr *tcph = (void *) iph + iph->ihl*4;
++      struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *) 
++                                      ((void *)tcph + tcph->doff*4);
++
 +      struct PptpControlHeader *ctlh;
-+        union pptp_ctrl_union pptpReq;
-+      
-+      struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
-+      u_int16_t msg, *cid, *pcid;
-+      u_int32_t seq;  
++      union pptp_ctrl_union pptpReq;
++      struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
++      struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
 +
-+      ctlh = (struct PptpControlHeader *) 
-+              ((char *) pptph + sizeof(struct pptp_pkt_hdr));
-+      pptpReq.rawreq = (void *) 
-+              ((char *) ctlh + sizeof(struct PptpControlHeader));
++      u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL;
++      u_int32_t old_dst_ip;
 +
-+      msg = ntohs(ctlh->messageType);
-+      DEBUGP("inbound control message %s\n", strMName[msg]);
++      struct ip_conntrack_tuple t, inv_t;
++      struct ip_conntrack_tuple *orig_t, *reply_t;
 +
-+      switch (msg) {
-+      case PPTP_START_SESSION_REPLY:
-+              /* server confirms new control session */
-+              if (info->sstate < PPTP_SESSION_REQUESTED) {
-+                      DEBUGP("%s without START_SESS_REQUEST\n",
-+                              strMName[msg]);
-+                      break;
-+              }
-+              if (pptpReq.srep->resultCode == PPTP_START_OK)
-+                      info->sstate = PPTP_SESSION_CONFIRMED;
-+              else 
-+                      info->sstate = PPTP_SESSION_ERROR;
-+              break;
++      /* FIXME: size checks !!! */
++      ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
++      pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
 +
-+      case PPTP_STOP_SESSION_REPLY:
-+              /* server confirms end of control session */
-+              if (info->sstate > PPTP_SESSION_STOPREQ) {
-+                      DEBUGP("%s without STOP_SESS_REQUEST\n",
-+                              strMName[msg]);
-+                      break;
-+              }
-+              if (pptpReq.strep->resultCode == PPTP_STOP_OK)
-+                      info->sstate = PPTP_SESSION_NONE;
-+              else
-+                      info->sstate = PPTP_SESSION_ERROR;
-+              break;
++      new_pcid = htons(nat_pptp_info->pns_call_id);
 +
++      switch (msg = ntohs(ctlh->messageType)) {
 +      case PPTP_OUT_CALL_REPLY:
-+              /* server accepted call, we now expect GRE frames */
-+              if (info->sstate != PPTP_SESSION_CONFIRMED) {
-+                      DEBUGP("%s but no session\n", strMName[msg]);
-+                      break;
-+              }
-+              if (info->cstate != PPTP_CALL_OUT_REQ &&
-+                  info->cstate != PPTP_CALL_OUT_CONF) {
-+                      DEBUGP("%s without OUTCALL_REQ\n", strMName[msg]);
-+                      break;
-+              }
-+              if (pptpReq.ocack->resultCode != PPTP_OUTCALL_CONNECT) {
-+                      info->cstate = PPTP_CALL_NONE;
-+                      break;
-+              }
-+
++              pcid = &pptpReq.ocack->peersCallID;     
 +              cid = &pptpReq.ocack->callID;
-+              pcid = &pptpReq.ocack->peersCallID;
-+
-+              info->pac_call_id = ntohs(*cid);
-+              
-+              if (htons(info->pns_call_id) != *pcid) {
-+                      DEBUGP("%s for unknown callid %u\n",
-+                              strMName[msg], ntohs(*pcid));
++              if (!oldexp) {
++                      DEBUGP("outcall but no expectation\n");
 +                      break;
 +              }
++              old_dst_ip = oldexp->tuple.dst.ip;
++              t = oldexp->tuple;
++              invert_tuplepr(&inv_t, &t);
 +
-+              DEBUGP("%s, CID=%X, PCID=%X\n", strMName[msg], 
-+                      ntohs(*cid), ntohs(*pcid));
-+              
-+              info->cstate = PPTP_CALL_OUT_CONF;
++              /* save original PAC call ID in nat_info */
++              nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
 +
-+              seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph);
-+              if (exp_gre(ct, seq, *cid, *pcid) != 0)
-+                      printk("ip_conntrack_pptp: error during exp_gre\n");
-+              break;
++              /* alter expectation */
++              orig_t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++              reply_t = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
++              if (t.src.ip == orig_t->src.ip && t.dst.ip == orig_t->dst.ip) {
++                      /* expectation for PNS->PAC direction */
++                      t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id);
++                      t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id);
++                      inv_t.src.ip = reply_t->src.ip;
++                      inv_t.dst.ip = reply_t->dst.ip;
++                      inv_t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id);
++                      inv_t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id);
++              } else {
++                      /* expectation for PAC->PNS direction */
++                      t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id);
++                      t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id);
++                      inv_t.src.ip = orig_t->src.ip;
++                      inv_t.dst.ip = orig_t->dst.ip;
++                      inv_t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id);
++                      inv_t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id);
++              }
 +
-+      case PPTP_IN_CALL_REQUEST:
-+              /* server tells us about incoming call request */
-+              if (info->sstate != PPTP_SESSION_CONFIRMED) {
-+                      DEBUGP("%s but no session\n", strMName[msg]);
-+                      break;
++              if (!ip_conntrack_change_expect(oldexp, &t)) {
++                      DEBUGP("successfully changed expect\n");
++              } else {
++                      DEBUGP("can't change expect\n");
 +              }
-+              pcid = &pptpReq.icack->peersCallID;
-+              DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
-+              info->cstate = PPTP_CALL_IN_REQ;
-+              info->pac_call_id= ntohs(*pcid);
++              if (oldexp->proto.gre.keymap_orig)
++                      ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig, 
++                                              &t);
++              if (oldexp->proto.gre.keymap_reply)
++                      ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, 
++                                              &inv_t);
 +              break;
-+
 +      case PPTP_IN_CALL_CONNECT:
-+              /* server tells us about incoming call established */
-+              if (info->sstate != PPTP_SESSION_CONFIRMED) {
-+                      DEBUGP("%s but no session\n", strMName[msg]);
++              pcid = &pptpReq.iccon->peersCallID;
++              if (!oldexp)
 +                      break;
++              old_dst_ip = oldexp->tuple.dst.ip;
++              t = oldexp->tuple;
++
++              /* alter expectation, no need for callID */
++              if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) {
++                      /* expectation for PNS->PAC direction */
++                      t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
++              } else {
++                      /* expectation for PAC->PNS direction */
++                      t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
 +              }
-+              if (info->sstate != PPTP_CALL_IN_REP
-+                  && info->sstate != PPTP_CALL_IN_CONF) {
-+                      DEBUGP("%s but never sent IN_CALL_REPLY\n",
-+                              strMName[msg]);
-+                      break;
++
++              if (!ip_conntrack_change_expect(oldexp, &t)) {
++                      DEBUGP("successfully changed expect\n");
++              } else {
++                      DEBUGP("can't change expect\n");
 +              }
++              break;
++      case PPTP_IN_CALL_REQUEST:
++              /* only need to nat in case PAC is behind NAT box */
++              break;
++      case PPTP_WAN_ERROR_NOTIFY:
++              pcid = &pptpReq.wanerr->peersCallID;
++              break;
++      case PPTP_CALL_DISCONNECT_NOTIFY:
++              pcid = &pptpReq.disc->callID;
++              break;
++      case PPTP_SET_LINK_INFO:
++              pcid = &pptpReq.setlink->peersCallID;
++              break;
 +
-+              pcid = &pptpReq.iccon->peersCallID;
-+              cid = &info->pac_call_id;
++      default:
++              DEBUGP("unknown inbound packet %s\n",
++                      (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
++              /* fall through */
++
++      case PPTP_START_SESSION_REQUEST:
++      case PPTP_START_SESSION_REPLY:
++      case PPTP_STOP_SESSION_REQUEST:
++      case PPTP_STOP_SESSION_REPLY:
++      case PPTP_ECHO_REQUEST:
++      case PPTP_ECHO_REPLY:
++              /* no need to alter packet */
++              return NF_ACCEPT;
++      }
++
++      /* mangle packet */
++      IP_NF_ASSERT(pcid);
++      DEBUGP("altering peer call id from 0x%04x to 0x%04x\n",
++              ntohs(*pcid), ntohs(new_pcid));
++      ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)pcid - (void *)pptph,
++                               sizeof(new_pcid), (char *)&new_pcid, 
++                               sizeof(new_pcid));
++
++      if (new_cid) {
++              IP_NF_ASSERT(cid);
++              DEBUGP("altering call id from 0x%04x to 0x%04x\n",
++                      ntohs(*cid), ntohs(new_cid));
++              ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, 
++                                       (void *)cid - (void *)pptph, 
++                                       sizeof(new_cid), (char *)&new_cid, 
++                                       sizeof(new_cid));
++      }
++
++      /* great, at least we don't need to resize packets */
++      return NF_ACCEPT;
++}
++
++
++static unsigned int tcp_help(struct ip_conntrack *ct,
++                           struct ip_conntrack_expect *exp,
++                           struct ip_nat_info *info,
++                           enum ip_conntrack_info ctinfo,
++                           unsigned int hooknum, struct sk_buff **pskb)
++{
++      struct iphdr *iph = (*pskb)->nh.iph;
++      struct tcphdr *tcph = (void *) iph + iph->ihl*4;
++      unsigned int datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4;
++      struct pptp_pkt_hdr *pptph;
 +
-+              if (info->pns_call_id != ntohs(*pcid)) {
-+                      DEBUGP("%s for unknown CallID %u\n", 
-+                              strMName[msg], ntohs(*cid));
-+                      break;
-+              }
++      int dir;
 +
-+              DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
-+              info->cstate = PPTP_CALL_IN_CONF;
++      DEBUGP("entering\n");
 +
-+              /* we expect a GRE connection from PAC to PNS */
-+              seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph);
-+              if (exp_gre(ct, seq, *cid, *pcid) != 0)
-+                      printk("ip_conntrack_pptp: error during exp_gre\n");
++      /* Only mangle things once: DST for original direction
++         and SRC for reply direction. */
++      dir = CTINFO2DIR(ctinfo);
++      if (!((HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
++           && dir == IP_CT_DIR_ORIGINAL)
++            || (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST
++                && dir == IP_CT_DIR_REPLY))) {
++              DEBUGP("Not touching dir %s at hook %s\n",
++                     dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
++                     hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
++                     : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
++                     : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT"
++                     : hooknum == NF_IP_LOCAL_IN ? "INPUT" : "???");
++              return NF_ACCEPT;
++      }
 +
-+              break;
++      /* if packet is too small, just skip it */
++      if (datalen < sizeof(struct pptp_pkt_hdr)+
++                    sizeof(struct PptpControlHeader)) {
++              DEBUGP("pptp packet too short\n");
++              return NF_ACCEPT;       
++      }
 +
-+      case PPTP_CALL_DISCONNECT_NOTIFY:
-+              /* server confirms disconnect */
-+              cid = &pptpReq.disc->callID;
-+              DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid));
-+              info->cstate = PPTP_CALL_NONE;
++      pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4);
 +
-+              /* untrack this call id, unexpect GRE packets */
-+              pptp_timeout_related(ct);
-+              break;
++      /* if it's not a control message, we can't handle it */
++      if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
++          ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
++              DEBUGP("not a pptp control packet\n");
++              return NF_ACCEPT;
++      }
 +
-+      case PPTP_WAN_ERROR_NOTIFY:
-+              break;
++      LOCK_BH(&ip_pptp_lock);
 +
-+      case PPTP_ECHO_REQUEST:
-+      case PPTP_ECHO_REPLY:
-+              /* I don't have to explain these ;) */
-+              break;
-+      default:
-+              DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)
-+                      ? strMName[msg]:strMName[0], msg);
-+              break;
++      if (dir == IP_CT_DIR_ORIGINAL) {
++              /* reuqests sent by client to server (PNS->PAC) */
++              pptp_outbound_pkt(pskb, ct, ctinfo, exp);
++      } else {
++              /* response from the server to the client (PAC->PNS) */
++              pptp_inbound_pkt(pskb, ct, ctinfo, exp);
 +      }
 +
++      UNLOCK_BH(&ip_pptp_lock);
++
 +      return NF_ACCEPT;
++}
++
++/* nat helper struct for control connection */
++static struct ip_nat_helper pptp_tcp_helper = { 
++      .list = { NULL, NULL },
++      .name = "pptp", 
++      .flags = IP_NAT_HELPER_F_ALWAYS, 
++      .me = THIS_MODULE,
++      .tuple = { .src = { .ip = 0, 
++                          .u = { .tcp = { .port = 
++                                      __constant_htons(PPTP_CONTROL_PORT) } 
++                               } 
++                        },
++                 .dst = { .ip = 0, 
++                          .u = { .all = 0 }, 
++                          .protonum = IPPROTO_TCP 
++                        } 
++               },
++
++      .mask = { .src = { .ip = 0, 
++                         .u = { .tcp = { .port = 0xFFFF } } 
++                       },
++                .dst = { .ip = 0, 
++                         .u = { .all = 0 }, 
++                         .protonum = 0xFFFF 
++                       } 
++              },
++      .help = tcp_help, 
++      .expect = pptp_nat_expected 
++};
++
++                        
++static int __init init(void)
++{
++      DEBUGP("%s: registering NAT helper\n", __FILE__);
++      if (ip_nat_helper_register(&pptp_tcp_helper)) {
++              printk(KERN_ERR "Unable to register NAT application helper "
++                              "for pptp\n");
++              return -EIO;
++      }
 +
++      printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION);
++      return 0;
 +}
 +
-+static inline int
-+pptp_outbound_pkt(struct tcphdr *tcph,
-+                struct pptp_pkt_hdr *pptph,
-+                size_t datalen,
-+                struct ip_conntrack *ct,
-+                enum ip_conntrack_info ctinfo)
++static void __exit fini(void)
 +{
-+      struct PptpControlHeader *ctlh;
-+        union pptp_ctrl_union pptpReq;
-+      struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
-+      u_int16_t msg, *cid, *pcid;
++      DEBUGP("cleanup_module\n" );
++      ip_nat_helper_unregister(&pptp_tcp_helper);
++      printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION);
++}
 +
-+      ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
-+      pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
++module_init(init);
++module_exit(fini);
+diff -urN linux.old/net/ipv4/netfilter/ip_nat_proto_gre.c linux.dev/net/ipv4/netfilter/ip_nat_proto_gre.c
+--- linux.old/net/ipv4/netfilter/ip_nat_proto_gre.c    1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/net/ipv4/netfilter/ip_nat_proto_gre.c    2006-12-06 00:34:27.000000000 +0100
+@@ -0,0 +1,202 @@
++/*
++ * ip_nat_proto_gre.c - Version 1.2
++ *
++ * NAT 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-2003 by Harald Welte <laforge@gnumonks.org>
++ *
++ * Development of this code funded by Astaro AG (http://www.astaro.com/)
++ *
++ */
 +
-+      msg = ntohs(ctlh->messageType);
-+      DEBUGP("outbound control message %s\n", strMName[msg]);
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/netfilter_ipv4/ip_nat.h>
++#include <linux/netfilter_ipv4/ip_nat_rule.h>
++#include <linux/netfilter_ipv4/ip_nat_protocol.h>
++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
 +
-+      switch (msg) {
-+      case PPTP_START_SESSION_REQUEST:
-+              /* client requests for new control session */
-+              if (info->sstate != PPTP_SESSION_NONE) {
-+                      DEBUGP("%s but we already have one",
-+                              strMName[msg]);
-+              }
-+              info->sstate = PPTP_SESSION_REQUESTED;
-+              break;
-+      case PPTP_STOP_SESSION_REQUEST:
-+              /* client requests end of control session */
-+              info->sstate = PPTP_SESSION_STOPREQ;
-+              break;
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
++MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE");
 +
-+      case PPTP_OUT_CALL_REQUEST:
-+              /* client initiating connection to server */
-+              if (info->sstate != PPTP_SESSION_CONFIRMED) {
-+                      DEBUGP("%s but no session\n",
-+                              strMName[msg]);
-+                      break;
-+              }
-+              info->cstate = PPTP_CALL_OUT_REQ;
-+              /* track PNS call id */
-+              cid = &pptpReq.ocreq->callID;
-+              DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid));
-+              info->pns_call_id = ntohs(*cid);
-+              break;
-+      case PPTP_IN_CALL_REPLY:
-+              /* client answers incoming call */
-+              if (info->cstate != PPTP_CALL_IN_REQ
-+                  && info->cstate != PPTP_CALL_IN_REP) {
-+                      DEBUGP("%s without incall_req\n", 
-+                              strMName[msg]);
-+                      break;
-+              }
-+              if (pptpReq.icack->resultCode != PPTP_INCALL_ACCEPT) {
-+                      info->cstate = PPTP_CALL_NONE;
-+                      break;
-+              }
-+              pcid = &pptpReq.icack->peersCallID;
-+              if (info->pac_call_id != ntohs(*pcid)) {
-+                      DEBUGP("%s for unknown call %u\n", 
-+                              strMName[msg], ntohs(*pcid));
-+                      break;
-+              }
-+              DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*pcid));
-+              /* part two of the three-way handshake */
-+              info->cstate = PPTP_CALL_IN_REP;
-+              info->pns_call_id = ntohs(pptpReq.icack->callID);
-+              break;
++#if 0
++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
++                                     ": " format, ## args)
++#else
++#define DEBUGP(x, args...)
++#endif
 +
-+      case PPTP_CALL_CLEAR_REQUEST:
-+              /* client requests hangup of call */
-+              if (info->sstate != PPTP_SESSION_CONFIRMED) {
-+                      DEBUGP("CLEAR_CALL but no session\n");
-+                      break;
-+              }
-+              /* FUTURE: iterate over all calls and check if
-+               * call ID is valid.  We don't do this without newnat,
-+               * because we only know about last call */
-+              info->cstate = PPTP_CALL_CLEAR_REQ;
-+              break;
-+      case PPTP_SET_LINK_INFO:
-+              break;
-+      case PPTP_ECHO_REQUEST:
-+      case PPTP_ECHO_REPLY:
-+              /* I don't have to explain these ;) */
-+              break;
-+      default:
-+              DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)? 
-+                      strMName[msg]:strMName[0], msg);
-+              /* unknown: no need to create GRE masq table entry */
-+              break;
-+      }
++/* is key in given range between min and max */
++static int
++gre_in_range(const struct ip_conntrack_tuple *tuple,
++           enum ip_nat_manip_type maniptype,
++           const union ip_conntrack_manip_proto *min,
++           const union ip_conntrack_manip_proto *max)
++{
++      u_int32_t key;
++
++      if (maniptype == IP_NAT_MANIP_SRC)
++              key = tuple->src.u.gre.key;
++      else
++              key = tuple->dst.u.gre.key;
 +
-+      return NF_ACCEPT;
++      return ntohl(key) >= ntohl(min->gre.key)
++              && ntohl(key) <= ntohl(max->gre.key);
 +}
 +
-+
-+/* track caller id inside control connection, call expect_related */
++/* generate unique tuple ... */
 +static int 
-+conntrack_pptp_help(const struct iphdr *iph, size_t len,
-+                  struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
-+
++gre_unique_tuple(struct ip_conntrack_tuple *tuple,
++               const struct ip_nat_range *range,
++               enum ip_nat_manip_type maniptype,
++               const struct ip_conntrack *conntrack)
 +{
-+      struct pptp_pkt_hdr *pptph;
-+      
-+      struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
-+      u_int32_t tcplen = len - iph->ihl * 4;
-+      u_int32_t datalen = tcplen - tcph->doff * 4;
-+      void *datalimit;
-+      int dir = CTINFO2DIR(ctinfo);
-+      struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
++      u_int32_t min, i, range_size;
++      u_int32_t key = 0, *keyptr;
 +
-+      int oldsstate, oldcstate;
-+      int ret;
++      if (maniptype == IP_NAT_MANIP_SRC)
++              keyptr = &tuple->src.u.gre.key;
++      else
++              keyptr = &tuple->dst.u.gre.key;
 +
-+      /* don't do any tracking before tcp handshake complete */
-+      if (ctinfo != IP_CT_ESTABLISHED 
-+          && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
-+              DEBUGP("ctinfo = %u, skipping\n", ctinfo);
-+              return NF_ACCEPT;
-+      }
-+      
-+      /* not a complete TCP header? */
-+      if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
-+              DEBUGP("tcplen = %u\n", tcplen);
-+              return NF_ACCEPT;
-+      }
++      if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
 +
-+      /* checksum invalid? */
-+      if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
-+                      csum_partial((char *) tcph, tcplen, 0))) {
-+              printk(KERN_NOTICE __FILE__ ": bad csum\n");
-+              /* W2K PPTP server sends TCP packets with wrong checksum :(( */
-+              //return NF_ACCEPT;
++              DEBUGP("%p: NATing GRE PPTP\n", conntrack);
++              min = 1;
++              range_size = 0xffff;
++
++      } else {
++              min = ntohl(range->min.gre.key);
++              range_size = ntohl(range->max.gre.key) - min + 1;
 +      }
 +
-+      if (tcph->fin || tcph->rst) {
-+              DEBUGP("RST/FIN received, timeouting GRE\n");
-+              /* can't do this after real newnat */
-+              info->cstate = PPTP_CALL_NONE;
++      DEBUGP("min = %u, range_size = %u\n", min, range_size); 
 +
-+              /* untrack this call id, unexpect GRE packets */
-+              pptp_timeout_related(ct);
++      for (i = 0; i < range_size; i++, key++) {
++              *keyptr = htonl(min + key % range_size);
++              if (!ip_nat_used_tuple(tuple, conntrack))
++                      return 1;
 +      }
 +
++      DEBUGP("%p: no NAT mapping\n", conntrack);
 +
-+      pptph = (struct pptp_pkt_hdr *) ((void *) tcph + tcph->doff * 4);
-+      datalimit = (void *) pptph + datalen;
++      return 0;
++}
 +
-+      /* not a full pptp packet header? */
-+      if ((void *) pptph+sizeof(*pptph) >= datalimit) {
-+              DEBUGP("no full PPTP header, can't track\n");
-+              return NF_ACCEPT;
-+      }
-+      
-+      /* if it's not a control message we can't do anything with it */
-+        if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
-+          ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
-+              DEBUGP("not a control packet\n");
-+              return NF_ACCEPT;
++/* manipulate a GRE packet according to maniptype */
++static void 
++gre_manip_pkt(struct iphdr *iph, size_t len, 
++            const struct ip_conntrack_manip *manip,
++            enum ip_nat_manip_type maniptype)
++{
++      struct gre_hdr *greh = (struct gre_hdr *)((u_int32_t *)iph+iph->ihl);
++      struct gre_hdr_pptp *pgreh = (struct gre_hdr_pptp *) greh;
++
++      /* we only have destination manip of a packet, since 'source key' 
++       * is not present in the packet itself */
++      if (maniptype == IP_NAT_MANIP_DST) {
++              /* key manipulation is always dest */
++              switch (greh->version) {
++              case 0:
++                      if (!greh->key) {
++                              DEBUGP("can't nat GRE w/o key\n");
++                              break;
++                      }
++                      if (greh->csum) {
++                              /* FIXME: Never tested this code... */
++                              *(gre_csum(greh)) = 
++                                      ip_nat_cheat_check(~*(gre_key(greh)),
++                                                      manip->u.gre.key,
++                                                      *(gre_csum(greh)));
++                      }
++                      *(gre_key(greh)) = manip->u.gre.key;
++                      break;
++              case GRE_VERSION_PPTP:
++                      DEBUGP("call_id -> 0x%04x\n", 
++                              ntohl(manip->u.gre.key));
++                      pgreh->call_id = htons(ntohl(manip->u.gre.key));
++                      break;
++              default:
++                      DEBUGP("can't nat unknown GRE version\n");
++                      break;
++              }
 +      }
++}
 +
-+      oldsstate = info->sstate;
-+      oldcstate = info->cstate;
++/* print out a nat tuple */
++static unsigned int 
++gre_print(char *buffer, 
++        const struct ip_conntrack_tuple *match,
++        const struct ip_conntrack_tuple *mask)
++{
++      unsigned int len = 0;
 +
-+      LOCK_BH(&ip_pptp_lock);
++      if (mask->src.u.gre.key)
++              len += sprintf(buffer + len, "srckey=0x%x ", 
++                              ntohl(match->src.u.gre.key));
 +
-+      /* FIXME: We just blindly assume that the control connection is always
-+       * established from PNS->PAC.  However, RFC makes no guarantee */
-+      if (dir == IP_CT_DIR_ORIGINAL)
-+              /* client -> server (PNS -> PAC) */
-+              ret = pptp_outbound_pkt(tcph, pptph, datalen, ct, ctinfo);
-+      else
-+              /* server -> client (PAC -> PNS) */
-+              ret = pptp_inbound_pkt(tcph, pptph, datalen, ct, ctinfo);
-+      DEBUGP("sstate: %d->%d, cstate: %d->%d\n",
-+              oldsstate, info->sstate, oldcstate, info->cstate);
-+      UNLOCK_BH(&ip_pptp_lock);
++      if (mask->dst.u.gre.key)
++              len += sprintf(buffer + len, "dstkey=0x%x ",
++                              ntohl(match->src.u.gre.key));
 +
-+      return ret;
++      return len;
 +}
 +
-+/* control protocol helper */
-+static struct ip_conntrack_helper pptp = { 
-+      .list = { NULL, NULL },
-+      .name = "pptp", 
-+      .flags = IP_CT_HELPER_F_REUSE_EXPECT,
-+      .me = THIS_MODULE,
-+      .max_expected = 2,
-+      .timeout = 0,
-+      .tuple = { .src = { .ip = 0, 
-+                          .u = { .tcp = { .port =  
-+                                  __constant_htons(PPTP_CONTROL_PORT) } } 
-+                        }, 
-+                 .dst = { .ip = 0, 
-+                          .u = { .all = 0 },
-+                          .protonum = IPPROTO_TCP
-+                        } 
-+               },
-+      .mask = { .src = { .ip = 0, 
-+                         .u = { .tcp = { .port = 0xffff } } 
-+                       }, 
-+                .dst = { .ip = 0, 
-+                         .u = { .all = 0 },
-+                         .protonum = 0xffff 
-+                       } 
-+              },
-+      .help = conntrack_pptp_help
-+};
++/* print a range of keys */
++static unsigned int 
++gre_print_range(char *buffer, const struct ip_nat_range *range)
++{
++      if (range->min.gre.key != 0 
++          || range->max.gre.key != 0xFFFF) {
++              if (range->min.gre.key == range->max.gre.key)
++                      return sprintf(buffer, "key 0x%x ",
++                                      ntohl(range->min.gre.key));
++              else
++                      return sprintf(buffer, "keys 0x%u-0x%u ",
++                                      ntohl(range->min.gre.key),
++                                      ntohl(range->max.gre.key));
++      } else
++              return 0;
++}
 +
-+/* ip_conntrack_pptp initialization */
++/* nat helper struct */
++static struct ip_nat_protocol gre = 
++      { { NULL, NULL }, "GRE", IPPROTO_GRE,
++        gre_manip_pkt,
++        gre_in_range,
++        gre_unique_tuple,
++        gre_print,
++        gre_print_range 
++      };
++                                
 +static int __init init(void)
 +{
-+      int retcode;
-+
-+      DEBUGP(__FILE__ ": registering helper\n");
-+      if ((retcode = ip_conntrack_helper_register(&pptp))) {
-+                printk(KERN_ERR "Unable to register conntrack application "
-+                              "helper for pptp: %d\n", retcode);
-+              return -EIO;
-+      }
++        if (ip_nat_protocol_register(&gre))
++                return -EIO;
 +
-+      printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION);
-+      return 0;
++        return 0;
 +}
 +
 +static void __exit fini(void)
 +{
-+      ip_conntrack_helper_unregister(&pptp);
-+      printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION);
++        ip_nat_protocol_unregister(&gre);
 +}
 +
 +module_init(init);
 +module_exit(fini);
+diff -urN linux.old/net/ipv4/netfilter/Makefile linux.dev/net/ipv4/netfilter/Makefile
+--- linux.old/net/ipv4/netfilter/Makefile      2006-12-06 00:31:46.000000000 +0100
++++ linux.dev/net/ipv4/netfilter/Makefile      2006-12-06 00:34:27.000000000 +0100
+@@ -36,23 +36,32 @@
+ ifdef CONFIG_IP_NF_AMANDA
+       export-objs += ip_conntrack_amanda.o
+ endif
+-
+ obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o
+ obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o
+ ifdef CONFIG_IP_NF_FTP
+       export-objs += ip_conntrack_ftp.o
+ endif
+-
+ obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o
+ ifdef CONFIG_IP_NF_IRC
+       export-objs += ip_conntrack_irc.o
+ endif
++obj-$(CONFIG_IP_NF_CT_PROTO_GRE) += ip_conntrack_proto_gre.o
++ifdef CONFIG_IP_NF_CT_PROTO_GRE
++      export-objs += ip_conntrack_proto_gre.o
++endif
++obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o
++ifdef CONFIG_IP_NF_NAT_PPTP
++      export-objs += ip_conntrack_pptp.o
++endif
 +
-+EXPORT_SYMBOL(ip_pptp_lock);
+ # NAT helpers 
+ obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o
+ obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o
+ obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o
+ obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o
++obj-$(CONFIG_IP_NF_NAT_PROTO_GRE) += ip_nat_proto_gre.o
++obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o
+ # generic IP tables 
+ obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o