diff options
author | Andy Boyett <agb@openwrt.org> | 2009-09-10 10:06:26 +0000 |
---|---|---|
committer | Andy Boyett <agb@openwrt.org> | 2009-09-10 10:06:26 +0000 |
commit | 7c6ce6633008c17e08b422d8d2f45b29ecf52d99 (patch) | |
tree | 171af120c2c9839efb9c52760c2fdd8717e98be9 /target/linux/generic-2.4/patches/603-netfilter_nat_pptp.patch | |
parent | 29666ffa212a996eae5d0196e4c95e32c58dcecd (diff) | |
download | master-31e0f0ae-7c6ce6633008c17e08b422d8d2f45b29ecf52d99.tar.gz master-31e0f0ae-7c6ce6633008c17e08b422d8d2f45b29ecf52d99.tar.bz2 master-31e0f0ae-7c6ce6633008c17e08b422d8d2f45b29ecf52d99.zip |
remove nat pptp conntracking patch
Patch 603-netfilter_nat_pptp.patch appears to be the cause of the
longstanding brcm-2.4 dnat off-by-one port forwarding bug. Months of
testing builds with this patch removed have not been able to reproduce
the issue.
The 603 patch is rather large and we (the OpenWrt developers) have not
been able to isolate the cause of the bug within the patch. The number
of people have actually use pptp nat conntracking is far less than
number of people that use port forwarding and as a result we must drop
this patch to ensure proper behavior for the majority of users.
SVN-Revision: 17552
Diffstat (limited to 'target/linux/generic-2.4/patches/603-netfilter_nat_pptp.patch')
-rw-r--r-- | target/linux/generic-2.4/patches/603-netfilter_nat_pptp.patch | 2406 |
1 files changed, 0 insertions, 2406 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 deleted file mode 100644 index 7e54a7027f..0000000000 --- a/target/linux/generic-2.4/patches/603-netfilter_nat_pptp.patch +++ /dev/null @@ -1,2406 +0,0 @@ ---- a/include/linux/netfilter_ipv4/ip_conntrack.h -+++ b/include/linux/netfilter_ipv4/ip_conntrack.h -@@ -50,16 +50,19 @@ enum ip_conntrack_status { - - #include <linux/netfilter_ipv4/ip_conntrack_tcp.h> - #include <linux/netfilter_ipv4/ip_conntrack_icmp.h> -+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> - - /* per conntrack: protocol private data */ - union ip_conntrack_proto { - /* insert conntrack proto private data here */ - struct ip_ct_tcp tcp; - struct ip_ct_icmp icmp; -+ struct ip_ct_gre gre; - }; - - union ip_conntrack_expect_proto { - /* insert expect proto private data here */ -+ struct ip_ct_gre_expect gre; - }; - - /* Add protocol helper include file here */ -@@ -67,6 +70,7 @@ union ip_conntrack_expect_proto { - - #include <linux/netfilter_ipv4/ip_conntrack_ftp.h> - #include <linux/netfilter_ipv4/ip_conntrack_irc.h> -+#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> - - /* per expectation: application helper private data */ - union ip_conntrack_expect_help { -@@ -74,6 +78,7 @@ union ip_conntrack_expect_help { - struct ip_ct_amanda_expect exp_amanda_info; - struct ip_ct_ftp_expect exp_ftp_info; - struct ip_ct_irc_expect exp_irc_info; -+ struct ip_ct_pptp_expect exp_pptp_info; - - #ifdef CONFIG_IP_NF_NAT_NEEDED - union { -@@ -87,14 +92,17 @@ union ip_conntrack_help { - /* insert conntrack helper private data (master) here */ - struct ip_ct_ftp_master ct_ftp_info; - struct ip_ct_irc_master ct_irc_info; -+ struct ip_ct_pptp_master ct_pptp_info; - }; - - #ifdef CONFIG_IP_NF_NAT_NEEDED - #include <linux/netfilter_ipv4/ip_nat.h> -+#include <linux/netfilter_ipv4/ip_nat_pptp.h> - - /* per conntrack: nat application helper private data */ - union ip_conntrack_nat_help { - /* insert nat helper private data here */ -+ struct ip_nat_pptp nat_pptp_info; - }; - #endif - ---- /dev/null -+++ b/include/linux/netfilter_ipv4/ip_conntrack_pptp.h -@@ -0,0 +1,313 @@ -+/* PPTP constants and structs */ -+#ifndef _CONNTRACK_PPTP_H -+#define _CONNTRACK_PPTP_H -+ -+/* state of the control session */ -+enum pptp_ctrlsess_state { -+ PPTP_SESSION_NONE, /* no session present */ -+ PPTP_SESSION_ERROR, /* some session error */ -+ PPTP_SESSION_STOPREQ, /* stop_sess request seen */ -+ PPTP_SESSION_REQUESTED, /* start_sess request seen */ -+ PPTP_SESSION_CONFIRMED, /* session established */ -+}; -+ -+/* state of the call inside the control session */ -+enum pptp_ctrlcall_state { -+ PPTP_CALL_NONE, -+ PPTP_CALL_ERROR, -+ PPTP_CALL_OUT_REQ, -+ PPTP_CALL_OUT_CONF, -+ PPTP_CALL_IN_REQ, -+ PPTP_CALL_IN_REP, -+ PPTP_CALL_IN_CONF, -+ PPTP_CALL_CLEAR_REQ, -+}; -+ -+ -+/* conntrack private data */ -+struct ip_ct_pptp_master { -+ enum pptp_ctrlsess_state sstate; /* session state */ -+ -+ /* everything below is going to be per-expectation in newnat, -+ * since there could be more than one call within one session */ -+ enum pptp_ctrlcall_state cstate; /* call state */ -+ u_int16_t pac_call_id; /* call id of PAC, host byte order */ -+ u_int16_t pns_call_id; /* call id of PNS, host byte order */ -+}; -+ -+/* conntrack_expect private member */ -+struct ip_ct_pptp_expect { -+ enum pptp_ctrlcall_state cstate; /* call state */ -+ u_int16_t pac_call_id; /* call id of PAC */ -+ u_int16_t pns_call_id; /* call id of PNS */ -+}; -+ -+ -+#ifdef __KERNEL__ -+ -+#include <linux/netfilter_ipv4/lockhelp.h> -+DECLARE_LOCK_EXTERN(ip_pptp_lock); -+ -+#define IP_CONNTR_PPTP PPTP_CONTROL_PORT -+ -+union pptp_ctrl_union { -+ void *rawreq; -+ struct PptpStartSessionRequest *sreq; -+ struct PptpStartSessionReply *srep; -+ struct PptpStopSessionRequest *streq; -+ struct PptpStopSessionReply *strep; -+ struct PptpOutCallRequest *ocreq; -+ struct PptpOutCallReply *ocack; -+ struct PptpInCallRequest *icreq; -+ struct PptpInCallReply *icack; -+ struct PptpInCallConnected *iccon; -+ struct PptpClearCallRequest *clrreq; -+ struct PptpCallDisconnectNotify *disc; -+ struct PptpWanErrorNotify *wanerr; -+ struct PptpSetLinkInfo *setlink; -+}; -+ -+ -+ -+#define PPTP_CONTROL_PORT 1723 -+ -+#define PPTP_PACKET_CONTROL 1 -+#define PPTP_PACKET_MGMT 2 -+ -+#define PPTP_MAGIC_COOKIE 0x1a2b3c4d -+ -+struct pptp_pkt_hdr { -+ __u16 packetLength; -+ __u16 packetType; -+ __u32 magicCookie; -+}; -+ -+/* PptpControlMessageType values */ -+#define PPTP_START_SESSION_REQUEST 1 -+#define PPTP_START_SESSION_REPLY 2 -+#define PPTP_STOP_SESSION_REQUEST 3 -+#define PPTP_STOP_SESSION_REPLY 4 -+#define PPTP_ECHO_REQUEST 5 -+#define PPTP_ECHO_REPLY 6 -+#define PPTP_OUT_CALL_REQUEST 7 -+#define PPTP_OUT_CALL_REPLY 8 -+#define PPTP_IN_CALL_REQUEST 9 -+#define PPTP_IN_CALL_REPLY 10 -+#define PPTP_IN_CALL_CONNECT 11 -+#define PPTP_CALL_CLEAR_REQUEST 12 -+#define PPTP_CALL_DISCONNECT_NOTIFY 13 -+#define PPTP_WAN_ERROR_NOTIFY 14 -+#define PPTP_SET_LINK_INFO 15 -+ -+#define PPTP_MSG_MAX 15 -+ -+/* PptpGeneralError values */ -+#define PPTP_ERROR_CODE_NONE 0 -+#define PPTP_NOT_CONNECTED 1 -+#define PPTP_BAD_FORMAT 2 -+#define PPTP_BAD_VALUE 3 -+#define PPTP_NO_RESOURCE 4 -+#define PPTP_BAD_CALLID 5 -+#define PPTP_REMOVE_DEVICE_ERROR 6 -+ -+struct PptpControlHeader { -+ __u16 messageType; -+ __u16 reserved; -+}; -+ -+/* FramingCapability Bitmap Values */ -+#define PPTP_FRAME_CAP_ASYNC 0x1 -+#define PPTP_FRAME_CAP_SYNC 0x2 -+ -+/* BearerCapability Bitmap Values */ -+#define PPTP_BEARER_CAP_ANALOG 0x1 -+#define PPTP_BEARER_CAP_DIGITAL 0x2 -+ -+struct PptpStartSessionRequest { -+ __u16 protocolVersion; -+ __u8 reserved1; -+ __u8 reserved2; -+ __u32 framingCapability; -+ __u32 bearerCapability; -+ __u16 maxChannels; -+ __u16 firmwareRevision; -+ __u8 hostName[64]; -+ __u8 vendorString[64]; -+}; -+ -+/* PptpStartSessionResultCode Values */ -+#define PPTP_START_OK 1 -+#define PPTP_START_GENERAL_ERROR 2 -+#define PPTP_START_ALREADY_CONNECTED 3 -+#define PPTP_START_NOT_AUTHORIZED 4 -+#define PPTP_START_UNKNOWN_PROTOCOL 5 -+ -+struct PptpStartSessionReply { -+ __u16 protocolVersion; -+ __u8 resultCode; -+ __u8 generalErrorCode; -+ __u32 framingCapability; -+ __u32 bearerCapability; -+ __u16 maxChannels; -+ __u16 firmwareRevision; -+ __u8 hostName[64]; -+ __u8 vendorString[64]; -+}; -+ -+/* PptpStopReasons */ -+#define PPTP_STOP_NONE 1 -+#define PPTP_STOP_PROTOCOL 2 -+#define PPTP_STOP_LOCAL_SHUTDOWN 3 -+ -+struct PptpStopSessionRequest { -+ __u8 reason; -+}; -+ -+/* PptpStopSessionResultCode */ -+#define PPTP_STOP_OK 1 -+#define PPTP_STOP_GENERAL_ERROR 2 -+ -+struct PptpStopSessionReply { -+ __u8 resultCode; -+ __u8 generalErrorCode; -+}; -+ -+struct PptpEchoRequest { -+ __u32 identNumber; -+}; -+ -+/* PptpEchoReplyResultCode */ -+#define PPTP_ECHO_OK 1 -+#define PPTP_ECHO_GENERAL_ERROR 2 -+ -+struct PptpEchoReply { -+ __u32 identNumber; -+ __u8 resultCode; -+ __u8 generalErrorCode; -+ __u16 reserved; -+}; -+ -+/* PptpFramingType */ -+#define PPTP_ASYNC_FRAMING 1 -+#define PPTP_SYNC_FRAMING 2 -+#define PPTP_DONT_CARE_FRAMING 3 -+ -+/* PptpCallBearerType */ -+#define PPTP_ANALOG_TYPE 1 -+#define PPTP_DIGITAL_TYPE 2 -+#define PPTP_DONT_CARE_BEARER_TYPE 3 -+ -+struct PptpOutCallRequest { -+ __u16 callID; -+ __u16 callSerialNumber; -+ __u32 minBPS; -+ __u32 maxBPS; -+ __u32 bearerType; -+ __u32 framingType; -+ __u16 packetWindow; -+ __u16 packetProcDelay; -+ __u16 reserved1; -+ __u16 phoneNumberLength; -+ __u16 reserved2; -+ __u8 phoneNumber[64]; -+ __u8 subAddress[64]; -+}; -+ -+/* PptpCallResultCode */ -+#define PPTP_OUTCALL_CONNECT 1 -+#define PPTP_OUTCALL_GENERAL_ERROR 2 -+#define PPTP_OUTCALL_NO_CARRIER 3 -+#define PPTP_OUTCALL_BUSY 4 -+#define PPTP_OUTCALL_NO_DIAL_TONE 5 -+#define PPTP_OUTCALL_TIMEOUT 6 -+#define PPTP_OUTCALL_DONT_ACCEPT 7 -+ -+struct PptpOutCallReply { -+ __u16 callID; -+ __u16 peersCallID; -+ __u8 resultCode; -+ __u8 generalErrorCode; -+ __u16 causeCode; -+ __u32 connectSpeed; -+ __u16 packetWindow; -+ __u16 packetProcDelay; -+ __u32 physChannelID; -+}; -+ -+struct PptpInCallRequest { -+ __u16 callID; -+ __u16 callSerialNumber; -+ __u32 callBearerType; -+ __u32 physChannelID; -+ __u16 dialedNumberLength; -+ __u16 dialingNumberLength; -+ __u8 dialedNumber[64]; -+ __u8 dialingNumber[64]; -+ __u8 subAddress[64]; -+}; -+ -+/* PptpInCallResultCode */ -+#define PPTP_INCALL_ACCEPT 1 -+#define PPTP_INCALL_GENERAL_ERROR 2 -+#define PPTP_INCALL_DONT_ACCEPT 3 -+ -+struct PptpInCallReply { -+ __u16 callID; -+ __u16 peersCallID; -+ __u8 resultCode; -+ __u8 generalErrorCode; -+ __u16 packetWindow; -+ __u16 packetProcDelay; -+ __u16 reserved; -+}; -+ -+struct PptpInCallConnected { -+ __u16 peersCallID; -+ __u16 reserved; -+ __u32 connectSpeed; -+ __u16 packetWindow; -+ __u16 packetProcDelay; -+ __u32 callFramingType; -+}; -+ -+struct PptpClearCallRequest { -+ __u16 callID; -+ __u16 reserved; -+}; -+ -+struct PptpCallDisconnectNotify { -+ __u16 callID; -+ __u8 resultCode; -+ __u8 generalErrorCode; -+ __u16 causeCode; -+ __u16 reserved; -+ __u8 callStatistics[128]; -+}; -+ -+struct PptpWanErrorNotify { -+ __u16 peersCallID; -+ __u16 reserved; -+ __u32 crcErrors; -+ __u32 framingErrors; -+ __u32 hardwareOverRuns; -+ __u32 bufferOverRuns; -+ __u32 timeoutErrors; -+ __u32 alignmentErrors; -+}; -+ -+struct PptpSetLinkInfo { -+ __u16 peersCallID; -+ __u16 reserved; -+ __u32 sendAccm; -+ __u32 recvAccm; -+}; -+ -+ -+struct pptp_priv_data { -+ __u16 call_id; -+ __u16 mcall_id; -+ __u16 pcall_id; -+}; -+ -+#endif /* __KERNEL__ */ -+#endif /* _CONNTRACK_PPTP_H */ ---- /dev/null -+++ b/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h -@@ -0,0 +1,123 @@ -+#ifndef _CONNTRACK_PROTO_GRE_H -+#define _CONNTRACK_PROTO_GRE_H -+#include <asm/byteorder.h> -+ -+/* GRE PROTOCOL HEADER */ -+ -+/* GRE Version field */ -+#define GRE_VERSION_1701 0x0 -+#define GRE_VERSION_PPTP 0x1 -+ -+/* GRE Protocol field */ -+#define GRE_PROTOCOL_PPTP 0x880B -+ -+/* GRE Flags */ -+#define GRE_FLAG_C 0x80 -+#define GRE_FLAG_R 0x40 -+#define GRE_FLAG_K 0x20 -+#define GRE_FLAG_S 0x10 -+#define GRE_FLAG_A 0x80 -+ -+#define GRE_IS_C(f) ((f)&GRE_FLAG_C) -+#define GRE_IS_R(f) ((f)&GRE_FLAG_R) -+#define GRE_IS_K(f) ((f)&GRE_FLAG_K) -+#define GRE_IS_S(f) ((f)&GRE_FLAG_S) -+#define GRE_IS_A(f) ((f)&GRE_FLAG_A) -+ -+/* GRE is a mess: Four different standards */ -+struct gre_hdr { -+#if defined(__LITTLE_ENDIAN_BITFIELD) -+ __u16 rec:3, -+ srr:1, -+ seq:1, -+ key:1, -+ routing:1, -+ csum:1, -+ version:3, -+ reserved:4, -+ ack:1; -+#elif defined(__BIG_ENDIAN_BITFIELD) -+ __u16 csum:1, -+ routing:1, -+ key:1, -+ seq:1, -+ srr:1, -+ rec:3, -+ ack:1, -+ reserved:4, -+ version:3; -+#else -+#error "Adjust your <asm/byteorder.h> defines" -+#endif -+ __u16 protocol; -+}; -+ -+/* modified GRE header for PPTP */ -+struct gre_hdr_pptp { -+ __u8 flags; /* bitfield */ -+ __u8 version; /* should be GRE_VERSION_PPTP */ -+ __u16 protocol; /* should be GRE_PROTOCOL_PPTP */ -+ __u16 payload_len; /* size of ppp payload, not inc. gre header */ -+ __u16 call_id; /* peer's call_id for this session */ -+ __u32 seq; /* sequence number. Present if S==1 */ -+ __u32 ack; /* seq number of highest packet recieved by */ -+ /* sender in this session */ -+}; -+ -+ -+/* this is part of ip_conntrack */ -+struct ip_ct_gre { -+ unsigned int stream_timeout; -+ unsigned int timeout; -+}; -+ -+/* this is part of ip_conntrack_expect */ -+struct ip_ct_gre_expect { -+ struct ip_ct_gre_keymap *keymap_orig, *keymap_reply; -+}; -+ -+#ifdef __KERNEL__ -+struct ip_conntrack_expect; -+ -+/* structure for original <-> reply keymap */ -+struct ip_ct_gre_keymap { -+ struct list_head list; -+ -+ struct ip_conntrack_tuple tuple; -+}; -+ -+ -+/* add new tuple->key_reply pair to keymap */ -+int ip_ct_gre_keymap_add(struct ip_conntrack_expect *exp, -+ struct ip_conntrack_tuple *t, -+ int reply); -+ -+/* change an existing keymap entry */ -+void ip_ct_gre_keymap_change(struct ip_ct_gre_keymap *km, -+ struct ip_conntrack_tuple *t); -+ -+/* delete keymap entries */ -+void ip_ct_gre_keymap_destroy(struct ip_conntrack_expect *exp); -+ -+ -+/* get pointer to gre key, if present */ -+static inline u_int32_t *gre_key(struct gre_hdr *greh) -+{ -+ if (!greh->key) -+ return NULL; -+ if (greh->csum || greh->routing) -+ return (u_int32_t *) (greh+sizeof(*greh)+4); -+ return (u_int32_t *) (greh+sizeof(*greh)); -+} -+ -+/* get pointer ot gre csum, if present */ -+static inline u_int16_t *gre_csum(struct gre_hdr *greh) -+{ -+ if (!greh->csum) -+ return NULL; -+ return (u_int16_t *) (greh+sizeof(*greh)); -+} -+ -+#endif /* __KERNEL__ */ -+ -+#endif /* _CONNTRACK_PROTO_GRE_H */ ---- a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h -+++ b/include/linux/netfilter_ipv4/ip_conntrack_tuple.h -@@ -14,7 +14,7 @@ - union ip_conntrack_manip_proto - { - /* Add other protocols here. */ -- u_int16_t all; -+ u_int32_t all; - - struct { - u_int16_t port; -@@ -25,6 +25,9 @@ union ip_conntrack_manip_proto - struct { - u_int16_t id; - } icmp; -+ struct { -+ u_int32_t key; -+ } gre; - }; - - /* The manipulable part of the tuple. */ -@@ -44,7 +47,7 @@ struct ip_conntrack_tuple - u_int32_t ip; - union { - /* Add other protocols here. */ -- u_int16_t all; -+ u_int32_t all; - - struct { - u_int16_t port; -@@ -55,6 +58,9 @@ struct ip_conntrack_tuple - struct { - u_int8_t type, code; - } icmp; -+ struct { -+ u_int32_t key; -+ } gre; - } u; - - /* The protocol. */ -@@ -80,10 +86,16 @@ enum ip_conntrack_dir - #ifdef __KERNEL__ - - #define DUMP_TUPLE(tp) \ --DEBUGP("tuple %p: %u %u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu\n", \ -+DEBUGP("tuple %p: %u %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n", \ - (tp), (tp)->dst.protonum, \ -- NIPQUAD((tp)->src.ip), ntohs((tp)->src.u.all), \ -- NIPQUAD((tp)->dst.ip), ntohs((tp)->dst.u.all)) -+ NIPQUAD((tp)->src.ip), ntohl((tp)->src.u.all), \ -+ NIPQUAD((tp)->dst.ip), ntohl((tp)->dst.u.all)) -+ -+#define DUMP_TUPLE_RAW(x) \ -+ DEBUGP("tuple %p: %u %u.%u.%u.%u:0x%08x -> %u.%u.%u.%u:0x%08x\n",\ -+ (x), (x)->dst.protonum, \ -+ NIPQUAD((x)->src.ip), ntohl((x)->src.u.all), \ -+ NIPQUAD((x)->dst.ip), ntohl((x)->dst.u.all)) - - #define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL) - ---- /dev/null -+++ b/include/linux/netfilter_ipv4/ip_nat_pptp.h -@@ -0,0 +1,11 @@ -+/* PPTP constants and structs */ -+#ifndef _NAT_PPTP_H -+#define _NAT_PPTP_H -+ -+/* conntrack private data */ -+struct ip_nat_pptp { -+ u_int16_t pns_call_id; /* NAT'ed PNS call id */ -+ u_int16_t pac_call_id; /* NAT'ed PAC call id */ -+}; -+ -+#endif /* _NAT_PPTP_H */ ---- a/net/ipv4/netfilter/Config.in -+++ b/net/ipv4/netfilter/Config.in -@@ -10,6 +10,8 @@ if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; - 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 - dep_tristate ' IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK -+ dep_tristate ' GRE protocol support' CONFIG_IP_NF_CT_PROTO_GRE $CONFIG_IP_NF_CONNTRACK -+ dep_tristate ' PPTP protocol support' CONFIG_IP_NF_PPTP $CONFIG_IP_NF_CT_PROTO_GRE - fi - - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then -@@ -63,6 +65,20 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; - 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 -+ if [ "$CONFIG_IP_NF_PPTP" = "m" ]; then -+ define_tristate CONFIG_IP_NF_NAT_PPTP m -+ else -+ if [ "$CONFIG_IP_NF_PPTP" = "y" ]; then -+ define_tristate CONFIG_IP_NF_NAT_PPTP $CONFIG_IP_NF_NAT -+ fi -+ fi -+ if [ "$CONFIG_IP_NF_CT_PROTO_GRE" = "m" ]; then -+ define_tristate CONFIG_IP_NF_NAT_PROTO_GRE m -+ else -+ if [ "$CONFIG_IP_NF_CT_PROTO_GRE" = "y" ]; then -+ define_tristate CONFIG_IP_NF_NAT_PROTO_GRE $CONFIG_IP_NF_NAT -+ fi -+ fi - if [ "$CONFIG_IP_NF_AMANDA" = "m" ]; then - define_tristate CONFIG_IP_NF_NAT_AMANDA m - else ---- a/net/ipv4/netfilter/ip_conntrack_core.c -+++ b/net/ipv4/netfilter/ip_conntrack_core.c -@@ -143,6 +143,8 @@ ip_ct_get_tuple(const struct iphdr *iph, - tuple->dst.ip = iph->daddr; - tuple->dst.protonum = iph->protocol; - -+ tuple->src.u.all = tuple->dst.u.all = 0; -+ - ret = protocol->pkt_to_tuple((u_int32_t *)iph + iph->ihl, - len - 4*iph->ihl, - tuple); -@@ -158,6 +160,8 @@ invert_tuple(struct ip_conntrack_tuple * - inverse->dst.ip = orig->src.ip; - inverse->dst.protonum = orig->dst.protonum; - -+ inverse->src.u.all = inverse->dst.u.all = 0; -+ - return protocol->invert_tuple(inverse, orig); - } - -@@ -970,8 +974,8 @@ int ip_conntrack_expect_related(struct i - * so there is no need to use the tuple lock too */ - - DEBUGP("ip_conntrack_expect_related %p\n", related_to); -- DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple); -- DEBUGP("mask: "); DUMP_TUPLE(&expect->mask); -+ DEBUGP("tuple: "); DUMP_TUPLE_RAW(&expect->tuple); -+ DEBUGP("mask: "); DUMP_TUPLE_RAW(&expect->mask); - - old = LIST_FIND(&ip_conntrack_expect_list, resent_expect, - struct ip_conntrack_expect *, &expect->tuple, -@@ -1089,15 +1093,14 @@ int ip_conntrack_change_expect(struct ip - - MUST_BE_READ_LOCKED(&ip_conntrack_lock); - WRITE_LOCK(&ip_conntrack_expect_tuple_lock); -- - DEBUGP("change_expect:\n"); -- DEBUGP("exp tuple: "); DUMP_TUPLE(&expect->tuple); -- DEBUGP("exp mask: "); DUMP_TUPLE(&expect->mask); -- DEBUGP("newtuple: "); DUMP_TUPLE(newtuple); -+ DEBUGP("exp tuple: "); DUMP_TUPLE_RAW(&expect->tuple); -+ DEBUGP("exp mask: "); DUMP_TUPLE_RAW(&expect->mask); -+ DEBUGP("newtuple: "); DUMP_TUPLE_RAW(newtuple); - if (expect->ct_tuple.dst.protonum == 0) { - /* Never seen before */ - DEBUGP("change expect: never seen before\n"); -- if (!ip_ct_tuple_equal(&expect->tuple, newtuple) -+ if (!ip_ct_tuple_mask_cmp(&expect->tuple, newtuple, &expect->mask) - && LIST_FIND(&ip_conntrack_expect_list, expect_clash, - struct ip_conntrack_expect *, newtuple, &expect->mask)) { - /* Force NAT to find an unused tuple */ ---- /dev/null -+++ b/net/ipv4/netfilter/ip_conntrack_pptp_priv.h -@@ -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 ---- /dev/null -+++ b/net/ipv4/netfilter/ip_conntrack_proto_gre.c -@@ -0,0 +1,333 @@ -+/* -+ * ip_conntrack_proto_gre.c - Version 1.2 -+ * -+ * Connection tracking protocol helper module for GRE. -+ * -+ * GRE is a generic encapsulation protocol, which is generally not very -+ * suited for NAT, as it has no protocol-specific part as port numbers. -+ * -+ * It has an optional key field, which may help us distinguishing two -+ * connections between the same two hosts. -+ * -+ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 -+ * -+ * PPTP is built on top of a modified version of GRE, and has a mandatory -+ * field called "CallID", which serves us for the same purpose as the key -+ * field in plain GRE. -+ * -+ * Documentation about PPTP can be found in RFC 2637 -+ * -+ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> -+ * -+ * Development of this code funded by Astaro AG (http://www.astaro.com/) -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/timer.h> -+#include <linux/netfilter.h> -+#include <linux/ip.h> -+#include <linux/in.h> -+#include <linux/list.h> -+ -+#include <linux/netfilter_ipv4/lockhelp.h> -+ -+DECLARE_RWLOCK(ip_ct_gre_lock); -+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_ct_gre_lock) -+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_ct_gre_lock) -+ -+#include <linux/netfilter_ipv4/listhelp.h> -+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h> -+#include <linux/netfilter_ipv4/ip_conntrack_helper.h> -+#include <linux/netfilter_ipv4/ip_conntrack_core.h> -+ -+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> -+#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); -+MODULE_DESCRIPTION("netfilter 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) -+#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); -+ -+static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km, -+ const struct ip_conntrack_tuple *t) -+{ -+ return ((km->tuple.src.ip == t->src.ip) && -+ (km->tuple.dst.ip == t->dst.ip) && -+ (km->tuple.dst.protonum == t->dst.protonum) && -+ (km->tuple.dst.u.all == t->dst.u.all)); -+} -+ -+/* look up the source key for a given tuple */ -+static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t) -+{ -+ struct ip_ct_gre_keymap *km; -+ u_int32_t key; -+ -+ READ_LOCK(&ip_ct_gre_lock); -+ km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn, -+ struct ip_ct_gre_keymap *, t); -+ if (!km) { -+ READ_UNLOCK(&ip_ct_gre_lock); -+ return 0; -+ } -+ -+ key = km->tuple.src.u.gre.key; -+ READ_UNLOCK(&ip_ct_gre_lock); -+ -+ return key; -+} -+ -+/* add a single keymap entry, associate with specified expect */ -+int ip_ct_gre_keymap_add(struct ip_conntrack_expect *exp, -+ struct ip_conntrack_tuple *t, int reply) -+{ -+ struct ip_ct_gre_keymap *km; -+ -+ km = kmalloc(sizeof(*km), GFP_ATOMIC); -+ if (!km) -+ return -1; -+ -+ /* initializing list head should be sufficient */ -+ memset(km, 0, sizeof(*km)); -+ -+ memcpy(&km->tuple, t, sizeof(*t)); -+ -+ if (!reply) -+ exp->proto.gre.keymap_orig = km; -+ else -+ exp->proto.gre.keymap_reply = km; -+ -+ DEBUGP("adding new entry %p: ", km); -+ DUMP_TUPLE_GRE(&km->tuple); -+ -+ WRITE_LOCK(&ip_ct_gre_lock); -+ list_append(&gre_keymap_list, km); -+ WRITE_UNLOCK(&ip_ct_gre_lock); -+ -+ return 0; -+} -+ -+/* 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); -+ -+ 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); -+} -+ -+ -+/* 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; -+} -+ -+/* gre hdr info to tuple */ -+static int gre_pkt_to_tuple(const void *datah, size_t datalen, -+ struct ip_conntrack_tuple *tuple) -+{ -+ struct gre_hdr *grehdr = (struct gre_hdr *) datah; -+ struct gre_hdr_pptp *pgrehdr = (struct gre_hdr_pptp *) datah; -+ u_int32_t srckey; -+ -+ /* 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; -+ } -+ tuple->dst.u.gre.key = *(gre_key(grehdr)); -+ break; -+ -+ case GRE_VERSION_PPTP: -+ if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) { -+ DEBUGP("GRE_VERSION_PPTP but unknown proto\n"); -+ return 0; -+ } -+ tuple->dst.u.gre.key = htonl(ntohs(pgrehdr->call_id)); -+ break; -+ -+ default: -+ printk(KERN_WARNING "unknown GRE version %hu\n", -+ grehdr->version); -+ return 0; -+ } -+ -+ 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 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)); -+} -+ -+/* 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)); -+} -+ -+/* 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; -+} -+ -+/* 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; -+} -+ -+/* 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); ---- a/net/ipv4/netfilter/ip_nat_core.c -+++ b/net/ipv4/netfilter/ip_nat_core.c -@@ -424,7 +424,7 @@ get_unique_tuple(struct ip_conntrack_tup - *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 @@ ip_nat_setup_info(struct ip_conntrack *c - 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): ---- /dev/null -+++ b/net/ipv4/netfilter/ip_nat_pptp.c -@@ -0,0 +1,482 @@ -+/* -+ * ip_nat_pptp.c - Version 1.5 -+ * -+ * NAT support for PPTP (Point to Point Tunneling Protocol). -+ * PPTP is a a protocol for creating virtual private networks. -+ * It is a specification defined by Microsoft and some vendors -+ * working with Microsoft. PPTP is built on top of a modified -+ * version of the Internet Generic Routing Encapsulation Protocol. -+ * GRE is defined in RFC 1701 and RFC 1702. Documentation of -+ * PPTP can be found in RFC 2637 -+ * -+ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> -+ * -+ * Development of this code funded by Astaro AG (http://www.astaro.com/) -+ * -+ * TODO: - Support for multiple calls within one session -+ * (needs netfilter newnat code) -+ * - NAT to a unique tuple, not to TCP source port -+ * (needs netfilter tuple reservation) -+ * -+ * Changes: -+ * 2002-02-10 - Version 1.3 -+ * - Use ip_nat_mangle_tcp_packet() because of cloned skb's -+ * in local connections (Philip Craig <philipc@snapgear.com>) -+ * - add checks for magicCookie and pptp version -+ * - make argument list of pptp_{out,in}bound_packet() shorter -+ * - move to C99 style initializers -+ * - print version number at module loadtime -+ * 2003-09-22 - Version 1.5 -+ * - use SNATed tcp sourceport as callid, since we get called before -+ * TCP header is mangled (Philip Craig <philipc@snapgear.com>) -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/ip.h> -+#include <linux/tcp.h> -+#include <net/tcp.h> -+#include <linux/netfilter_ipv4/ip_nat.h> -+#include <linux/netfilter_ipv4/ip_nat_rule.h> -+#include <linux/netfilter_ipv4/ip_nat_helper.h> -+#include <linux/netfilter_ipv4/ip_nat_pptp.h> -+#include <linux/netfilter_ipv4/ip_conntrack_helper.h> -+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> -+#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> -+ -+#define IP_NAT_PPTP_VERSION "1.5" -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); -+MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP"); -+ -+ -+#if 0 -+#include "ip_conntrack_pptp_priv.h" -+#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ -+ ": " format, ## args) -+#else -+#define DEBUGP(format, args...) -+#endif -+ -+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; -+ -+ IP_NF_ASSERT(info); -+ IP_NF_ASSERT(master); -+ IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); -+ -+ DEBUGP("we have a connection!\n"); -+ -+ LOCK_BH(&ip_pptp_lock); -+ ct_pptp_info = &master->help.ct_pptp_info; -+ nat_pptp_info = &master->nat.help.nat_pptp_info; -+ -+ /* need to alter GRE tuple because conntrack expectfn() used 'wrong' -+ * (unmanipulated) values */ -+ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { -+ DEBUGP("completing tuples with NAT info \n"); -+ /* we can do this, since we're unconfirmed */ -+ if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == -+ htonl(ct_pptp_info->pac_call_id)) { -+ /* assume PNS->PAC */ -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = -+ htonl(nat_pptp_info->pns_call_id); -+ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = -+ htonl(nat_pptp_info->pns_call_id); -+ newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; -+ newcid = htonl(nat_pptp_info->pac_call_id); -+ } else { -+ /* assume PAC->PNS */ -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = -+ htonl(nat_pptp_info->pac_call_id); -+ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = -+ htonl(nat_pptp_info->pac_call_id); -+ newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; -+ newcid = htonl(nat_pptp_info->pns_call_id); -+ } -+ } else { -+ if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == -+ htonl(ct_pptp_info->pac_call_id)) { -+ /* assume PNS->PAC */ -+ newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; -+ newcid = htonl(ct_pptp_info->pns_call_id); -+ } -+ else { -+ /* assume PAC->PNS */ -+ newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; -+ newcid = htonl(ct_pptp_info->pac_call_id); -+ } -+ } -+ -+ mr.rangesize = 1; -+ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED; -+ mr.range[0].min_ip = mr.range[0].max_ip = newip; -+ mr.range[0].min = mr.range[0].max = -+ ((union ip_conntrack_manip_proto ) { newcid }); -+ DEBUGP("change ip to %u.%u.%u.%u\n", -+ NIPQUAD(newip)); -+ DEBUGP("change key to 0x%x\n", ntohl(newcid)); -+ ret = ip_nat_setup_info(ct, &mr, hooknum); -+ -+ UNLOCK_BH(&ip_pptp_lock); -+ -+ return ret; -+ -+} -+ -+/* 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 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) -+{ -+ 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; -+ -+ /* 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; -+ -+ /* alter expectation */ -+ orig_t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; -+ reply_t = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; -+ if (t.src.ip == orig_t->src.ip && t.dst.ip == orig_t->dst.ip) { -+ /* expectation for PNS->PAC direction */ -+ t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id); -+ t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id); -+ inv_t.src.ip = reply_t->src.ip; -+ inv_t.dst.ip = reply_t->dst.ip; -+ inv_t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id); -+ inv_t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id); -+ } else { -+ /* expectation for PAC->PNS direction */ -+ t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id); -+ t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id); -+ inv_t.src.ip = orig_t->src.ip; -+ inv_t.dst.ip = orig_t->dst.ip; -+ inv_t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id); -+ inv_t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id); -+ } -+ -+ if (!ip_conntrack_change_expect(oldexp, &t)) { -+ DEBUGP("successfully changed expect\n"); -+ } else { -+ DEBUGP("can't change expect\n"); -+ } -+ 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: -+ pcid = &pptpReq.iccon->peersCallID; -+ if (!oldexp) -+ break; -+ old_dst_ip = oldexp->tuple.dst.ip; -+ t = oldexp->tuple; -+ -+ /* alter expectation, no need for callID */ -+ if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) { -+ /* expectation for PNS->PAC direction */ -+ t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; -+ } else { -+ /* expectation for PAC->PNS direction */ -+ t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; -+ } -+ -+ if (!ip_conntrack_change_expect(oldexp, &t)) { -+ DEBUGP("successfully changed expect\n"); -+ } else { -+ DEBUGP("can't change expect\n"); -+ } -+ break; -+ case PPTP_IN_CALL_REQUEST: -+ /* only need to nat in case PAC is behind NAT box */ -+ break; -+ case PPTP_WAN_ERROR_NOTIFY: -+ pcid = &pptpReq.wanerr->peersCallID; -+ break; -+ case PPTP_CALL_DISCONNECT_NOTIFY: -+ pcid = &pptpReq.disc->callID; -+ break; -+ case PPTP_SET_LINK_INFO: -+ pcid = &pptpReq.setlink->peersCallID; -+ break; -+ -+ default: -+ DEBUGP("unknown inbound packet %s\n", -+ (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]); -+ /* fall through */ -+ -+ case PPTP_START_SESSION_REQUEST: -+ case PPTP_START_SESSION_REPLY: -+ case PPTP_STOP_SESSION_REQUEST: -+ case PPTP_STOP_SESSION_REPLY: -+ case PPTP_ECHO_REQUEST: -+ case PPTP_ECHO_REPLY: -+ /* no need to alter packet */ -+ return NF_ACCEPT; -+ } -+ -+ /* mangle packet */ -+ IP_NF_ASSERT(pcid); -+ DEBUGP("altering peer call id from 0x%04x to 0x%04x\n", -+ ntohs(*pcid), ntohs(new_pcid)); -+ ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)pcid - (void *)pptph, -+ sizeof(new_pcid), (char *)&new_pcid, -+ sizeof(new_pcid)); -+ -+ if (new_cid) { -+ IP_NF_ASSERT(cid); -+ DEBUGP("altering call id from 0x%04x to 0x%04x\n", -+ ntohs(*cid), ntohs(new_cid)); -+ ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, -+ (void *)cid - (void *)pptph, -+ sizeof(new_cid), (char *)&new_cid, -+ sizeof(new_cid)); -+ } -+ -+ /* great, at least we don't need to resize packets */ -+ return NF_ACCEPT; -+} -+ -+ -+static unsigned int tcp_help(struct ip_conntrack *ct, -+ struct ip_conntrack_expect *exp, -+ struct ip_nat_info *info, -+ enum ip_conntrack_info ctinfo, -+ unsigned int hooknum, struct sk_buff **pskb) -+{ -+ struct iphdr *iph = (*pskb)->nh.iph; -+ struct tcphdr *tcph = (void *) iph + iph->ihl*4; -+ unsigned int datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4; -+ struct pptp_pkt_hdr *pptph; -+ -+ int dir; -+ -+ DEBUGP("entering\n"); -+ -+ /* Only mangle things once: DST for original direction -+ and SRC for reply direction. */ -+ dir = CTINFO2DIR(ctinfo); -+ if (!((HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC -+ && dir == IP_CT_DIR_ORIGINAL) -+ || (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST -+ && dir == IP_CT_DIR_REPLY))) { -+ DEBUGP("Not touching dir %s at hook %s\n", -+ dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", -+ hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" -+ : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" -+ : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" -+ : hooknum == NF_IP_LOCAL_IN ? "INPUT" : "???"); -+ return NF_ACCEPT; -+ } -+ -+ /* if packet is too small, just skip it */ -+ if (datalen < sizeof(struct pptp_pkt_hdr)+ -+ sizeof(struct PptpControlHeader)) { -+ DEBUGP("pptp packet too short\n"); -+ return NF_ACCEPT; -+ } -+ -+ pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4); -+ -+ /* if it's not a control message, we can't handle it */ -+ if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || -+ ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { -+ DEBUGP("not a pptp control packet\n"); -+ return NF_ACCEPT; -+ } -+ -+ LOCK_BH(&ip_pptp_lock); -+ -+ if (dir == IP_CT_DIR_ORIGINAL) { -+ /* reuqests sent by client to server (PNS->PAC) */ -+ pptp_outbound_pkt(pskb, ct, ctinfo, exp); -+ } else { -+ /* response from the server to the client (PAC->PNS) */ -+ pptp_inbound_pkt(pskb, ct, ctinfo, exp); -+ } -+ -+ UNLOCK_BH(&ip_pptp_lock); -+ -+ return NF_ACCEPT; -+} -+ -+/* nat helper struct for control connection */ -+static struct ip_nat_helper pptp_tcp_helper = { -+ .list = { NULL, NULL }, -+ .name = "pptp", -+ .flags = IP_NAT_HELPER_F_ALWAYS, -+ .me = THIS_MODULE, -+ .tuple = { .src = { .ip = 0, -+ .u = { .tcp = { .port = -+ __constant_htons(PPTP_CONTROL_PORT) } -+ } -+ }, -+ .dst = { .ip = 0, -+ .u = { .all = 0 }, -+ .protonum = IPPROTO_TCP -+ } -+ }, -+ -+ .mask = { .src = { .ip = 0, -+ .u = { .tcp = { .port = 0xFFFF } } -+ }, -+ .dst = { .ip = 0, -+ .u = { .all = 0 }, -+ .protonum = 0xFFFF -+ } -+ }, -+ .help = tcp_help, -+ .expect = pptp_nat_expected -+}; -+ -+ -+static int __init init(void) -+{ -+ DEBUGP("%s: registering NAT helper\n", __FILE__); -+ if (ip_nat_helper_register(&pptp_tcp_helper)) { -+ printk(KERN_ERR "Unable to register NAT application helper " -+ "for pptp\n"); -+ return -EIO; -+ } -+ -+ printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION); -+ return 0; -+} -+ -+static void __exit fini(void) -+{ -+ DEBUGP("cleanup_module\n" ); -+ ip_nat_helper_unregister(&pptp_tcp_helper); -+ printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION); -+} -+ -+module_init(init); -+module_exit(fini); ---- /dev/null -+++ b/net/ipv4/netfilter/ip_nat_proto_gre.c -@@ -0,0 +1,202 @@ -+/* -+ * ip_nat_proto_gre.c - Version 1.2 -+ * -+ * NAT protocol helper module for GRE. -+ * -+ * GRE is a generic encapsulation protocol, which is generally not very -+ * suited for NAT, as it has no protocol-specific part as port numbers. -+ * -+ * It has an optional key field, which may help us distinguishing two -+ * connections between the same two hosts. -+ * -+ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 -+ * -+ * PPTP is built on top of a modified version of GRE, and has a mandatory -+ * field called "CallID", which serves us for the same purpose as the key -+ * field in plain GRE. -+ * -+ * Documentation about PPTP can be found in RFC 2637 -+ * -+ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> -+ * -+ * Development of this code funded by Astaro AG (http://www.astaro.com/) -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/ip.h> -+#include <linux/netfilter_ipv4/ip_nat.h> -+#include <linux/netfilter_ipv4/ip_nat_rule.h> -+#include <linux/netfilter_ipv4/ip_nat_protocol.h> -+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); -+MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE"); -+ -+#if 0 -+#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ -+ ": " format, ## args) -+#else -+#define DEBUGP(x, args...) -+#endif -+ -+/* is key in given range between min and max */ -+static int -+gre_in_range(const struct ip_conntrack_tuple *tuple, -+ enum ip_nat_manip_type maniptype, -+ const union ip_conntrack_manip_proto *min, -+ const union ip_conntrack_manip_proto *max) -+{ -+ u_int32_t key; -+ -+ if (maniptype == IP_NAT_MANIP_SRC) -+ key = tuple->src.u.gre.key; -+ else -+ key = tuple->dst.u.gre.key; -+ -+ return ntohl(key) >= ntohl(min->gre.key) -+ && ntohl(key) <= ntohl(max->gre.key); -+} -+ -+/* generate unique tuple ... */ -+static int -+gre_unique_tuple(struct ip_conntrack_tuple *tuple, -+ const struct ip_nat_range *range, -+ enum ip_nat_manip_type maniptype, -+ const struct ip_conntrack *conntrack) -+{ -+ u_int32_t min, i, range_size; -+ u_int32_t key = 0, *keyptr; -+ -+ if (maniptype == IP_NAT_MANIP_SRC) -+ keyptr = &tuple->src.u.gre.key; -+ else -+ keyptr = &tuple->dst.u.gre.key; -+ -+ if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { -+ -+ DEBUGP("%p: NATing GRE PPTP\n", conntrack); -+ min = 1; -+ range_size = 0xffff; -+ -+ } else { -+ min = ntohl(range->min.gre.key); -+ range_size = ntohl(range->max.gre.key) - min + 1; -+ } -+ -+ DEBUGP("min = %u, range_size = %u\n", min, range_size); -+ -+ for (i = 0; i < range_size; i++, key++) { -+ *keyptr = htonl(min + key % range_size); -+ if (!ip_nat_used_tuple(tuple, conntrack)) -+ return 1; -+ } -+ -+ DEBUGP("%p: no NAT mapping\n", conntrack); -+ -+ return 0; -+} -+ -+/* 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; -+ } -+ } -+} -+ -+/* print out a nat tuple */ -+static unsigned int -+gre_print(char *buffer, -+ const struct ip_conntrack_tuple *match, -+ const struct ip_conntrack_tuple *mask) -+{ -+ unsigned int len = 0; -+ -+ if (mask->src.u.gre.key) -+ len += sprintf(buffer + len, "srckey=0x%x ", -+ ntohl(match->src.u.gre.key)); -+ -+ if (mask->dst.u.gre.key) -+ len += sprintf(buffer + len, "dstkey=0x%x ", -+ ntohl(match->src.u.gre.key)); -+ -+ return len; -+} -+ -+/* print a range of keys */ -+static unsigned int -+gre_print_range(char *buffer, const struct ip_nat_range *range) -+{ -+ if (range->min.gre.key != 0 -+ || range->max.gre.key != 0xFFFF) { -+ if (range->min.gre.key == range->max.gre.key) -+ return sprintf(buffer, "key 0x%x ", -+ ntohl(range->min.gre.key)); -+ else -+ return sprintf(buffer, "keys 0x%u-0x%u ", -+ ntohl(range->min.gre.key), -+ ntohl(range->max.gre.key)); -+ } else -+ return 0; -+} -+ -+/* nat helper struct */ -+static struct ip_nat_protocol gre = -+ { { NULL, NULL }, "GRE", IPPROTO_GRE, -+ gre_manip_pkt, -+ gre_in_range, -+ gre_unique_tuple, -+ gre_print, -+ gre_print_range -+ }; -+ -+static int __init init(void) -+{ -+ if (ip_nat_protocol_register(&gre)) -+ return -EIO; -+ -+ return 0; -+} -+ -+static void __exit fini(void) -+{ -+ ip_nat_protocol_unregister(&gre); -+} -+ -+module_init(init); -+module_exit(fini); ---- a/net/ipv4/netfilter/Makefile -+++ b/net/ipv4/netfilter/Makefile -@@ -36,23 +36,32 @@ obj-$(CONFIG_IP_NF_AMANDA) += ip_conntra - ifdef CONFIG_IP_NF_AMANDA - export-objs += ip_conntrack_amanda.o - endif -- - obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o - obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o - ifdef CONFIG_IP_NF_FTP - export-objs += ip_conntrack_ftp.o - endif -- - obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o - ifdef CONFIG_IP_NF_IRC - export-objs += ip_conntrack_irc.o - endif -+obj-$(CONFIG_IP_NF_CT_PROTO_GRE) += ip_conntrack_proto_gre.o -+ifdef CONFIG_IP_NF_CT_PROTO_GRE -+ export-objs += ip_conntrack_proto_gre.o -+endif -+obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o -+ifdef CONFIG_IP_NF_NAT_PPTP -+ export-objs += ip_conntrack_pptp.o -+endif -+ - - # NAT helpers - obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o - obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o - obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o - obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o -+obj-$(CONFIG_IP_NF_NAT_PROTO_GRE) += ip_nat_proto_gre.o -+obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o - - # generic IP tables - obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o ---- /dev/null -+++ b/net/ipv4/netfilter/ip_conntrack_pptp.c -@@ -0,0 +1,635 @@ -+/* -+ * ip_conntrack_pptp.c - Version 1.9 -+ * -+ * 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 -+ * -+ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> -+ * -+ * Development of this code funded by Astaro AG (http://www.astaro.com/) -+ * -+ * Limitations: -+ * - We blindly assume that control connections are always -+ * established in PNS->PAC direction. This is a violation -+ * of RFFC2673 -+ * -+ * TODO: - finish support for multiple calls within one session -+ * (needs expect reservations in newnat) -+ * - testing of incoming PPTP calls -+ * -+ * Changes: -+ * 2002-02-05 - Version 1.3 -+ * - Call ip_conntrack_unexpect_related() from -+ * pptp_timeout_related() to destroy expectations in case -+ * CALL_DISCONNECT_NOTIFY or tcp fin packet was seen -+ * (Philip Craig <philipc@snapgear.com>) -+ * - Add Version information at module loadtime -+ * 2002-02-10 - Version 1.6 -+ * - move to C99 style initializers -+ * - remove second expectation if first arrives -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/netfilter.h> -+#include <linux/ip.h> -+#include <net/checksum.h> -+#include <net/tcp.h> -+ -+#include <linux/netfilter_ipv4/lockhelp.h> -+#include <linux/netfilter_ipv4/ip_conntrack_helper.h> -+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> -+#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> -+ -+#define IP_CT_PPTP_VERSION "1.9" -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); -+MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP"); -+ -+DECLARE_LOCK(ip_pptp_lock); -+ -+#if 0 -+#include "ip_conntrack_pptp_priv.h" -+#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ -+ ": " format, ## args) -+#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) -+{ -+ struct ip_conntrack *master; -+ struct ip_conntrack_expect *exp; -+ -+ DEBUGP("increasing timeouts\n"); -+ /* increase timeout of GRE data channel conntrack entry */ -+ ct->proto.gre.timeout = PPTP_GRE_TIMEOUT; -+ ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT; -+ -+ master = master_ct(ct); -+ if (!master) { -+ DEBUGP(" no master!!!\n"); -+ return 0; -+ } -+ -+ exp = ct->master; -+ if (!exp) { -+ DEBUGP("no expectation!!\n"); -+ return 0; -+ } -+ -+ DEBUGP("completing tuples with ct info\n"); -+ /* we can do this, since we're unconfirmed */ -+ if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == -+ htonl(master->help.ct_pptp_info.pac_call_id)) { -+ /* assume PNS->PAC */ -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = -+ htonl(master->help.ct_pptp_info.pns_call_id); -+ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = -+ htonl(master->help.ct_pptp_info.pns_call_id); -+ } else { -+ /* assume PAC->PNS */ -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = -+ htonl(master->help.ct_pptp_info.pac_call_id); -+ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = -+ htonl(master->help.ct_pptp_info.pac_call_id); -+ } -+ -+ /* delete other expectation */ -+ if (exp->expected_list.next != &exp->expected_list) { -+ struct ip_conntrack_expect *other_exp; -+ struct list_head *cur_item, *next; -+ -+ for (cur_item = master->sibling_list.next; -+ cur_item != &master->sibling_list; cur_item = next) { -+ next = cur_item->next; -+ other_exp = list_entry(cur_item, -+ struct ip_conntrack_expect, -+ expected_list); -+ /* remove only if occurred at same sequence number */ -+ if (other_exp != exp && other_exp->seq == exp->seq) { -+ DEBUGP("unexpecting other direction\n"); -+ ip_ct_gre_keymap_destroy(other_exp); -+ ip_conntrack_unexpect_related(other_exp); -+ } -+ } -+ } -+ -+ 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); -+ -+ ip_ct_gre_keymap_destroy(exp); -+ if (!exp->sibling) { -+ ip_conntrack_unexpect_related(exp); -+ continue; -+ } -+ -+ DEBUGP("setting timeout of conntrack %p to 0\n", -+ exp->sibling); -+ exp->sibling->proto.gre.timeout = 0; -+ exp->sibling->proto.gre.stream_timeout = 0; -+ ip_ct_refresh_acct(exp->sibling, ctinfo, iph, 0); -+ } -+ -+ return 0; -+} -+ -+/* expect GRE connections (PNS->PAC and PAC->PNS direction) */ -+static inline int -+exp_gre(struct ip_conntrack *master, -+ u_int32_t seq, -+ u_int16_t callid, -+ u_int16_t peer_callid) -+{ -+ struct ip_conntrack_expect exp; -+ struct ip_conntrack_tuple inv_tuple; -+ -+ memset(&exp, 0, sizeof(exp)); -+ /* tuple in original direction, PNS->PAC */ -+ exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; -+ exp.tuple.src.u.gre.key = htonl(ntohs(peer_callid)); -+ exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; -+ exp.tuple.dst.u.gre.key = htonl(ntohs(callid)); -+ exp.tuple.dst.protonum = IPPROTO_GRE; -+ -+ 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 (ip_conntrack_expect_related(master, &exp) != 0) { -+ ip_ct_gre_keymap_destroy(&exp); -+ DEBUGP("cannot expect_related()\n"); -+ return 1; -+ } -+ -+ /* tuple in reply direction, PAC->PNS */ -+ exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; -+ exp.tuple.src.u.gre.key = htonl(ntohs(callid)); -+ exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; -+ exp.tuple.dst.u.gre.key = htonl(ntohs(peer_callid)); -+ -+ DEBUGP("calling expect_related "); -+ DUMP_TUPLE_RAW(&exp.tuple); -+ -+ /* Add GRE keymap entries */ -+ ip_ct_gre_keymap_add(&exp, &exp.tuple, 0); -+ invert_tuplepr(&inv_tuple, &exp.tuple); -+ ip_ct_gre_keymap_add(&exp, &inv_tuple, 1); -+ /* FIXME: cannot handle error correctly, since we need to free -+ * the above keymap :( */ -+ -+ if (ip_conntrack_expect_related(master, &exp) != 0) { -+ /* free the second pair of keypmaps */ -+ ip_ct_gre_keymap_destroy(&exp); -+ DEBUGP("cannot expect_related():\n"); -+ return 1; -+ } -+ -+ return 0; -+} -+ -+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 PptpControlHeader *ctlh; -+ union pptp_ctrl_union pptpReq; -+ -+ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; -+ u_int16_t msg, *cid, *pcid; -+ u_int32_t seq; -+ -+ ctlh = (struct PptpControlHeader *) -+ ((char *) pptph + sizeof(struct pptp_pkt_hdr)); -+ pptpReq.rawreq = (void *) -+ ((char *) ctlh + sizeof(struct PptpControlHeader)); -+ -+ 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 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; -+ -+ 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; -+ } -+ -+ 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)); -+ break; -+ } -+ -+ DEBUGP("%s, CID=%X, PCID=%X\n", strMName[msg], -+ ntohs(*cid), ntohs(*pcid)); -+ -+ info->cstate = PPTP_CALL_OUT_CONF; -+ -+ seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph); -+ if (exp_gre(ct, seq, *cid, *pcid) != 0) -+ printk("ip_conntrack_pptp: error during exp_gre\n"); -+ break; -+ -+ case PPTP_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; -+ -+ 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; -+ } -+ -+ pcid = &pptpReq.iccon->peersCallID; -+ cid = &info->pac_call_id; -+ -+ if (info->pns_call_id != ntohs(*pcid)) { -+ DEBUGP("%s for unknown CallID %u\n", -+ strMName[msg], ntohs(*cid)); -+ break; -+ } -+ -+ DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid)); -+ info->cstate = PPTP_CALL_IN_CONF; -+ -+ /* we expect a GRE connection from PAC to PNS */ -+ seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph); -+ if (exp_gre(ct, seq, *cid, *pcid) != 0) -+ printk("ip_conntrack_pptp: error during exp_gre\n"); -+ -+ break; -+ -+ case PPTP_CALL_DISCONNECT_NOTIFY: -+ /* server confirms disconnect */ -+ cid = &pptpReq.disc->callID; -+ DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid)); -+ info->cstate = PPTP_CALL_NONE; -+ -+ /* untrack this call id, unexpect GRE packets */ -+ pptp_timeout_related(ct); -+ break; -+ -+ case PPTP_WAN_ERROR_NOTIFY: -+ break; -+ -+ case PPTP_ECHO_REQUEST: -+ case PPTP_ECHO_REPLY: -+ /* I don't have to explain these ;) */ -+ break; -+ default: -+ DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX) -+ ? strMName[msg]:strMName[0], msg); -+ break; -+ } -+ -+ return NF_ACCEPT; -+ -+} -+ -+static inline int -+pptp_outbound_pkt(struct tcphdr *tcph, -+ struct pptp_pkt_hdr *pptph, -+ size_t datalen, -+ struct ip_conntrack *ct, -+ enum ip_conntrack_info ctinfo) -+{ -+ struct PptpControlHeader *ctlh; -+ union pptp_ctrl_union pptpReq; -+ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; -+ u_int16_t msg, *cid, *pcid; -+ -+ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph)); -+ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh)); -+ -+ msg = ntohs(ctlh->messageType); -+ DEBUGP("outbound control message %s\n", strMName[msg]); -+ -+ switch (msg) { -+ case PPTP_START_SESSION_REQUEST: -+ /* client requests for new control session */ -+ if (info->sstate != PPTP_SESSION_NONE) { -+ DEBUGP("%s but we already have one", -+ strMName[msg]); -+ } -+ info->sstate = PPTP_SESSION_REQUESTED; -+ break; -+ case PPTP_STOP_SESSION_REQUEST: -+ /* client requests end of control session */ -+ info->sstate = PPTP_SESSION_STOPREQ; -+ break; -+ -+ 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; -+ -+ case PPTP_CALL_CLEAR_REQUEST: -+ /* client requests hangup of call */ -+ if (info->sstate != PPTP_SESSION_CONFIRMED) { -+ DEBUGP("CLEAR_CALL but no session\n"); -+ break; -+ } -+ /* FUTURE: iterate over all calls and check if -+ * call ID is valid. We don't do this without newnat, -+ * because we only know about last call */ -+ info->cstate = PPTP_CALL_CLEAR_REQ; -+ break; -+ case PPTP_SET_LINK_INFO: -+ break; -+ case PPTP_ECHO_REQUEST: -+ case PPTP_ECHO_REPLY: -+ /* I don't have to explain these ;) */ -+ break; -+ default: -+ DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)? -+ strMName[msg]:strMName[0], msg); -+ /* unknown: no need to create GRE masq table entry */ -+ break; -+ } -+ -+ return NF_ACCEPT; -+} -+ -+ -+/* 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 pptp_pkt_hdr *pptph; -+ -+ struct tcphdr *tcph = (void *) iph + iph->ihl * 4; -+ u_int32_t tcplen = len - iph->ihl * 4; -+ u_int32_t datalen = tcplen - tcph->doff * 4; -+ void *datalimit; -+ int dir = CTINFO2DIR(ctinfo); -+ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; -+ -+ int oldsstate, oldcstate; -+ int ret; -+ -+ /* don't do any tracking before tcp handshake complete */ -+ if (ctinfo != IP_CT_ESTABLISHED -+ && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { -+ DEBUGP("ctinfo = %u, skipping\n", ctinfo); -+ return NF_ACCEPT; -+ } -+ -+ /* not a complete TCP header? */ -+ if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { -+ DEBUGP("tcplen = %u\n", tcplen); -+ return NF_ACCEPT; -+ } -+ -+ /* checksum invalid? */ -+ if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, -+ csum_partial((char *) tcph, tcplen, 0))) { -+ printk(KERN_NOTICE __FILE__ ": bad csum\n"); -+ /* W2K PPTP server sends TCP packets with wrong checksum :(( */ -+ //return NF_ACCEPT; -+ } -+ -+ if (tcph->fin || tcph->rst) { -+ DEBUGP("RST/FIN received, timeouting GRE\n"); -+ /* can't do this after real newnat */ -+ info->cstate = PPTP_CALL_NONE; -+ -+ /* 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 control packet\n"); -+ return NF_ACCEPT; -+ } -+ -+ oldsstate = info->sstate; -+ oldcstate = info->cstate; -+ -+ LOCK_BH(&ip_pptp_lock); -+ -+ /* FIXME: We just blindly assume that the control connection is always -+ * established from PNS->PAC. However, RFC makes no guarantee */ -+ if (dir == IP_CT_DIR_ORIGINAL) -+ /* client -> server (PNS -> PAC) */ -+ ret = pptp_outbound_pkt(tcph, pptph, datalen, ct, ctinfo); -+ else -+ /* server -> client (PAC -> PNS) */ -+ ret = pptp_inbound_pkt(tcph, pptph, datalen, ct, ctinfo); -+ DEBUGP("sstate: %d->%d, cstate: %d->%d\n", -+ oldsstate, info->sstate, oldcstate, info->cstate); -+ UNLOCK_BH(&ip_pptp_lock); -+ -+ return ret; -+} -+ -+/* control protocol helper */ -+static struct ip_conntrack_helper pptp = { -+ .list = { NULL, NULL }, -+ .name = "pptp", -+ .flags = IP_CT_HELPER_F_REUSE_EXPECT, -+ .me = THIS_MODULE, -+ .max_expected = 2, -+ .timeout = 0, -+ .tuple = { .src = { .ip = 0, -+ .u = { .tcp = { .port = -+ __constant_htons(PPTP_CONTROL_PORT) } } -+ }, -+ .dst = { .ip = 0, -+ .u = { .all = 0 }, -+ .protonum = IPPROTO_TCP -+ } -+ }, -+ .mask = { .src = { .ip = 0, -+ .u = { .tcp = { .port = 0xffff } } -+ }, -+ .dst = { .ip = 0, -+ .u = { .all = 0 }, -+ .protonum = 0xffff -+ } -+ }, -+ .help = conntrack_pptp_help -+}; -+ -+/* ip_conntrack_pptp initialization */ -+static int __init init(void) -+{ -+ int retcode; -+ -+ DEBUGP(__FILE__ ": registering helper\n"); -+ if ((retcode = ip_conntrack_helper_register(&pptp))) { -+ printk(KERN_ERR "Unable to register conntrack application " -+ "helper for pptp: %d\n", retcode); -+ return -EIO; -+ } -+ -+ printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION); -+ return 0; -+} -+ -+static void __exit fini(void) -+{ -+ ip_conntrack_helper_unregister(&pptp); -+ printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION); -+} -+ -+module_init(init); -+module_exit(fini); -+ -+EXPORT_SYMBOL(ip_pptp_lock); |