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