&& 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.c linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_pptp.c
---- linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_pptp.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_pptp.c 2005-04-02 19:13:21.000000000 +0200
-@@ -0,0 +1,633 @@
+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 @@
+/*
-+ * ip_conntrack_pptp.c - Version 1.9
++ * ip_conntrack_proto_gre.c - Version 1.2
+ *
-+ * 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
++ * Connection tracking protocol helper module for GRE.
+ *
-+ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org>
++ * GRE is a generic encapsulation protocol, which is generally not very
++ * suited for NAT, as it has no protocol-specific part as port numbers.
+ *
-+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
++ * It has an optional key field, which may help us distinguishing two
++ * connections between the same two hosts.
+ *
-+ * Limitations:
-+ * - We blindly assume that control connections are always
-+ * established in PNS->PAC direction. This is a violation
-+ * of RFFC2673
++ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784
+ *
-+ * TODO: - finish support for multiple calls within one session
-+ * (needs expect reservations in newnat)
-+ * - testing of incoming PPTP calls
++ * 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.
+ *
-+ * 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
++ * 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/)
+ *
+ */
+
+#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 <net/checksum.h>
-+#include <net/tcp.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>
+
-+#define IP_CT_PPTP_VERSION "1.9"
-+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
-+MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP");
++MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE");
+
-+DECLARE_LOCK(ip_pptp_lock);
++/* shamelessly stolen from ip_conntrack_proto_udp.c */
++#define GRE_TIMEOUT (30*HZ)
++#define GRE_STREAM_TIMEOUT (180*HZ)
+
+#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)
++#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(format, args...)
++#define DEBUGP(x, args...)
++#define DUMP_TUPLE_GRE(x)
+#endif
++
++/* GRE KEYMAP HANDLING FUNCTIONS */
++static LIST_HEAD(gre_keymap_list);
+
-+#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 inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km,
++ const struct ip_conntrack_tuple *t)
+{
-+ struct ip_conntrack *master;
-+ struct ip_conntrack_expect *exp;
-+
-+ 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;
++ 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));
++}
+
-+ master = master_ct(ct);
-+ if (!master) {
-+ DEBUGP(" no master!!!\n");
-+ return 0;
-+ }
++/* 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;
+
-+ exp = ct->master;
-+ if (!exp) {
-+ DEBUGP("no expectation!!\n");
++ 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;
+ }
+
-+ 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;
-+
-+ 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);
-+ }
-+ }
-+ }
++ key = km->tuple.src.u.gre.key;
++ READ_UNLOCK(&ip_ct_gre_lock);
+
-+ return 0;
++ return key;
+}
+
-+/* timeout GRE data connections */
-+static int pptp_timeout_related(struct ip_conntrack *ct)
++/* 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 list_head *cur_item, *next;
-+ struct ip_conntrack_expect *exp;
++ struct ip_ct_gre_keymap *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);
++ km = kmalloc(sizeof(*km), GFP_ATOMIC);
++ if (!km)
++ return -1;
+
-+ ip_ct_gre_keymap_destroy(exp);
-+ if (!exp->sibling) {
-+ ip_conntrack_unexpect_related(exp);
-+ continue;
-+ }
++ /* initializing list head should be sufficient */
++ memset(km, 0, sizeof(*km));
+
-+ 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);
-+ }
++ memcpy(&km->tuple, t, sizeof(*t));
++
++ if (!reply)
++ exp->proto.gre.keymap_orig = km;
++ else
++ exp->proto.gre.keymap_reply = km;
++
++ DEBUGP("adding new entry %p: ", km);
++ DUMP_TUPLE_GRE(&km->tuple);
++
++ WRITE_LOCK(&ip_ct_gre_lock);
++ list_append(&gre_keymap_list, km);
++ WRITE_UNLOCK(&ip_ct_gre_lock);
+
+ return 0;
+}
+
-+/* 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)
++/* 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)
+{
-+ struct ip_conntrack_expect exp;
-+ struct ip_conntrack_tuple inv_tuple;
-+
-+ 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;
++ DEBUGP("changing entry %p to: ", km);
++ DUMP_TUPLE_GRE(t);
+
-+ 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;
++ WRITE_LOCK(&ip_ct_gre_lock);
++ memcpy(&km->tuple, t, sizeof(km->tuple));
++ WRITE_UNLOCK(&ip_ct_gre_lock);
++}
+
-+ 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;
++/* 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 (ip_conntrack_expect_related(master, &exp) != 0) {
-+ ip_ct_gre_keymap_destroy(&exp);
-+ DEBUGP("cannot expect_related()\n");
-+ 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;
+ }
++ 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));
+
-+ 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;
-+ }
++/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
+
-+ return 0;
++/* 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;
+}
+
-+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)
++/* gre hdr info to tuple */
++static int gre_pkt_to_tuple(const void *datah, size_t datalen,
++ struct ip_conntrack_tuple *tuple)
+{
-+ 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;
-+
-+ ctlh = (struct PptpControlHeader *)
-+ ((char *) pptph + sizeof(struct pptp_pkt_hdr));
-+ pptpReq.rawreq = (void *)
-+ ((char *) ctlh + sizeof(struct PptpControlHeader));
++ struct gre_hdr *grehdr = (struct gre_hdr *) datah;
++ struct gre_hdr_pptp *pgrehdr = (struct gre_hdr_pptp *) datah;
++ u_int32_t srckey;
+
-+ msg = ntohs(ctlh->messageType);
-+ DEBUGP("inbound control message %s\n", strMName[msg]);
++ /* core guarantees 8 protocol bytes, no need for size check */
+
-+ 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]);
++ switch (grehdr->version) {
++ case GRE_VERSION_1701:
++ if (!grehdr->key) {
++ DEBUGP("Can't track GRE without key\n");
++ return 0;
++ }
++ tuple->dst.u.gre.key = *(gre_key(grehdr));
+ break;
-+ }
-+ if (pptpReq.srep->resultCode == PPTP_START_OK)
-+ info->sstate = PPTP_SESSION_CONFIRMED;
-+ else
-+ info->sstate = PPTP_SESSION_ERROR;
-+ break;
+
-+ 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]);
++ case GRE_VERSION_PPTP:
++ if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) {
++ DEBUGP("GRE_VERSION_PPTP but unknown proto\n");
++ return 0;
++ }
++ tuple->dst.u.gre.key = htonl(ntohs(pgrehdr->call_id));
+ break;
-+ }
-+ if (pptpReq.strep->resultCode == PPTP_STOP_OK)
-+ info->sstate = PPTP_SESSION_NONE;
-+ else
-+ info->sstate = PPTP_SESSION_ERROR;
-+ break;
+
-+ 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;
-+ }
++ default:
++ printk(KERN_WARNING "unknown GRE version %hu\n",
++ grehdr->version);
++ return 0;
++ }
+
-+ cid = &pptpReq.ocack->callID;
-+ pcid = &pptpReq.ocack->peersCallID;
++ srckey = gre_keymap_lookup(tuple);
+
-+ 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;
-+ }
++#if 0
++ DEBUGP("found src key %x for tuple ", ntohl(srckey));
++ DUMP_TUPLE_GRE(tuple);
++#endif
++ tuple->src.u.gre.key = srckey;
+
-+ DEBUGP("%s, CID=%X, PCID=%X\n", strMName[msg],
-+ ntohs(*cid), ntohs(*pcid));
-+
-+ info->cstate = PPTP_CALL_OUT_CONF;
++ return 1;
++}
+
-+ 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 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));
++}
+
-+ 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;
++/* 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_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;
-+ }
++/* 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;
++}
+
-+ pcid = &pptpReq.iccon->peersCallID;
-+ cid = &info->pac_call_id;
++/* 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);
+
-+ if (info->pns_call_id != ntohs(*pcid)) {
-+ DEBUGP("%s for unknown CallID %u\n",
-+ strMName[msg], ntohs(*cid));
-+ break;
-+ }
-+
-+ DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
-+ info->cstate = PPTP_CALL_IN_CONF;
-+
-+ /* 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");
-+
-+ break;
-+
-+ 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;
-+
-+ /* untrack this call id, unexpect GRE packets */
-+ pptp_timeout_related(ct);
-+ break;
-+
-+ case PPTP_WAN_ERROR_NOTIFY:
-+ 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);
-+ break;
-+ }
-+
-+ return NF_ACCEPT;
++ /* 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;
+}
+
-+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)
++/* 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 PptpControlHeader *ctlh;
-+ union pptp_ctrl_union pptpReq;
-+ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
-+ u_int16_t msg, *cid, *pcid;
-+
-+ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
-+ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
-+
-+ msg = ntohs(ctlh->messageType);
-+ DEBUGP("outbound control message %s\n", strMName[msg]);
-+
-+ 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;
++ struct ip_conntrack_expect *master = ct->master;
+
-+ 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;
++ DEBUGP(" entering\n");
+
-+ 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;
++ if (!master) {
++ DEBUGP("no master exp for ct %p\n", ct);
++ return;
+ }
+
-+ return NF_ACCEPT;
++ 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 };
+
-+/* 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)
-+
++/* ip_conntrack_proto_gre initialization */
++static int __init init(void)
+{
-+ 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 oldsstate, oldcstate;
-+ int ret;
-+
-+ /* 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;
-+ }
-+
-+ /* 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;
-+ }
-+
-+ if (tcph->fin || tcph->rst) {
-+ DEBUGP("RST/FIN received, timeouting GRE\n");
-+ /* can't do this after real newnat */
-+ info->cstate = PPTP_CALL_NONE;
++ int retcode;
+
-+ /* untrack this call id, unexpect GRE packets */
-+ pptp_timeout_related(ct);
++ 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;
++}
+
-+ pptph = (struct pptp_pkt_hdr *) ((void *) tcph + tcph->doff * 4);
-+ datalimit = (void *) pptph + datalen;
++static void __exit fini(void)
++{
++ struct list_head *pos, *n;
+
-+ /* 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;
++ /* 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);
+
-+ oldsstate = info->sstate;
-+ oldcstate = info->cstate;
-+
-+ 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 ret;
++ ip_conntrack_protocol_unregister(&gre);
+}
+
-+/* 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
-+};
-+
-+/* ip_conntrack_pptp initialization */
-+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;
-+ }
-+
-+ printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION);
-+ return 0;
-+}
-+
-+static void __exit fini(void)
-+{
-+ ip_conntrack_helper_unregister(&pptp);
-+ printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION);
-+}
++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);
-+
-+EXPORT_SYMBOL(ip_pptp_lock);
-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-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_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.
-+ *
-+ * 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.
++ * ip_nat_pptp.c - Version 1.5
+ *
-+ * Documentation about PPTP can be found in RFC 2637
++ * 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/types.h>
-+#include <linux/timer.h>
-+#include <linux/netfilter.h>
+#include <linux/ip.h>
-+#include <linux/in.h>
-+#include <linux/list.h>
-+
-+#include <linux/netfilter_ipv4/lockhelp.h>
-+
-+DECLARE_RWLOCK(ip_ct_gre_lock);
-+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_ct_gre_lock)
-+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_ct_gre_lock)
-+
-+#include <linux/netfilter_ipv4/listhelp.h>
-+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
++#include <linux/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_core.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 connection tracking protocol helper for GRE");
++MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
+
-+/* shamelessly stolen from ip_conntrack_proto_udp.c */
-+#define GRE_TIMEOUT (30*HZ)
-+#define GRE_STREAM_TIMEOUT (180*HZ)
+
+#if 0
++#include "ip_conntrack_pptp_priv.h"
+#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))
++ ": " 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));
-+}
-+
-+/* look up the source key for a given tuple */
-+static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t)
-+{
-+ struct ip_ct_gre_keymap *km;
-+ u_int32_t key;
-+
-+ READ_LOCK(&ip_ct_gre_lock);
-+ km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
-+ struct ip_ct_gre_keymap *, t);
-+ if (!km) {
-+ READ_UNLOCK(&ip_ct_gre_lock);
-+ return 0;
-+ }
-+
-+ key = km->tuple.src.u.gre.key;
-+ READ_UNLOCK(&ip_ct_gre_lock);
-+
-+ return key;
-+}
+
-+/* add a single keymap entry, associate with specified expect */
-+int ip_ct_gre_keymap_add(struct ip_conntrack_expect *exp,
-+ struct ip_conntrack_tuple *t, int reply)
++static unsigned int
++pptp_nat_expected(struct sk_buff **pskb,
++ unsigned int hooknum,
++ struct ip_conntrack *ct,
++ struct ip_nat_info *info)
+{
-+ struct ip_ct_gre_keymap *km;
-+
-+ km = kmalloc(sizeof(*km), GFP_ATOMIC);
-+ if (!km)
-+ return -1;
-+
-+ /* initializing list head should be sufficient */
-+ memset(km, 0, sizeof(*km));
-+
-+ memcpy(&km->tuple, t, sizeof(*t));
-+
-+ if (!reply)
-+ exp->proto.gre.keymap_orig = km;
-+ else
-+ exp->proto.gre.keymap_reply = km;
++ 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("adding new entry %p: ", km);
-+ DUMP_TUPLE_GRE(&km->tuple);
++ IP_NF_ASSERT(info);
++ IP_NF_ASSERT(master);
++ IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
+
-+ WRITE_LOCK(&ip_ct_gre_lock);
-+ list_append(&gre_keymap_list, km);
-+ WRITE_UNLOCK(&ip_ct_gre_lock);
++ DEBUGP("we have a connection!\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;
+
-+/* 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);
++ /* 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);
++ }
++ }
++
++ 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;
+
-+ 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)
++/* 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)
++
+{
-+ 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);
-+}
++ 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;
+
-+/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
++ u_int16_t msg, *cid = NULL, new_callid;
+
-+/* 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;
++ /* FIXME: size checks !!! */
++ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
++ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
+
-+ return 1;
-+}
++ 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 */
+
-+/* gre hdr info to tuple */
-+static int gre_pkt_to_tuple(const void *datah, size_t datalen,
-+ struct ip_conntrack_tuple *tuple)
-+{
-+ struct gre_hdr *grehdr = (struct gre_hdr *) datah;
-+ struct gre_hdr_pptp *pgrehdr = (struct gre_hdr_pptp *) datah;
-+ u_int32_t srckey;
++ /* save original call ID in nat_info */
++ nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id;
+
-+ /* core guarantees 8 protocol bytes, no need for size check */
++ /* 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;
+
-+ 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));
++ /* save new call ID in ct info */
++ ct_pptp_info->pns_call_id = ntohs(new_callid);
+ 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_IN_CALL_REPLY:
++ cid = &pptpReq.icreq->callID;
++ break;
++ case PPTP_CALL_CLEAR_REQUEST:
++ cid = &pptpReq.clrreq->callID;
+ break;
-+
+ default:
-+ printk(KERN_WARNING "unknown GRE version %hu\n",
-+ grehdr->version);
-+ return 0;
-+ }
-+
-+ srckey = gre_keymap_lookup(tuple);
++ DEBUGP("unknown outbound packet 0x%04x:%s\n", msg,
++ (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
++ /* fall through */
+
-+#if 0
-+ DEBUGP("found src key %x for tuple ", ntohl(srckey));
-+ DUMP_TUPLE_GRE(tuple);
-+#endif
-+ tuple->src.u.gre.key = srckey;
++ 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;
++ }
+
-+ return 1;
-+}
++ IP_NF_ASSERT(cid);
+
-+/* 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));
-+}
++ DEBUGP("altering call id from 0x%04x to 0x%04x\n",
++ ntohs(*cid), ntohs(new_callid));
+
-+/* 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));
-+}
++ /* mangle packet */
++ ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)cid - (void *)pptph,
++ sizeof(new_callid), (char *)&new_callid,
++ sizeof(new_callid));
+
-+/* Returns verdict for packet, and may modify conntrack */
-+static int gre_packet(struct ip_conntrack *ct,
-+ struct iphdr *iph, size_t len,
-+ enum ip_conntrack_info conntrackinfo)
-+{
-+ /* If we've seen traffic both ways, this is a GRE connection.
-+ * Extend timeout. */
-+ if (ct->status & IPS_SEEN_REPLY) {
-+ ip_ct_refresh(ct, ct->proto.gre.stream_timeout);
-+ /* Also, more likely to be important, and not a probe. */
-+ set_bit(IPS_ASSURED_BIT, &ct->status);
-+ } else
-+ ip_ct_refresh(ct, ct->proto.gre.timeout);
-+
+ return NF_ACCEPT;
+}
+
-+/* Called when a new connection for this protocol found. */
-+static int gre_new(struct ip_conntrack *ct,
-+ struct iphdr *iph, size_t len)
-+{
-+ DEBUGP(": ");
-+ DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
-+
-+ /* initialize to sane value. Ideally a conntrack helper
-+ * (e.g. in case of pptp) is increasing them */
-+ ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT;
-+ ct->proto.gre.timeout = GRE_TIMEOUT;
++/* 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)
++{
++ 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);
+
-+ return 1;
-+}
++ 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;
+
-+/* 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;
++ u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL;
++ u_int32_t old_dst_ip;
+
-+ DEBUGP(" entering\n");
++ struct ip_conntrack_tuple t, inv_t;
++ struct ip_conntrack_tuple *orig_t, *reply_t;
+
-+ if (!master) {
-+ DEBUGP("no master exp for ct %p\n", ct);
-+ return;
-+ }
++ /* FIXME: size checks !!! */
++ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
++ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
+
-+ ip_ct_gre_keymap_destroy(master);
-+}
++ new_pcid = htons(nat_pptp_info->pns_call_id);
+
-+/* 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 };
++ 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);
+
-+/* ip_conntrack_proto_gre initialization */
++ /* save original PAC call ID in nat_info */
++ nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
++
++ /* 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);
++ }
++
++ if (!ip_conntrack_change_expect(oldexp, &t)) {
++ DEBUGP("successfully changed expect\n");
++ } else {
++ DEBUGP("can't change expect\n");
++ }
++ ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig, &t);
++ ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, &inv_t);
++ break;
++ case PPTP_IN_CALL_CONNECT:
++ 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 (!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;
++
++ 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;
++
++ int dir;
++
++ DEBUGP("entering\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;
++ }
++
++ /* 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;
++ }
++
++ pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4);
++
++ /* 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;
++ }
++
++ LOCK_BH(&ip_pptp_lock);
++
++ 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)
+{
-+ int retcode;
++ 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 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);
++}
++
++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 @@
++/*
++ * 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/)
++ *
++ */
++
++#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>
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
++MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE");
++
++#if 0
++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
++ ": " format, ## args)
++#else
++#define DEBUGP(x, args...)
++#endif
++
++/* 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 ntohl(key) >= ntohl(min->gre.key)
++ && ntohl(key) <= ntohl(max->gre.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)
++{
++ u_int32_t min, i, range_size;
++ u_int32_t key = 0, *keyptr;
+
-+ if ((retcode = ip_conntrack_protocol_register(&gre))) {
-+ printk(KERN_ERR "Unable to register conntrack protocol "
-+ "helper for gre: %d\n", retcode);
-+ return -EIO;
++ if (maniptype == IP_NAT_MANIP_SRC)
++ keyptr = &tuple->src.u.gre.key;
++ else
++ keyptr = &tuple->dst.u.gre.key;
++
++ if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
++
++ 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;
++ }
++
++ DEBUGP("min = %u, range_size = %u\n", min, range_size);
++
++ 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);
++
+ return 0;
+}
+
-+static void __exit fini(void)
++/* 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 list_head *pos, *n;
++ struct gre_hdr *greh = (struct gre_hdr *)((u_int32_t *)iph+iph->ihl);
++ struct gre_hdr_pptp *pgreh = (struct gre_hdr_pptp *) greh;
+
-+ /* 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);
++ /* 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;
++ }
+ }
-+ WRITE_UNLOCK(&ip_ct_gre_lock);
++}
+
-+ ip_conntrack_protocol_unregister(&gre);
++/* 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;
++
++ if (mask->src.u.gre.key)
++ len += sprintf(buffer + len, "srckey=0x%x ",
++ ntohl(match->src.u.gre.key));
++
++ if (mask->dst.u.gre.key)
++ len += sprintf(buffer + len, "dstkey=0x%x ",
++ ntohl(match->src.u.gre.key));
++
++ return len;
+}
+
-+EXPORT_SYMBOL(ip_ct_gre_keymap_add);
-+EXPORT_SYMBOL(ip_ct_gre_keymap_change);
-+EXPORT_SYMBOL(ip_ct_gre_keymap_destroy);
++/* 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;
++}
++
++/* 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)
++{
++ if (ip_nat_protocol_register(&gre))
++ return -EIO;
++
++ return 0;
++}
++
++static void __exit fini(void)
++{
++ ip_nat_protocol_unregister(&gre);
++}
+
+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. */
+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
++
-@@ -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
+ # 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
- /* 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 @@
+ # 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 @@
+/*
-+ * ip_nat_pptp.c - Version 1.5
++ * ip_conntrack_pptp.c - Version 1.9
+ *
-+ * NAT support for PPTP (Point to Point Tunneling Protocol).
++ * 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
+ *
+ * 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)
++ * 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
+ *
-+ * 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 <linux/tcp.h>
++#include <net/checksum.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/lockhelp.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"
++#define IP_CT_PPTP_VERSION "1.9"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
-+MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
++MODULE_DESCRIPTION("Netfilter connection tracking 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
+
-+static unsigned int
-+pptp_nat_expected(struct sk_buff **pskb,
-+ unsigned int hooknum,
-+ struct ip_conntrack *ct,
-+ struct ip_nat_info *info)
++#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)
+{
-+ 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;
++ struct ip_conntrack *master;
++ struct ip_conntrack_expect *exp;
+
-+ IP_NF_ASSERT(info);
-+ IP_NF_ASSERT(master);
-+ IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
++ 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;
+
-+ DEBUGP("we have a connection!\n");
++ master = master_ct(ct);
++ if (!master) {
++ DEBUGP(" no master!!!\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;
++ exp = ct->master;
++ if (!exp) {
++ DEBUGP("no expectation!!\n");
++ return 0;
++ }
+
-+ /* 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);
-+ }
++ 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 {
-+ 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);
++ /* 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);
++ }
+ }
+ }
+
-+ 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);
++ return 0;
++}
+
-+ UNLOCK_BH(&ip_pptp_lock);
++/* 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;
+
-+ return ret;
++ /* 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);
+
-+}
++ ip_ct_gre_keymap_destroy(exp);
++ if (!exp->sibling) {
++ ip_conntrack_unexpect_related(exp);
++ continue;
++ }
+
-+/* 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)
++ 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 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)
+{
-+ 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 ip_conntrack_expect exp;
++ struct ip_conntrack_tuple inv_tuple;
+
-+ 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;
++ 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;
+
-+ u_int16_t msg, *cid = NULL, new_callid;
++ 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;
+
-+ /* FIXME: size checks !!! */
-+ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
-+ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
++ exp.help.exp_pptp_info.pac_call_id = ntohs(callid);
++ exp.help.exp_pptp_info.pns_call_id = ntohs(peer_callid);
+
-+ new_callid = htons(ct_pptp_info->pns_call_id);
++ DEBUGP("calling expect_related ");
++ DUMP_TUPLE_RAW(&exp.tuple);
+
-+ 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;
++ /* Add GRE keymap entries */
++ if (ip_ct_gre_keymap_add(&exp, &exp.tuple, 0) != 0)
++ return 1;
+
-+ /* 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;
++ 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 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 */
++ /* 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));
+
-+ 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;
++ 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;
+ }
+
-+ 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;
++ return 0;
+}
+
-+/* inbound packets == from PAC to PNS */
-+static inline unsigned int
-+pptp_inbound_pkt(struct sk_buff **pskb,
++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 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;
++ u_int32_t seq;
+
-+ /* FIXME: size checks !!! */
-+ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
-+ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
++ ctlh = (struct PptpControlHeader *)
++ ((char *) pptph + sizeof(struct pptp_pkt_hdr));
++ pptpReq.rawreq = (void *)
++ ((char *) ctlh + sizeof(struct PptpControlHeader));
+
-+ new_pcid = htons(nat_pptp_info->pns_call_id);
++ msg = ntohs(ctlh->messageType);
++ DEBUGP("inbound control message %s\n", strMName[msg]);
+
-+ 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");
++ 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;
+ }
-+ 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;
++ if (pptpReq.srep->resultCode == PPTP_START_OK)
++ info->sstate = PPTP_SESSION_CONFIRMED;
++ else
++ info->sstate = PPTP_SESSION_ERROR;
++ 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_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;
+
-+ if (!ip_conntrack_change_expect(oldexp, &t)) {
-+ DEBUGP("successfully changed expect\n");
-+ } else {
-+ DEBUGP("can't change expect\n");
++ 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;
+ }
-+ ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig, &t);
-+ ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, &inv_t);
-+ break;
-+ case PPTP_IN_CALL_CONNECT:
-+ pcid = &pptpReq.iccon->peersCallID;
-+ if (!oldexp)
++ if (info->cstate != PPTP_CALL_OUT_REQ &&
++ info->cstate != PPTP_CALL_OUT_CONF) {
++ DEBUGP("%s without OUTCALL_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.ocack->resultCode != PPTP_OUTCALL_CONNECT) {
++ info->cstate = PPTP_CALL_NONE;
++ break;
+ }
-+ 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;
-+
-+ 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;
++ cid = &pptpReq.ocack->callID;
++ pcid = &pptpReq.ocack->peersCallID;
+
-+ int dir;
++ 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;
++ }
+
-+ DEBUGP("entering\n");
++ DEBUGP("%s, CID=%X, PCID=%X\n", strMName[msg],
++ ntohs(*cid), ntohs(*pcid));
++
++ info->cstate = PPTP_CALL_OUT_CONF;
+
-+ /* 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;
-+ }
++ 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;
+
-+ /* 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_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;
+
-+ pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4);
++ 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;
++ }
+
-+ /* 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;
-+ }
++ pcid = &pptpReq.iccon->peersCallID;
++ cid = &info->pac_call_id;
+
-+ LOCK_BH(&ip_pptp_lock);
++ if (info->pns_call_id != ntohs(*pcid)) {
++ DEBUGP("%s for unknown CallID %u\n",
++ strMName[msg], ntohs(*cid));
++ 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);
-+ }
++ DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
++ info->cstate = PPTP_CALL_IN_CONF;
+
-+ UNLOCK_BH(&ip_pptp_lock);
++ /* 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");
+
-+ return NF_ACCEPT;
-+}
++ break;
+
-+/* 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
-+ }
-+ },
++ 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;
+
-+ .mask = { .src = { .ip = 0,
-+ .u = { .tcp = { .port = 0xFFFF } }
-+ },
-+ .dst = { .ip = 0,
-+ .u = { .all = 0 },
-+ .protonum = 0xFFFF
-+ }
-+ },
-+ .help = tcp_help,
-+ .expect = pptp_nat_expected
-+};
++ /* untrack this call id, unexpect GRE packets */
++ pptp_timeout_related(ct);
++ break;
+
-+
-+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;
++ case PPTP_WAN_ERROR_NOTIFY:
++ 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);
++ break;
+ }
+
-+ printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION);
-+ return 0;
-+}
++ return NF_ACCEPT;
+
-+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);
+}
+
-+module_init(init);
-+module_exit(fini);
-Binary files linux-2.4.29-old/net/ipv4/netfilter/.ip_nat_pptp.c.swp and linux-2.4.29-dev/net/ipv4/netfilter/.ip_nat_pptp.c.swp differ
-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 @@
-+/*
-+ * 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/)
-+ *
-+ */
-+
-+#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>
++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 PptpControlHeader *ctlh;
++ union pptp_ctrl_union pptpReq;
++ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
++ u_int16_t msg, *cid, *pcid;
+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
-+MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE");
++ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
++ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
+
-+#if 0
-+#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
-+ ": " format, ## args)
-+#else
-+#define DEBUGP(x, args...)
-+#endif
++ msg = ntohs(ctlh->messageType);
++ DEBUGP("outbound control message %s\n", strMName[msg]);
+
-+/* 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;
++ 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 (maniptype == IP_NAT_MANIP_SRC)
-+ key = tuple->src.u.gre.key;
-+ else
-+ key = tuple->dst.u.gre.key;
++ 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;
+
-+ return ntohl(key) >= ntohl(min->gre.key)
-+ && ntohl(key) <= ntohl(max->gre.key);
++ 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;
++ }
++
++ return NF_ACCEPT;
+}
+
-+/* generate unique tuple ... */
++
++/* track caller id inside control connection, call expect_related */
+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)
-+{
-+ u_int32_t min, i, range_size;
-+ u_int32_t key = 0, *keyptr;
++conntrack_pptp_help(const struct iphdr *iph, size_t len,
++ struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
+
-+ if (maniptype == IP_NAT_MANIP_SRC)
-+ keyptr = &tuple->src.u.gre.key;
-+ else
-+ keyptr = &tuple->dst.u.gre.key;
++{
++ 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;
+
-+ if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
++ int oldsstate, oldcstate;
++ int ret;
+
-+ DEBUGP("%p: NATing GRE PPTP\n", conntrack);
-+ min = 1;
-+ range_size = 0xffff;
++ /* 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;
++ }
+
-+ } else {
-+ min = ntohl(range->min.gre.key);
-+ range_size = ntohl(range->max.gre.key) - min + 1;
++ /* 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("min = %u, range_size = %u\n", min, range_size);
++ if (tcph->fin || tcph->rst) {
++ DEBUGP("RST/FIN received, timeouting GRE\n");
++ /* can't do this after real newnat */
++ info->cstate = PPTP_CALL_NONE;
+
-+ for (i = 0; i < range_size; i++, key++) {
-+ *keyptr = htonl(min + key % range_size);
-+ if (!ip_nat_used_tuple(tuple, conntrack))
-+ return 1;
++ /* untrack this call id, unexpect GRE packets */
++ pptp_timeout_related(ct);
+ }
+
-+ DEBUGP("%p: no NAT mapping\n", conntrack);
-+
-+ return 0;
-+}
+
-+/* 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;
++ pptph = (struct pptp_pkt_hdr *) ((void *) tcph + tcph->doff * 4);
++ datalimit = (void *) pptph + datalen;
+
-+ /* 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;
-+ }
++ /* 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;
+ }
-+}
+
-+/* 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;
++ oldsstate = info->sstate;
++ oldcstate = info->cstate;
+
-+ if (mask->src.u.gre.key)
-+ len += sprintf(buffer + len, "srckey=0x%x ",
-+ ntohl(match->src.u.gre.key));
++ LOCK_BH(&ip_pptp_lock);
+
-+ if (mask->dst.u.gre.key)
-+ len += sprintf(buffer + len, "dstkey=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);
+
-+ return len;
++ return ret;
+}
+
-+/* 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;
-+}
++/* 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
++};
+
-+/* 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
-+ };
-+
++/* ip_conntrack_pptp initialization */
+static int __init init(void)
+{
-+ if (ip_nat_protocol_register(&gre))
-+ return -EIO;
++ int retcode;
+
-+ return 0;
++ 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_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION);
++ return 0;
+}
+
+static void __exit fini(void)
+{
-+ ip_nat_protocol_unregister(&gre);
++ 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/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
+
-
- # 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
++EXPORT_SYMBOL(ip_pptp_lock);