diff options
author | Denys Dmytriyenko <denis@denix.org> | 2009-03-17 14:32:59 -0400 |
---|---|---|
committer | Denys Dmytriyenko <denis@denix.org> | 2009-03-17 14:32:59 -0400 |
commit | 709c4d66e0b107ca606941b988bad717c0b45d9b (patch) | |
tree | 37ee08b1eb308f3b2b6426d5793545c38396b838 /packages/linux/linux-mtx-1-2.4.27/48-pptp.patch | |
parent | fa6cd5a3b993f16c27de4ff82b42684516d433ba (diff) |
rename packages/ to recipes/ per earlier agreement
See links below for more details:
http://thread.gmane.org/gmane.comp.handhelds.openembedded/21326
http://thread.gmane.org/gmane.comp.handhelds.openembedded/21816
Signed-off-by: Denys Dmytriyenko <denis@denix.org>
Acked-by: Mike Westerhof <mwester@dls.net>
Acked-by: Philip Balister <philip@balister.org>
Acked-by: Khem Raj <raj.khem@gmail.com>
Acked-by: Marcin Juszkiewicz <hrw@openembedded.org>
Acked-by: Koen Kooi <koen@openembedded.org>
Acked-by: Frans Meulenbroeks <fransmeulenbroeks@gmail.com>
Diffstat (limited to 'packages/linux/linux-mtx-1-2.4.27/48-pptp.patch')
-rw-r--r-- | packages/linux/linux-mtx-1-2.4.27/48-pptp.patch | 5092 |
1 files changed, 0 insertions, 5092 deletions
diff --git a/packages/linux/linux-mtx-1-2.4.27/48-pptp.patch b/packages/linux/linux-mtx-1-2.4.27/48-pptp.patch deleted file mode 100644 index 5896f90370..0000000000 --- a/packages/linux/linux-mtx-1-2.4.27/48-pptp.patch +++ /dev/null @@ -1,5092 +0,0 @@ -diff -uNr linux_org/Documentation/Configure.help linux/Documentation/Configure.help ---- linux_org/Documentation/Configure.help 2006-10-27 14:08:20.000000000 +0200 -+++ linux/Documentation/Configure.help 2006-10-27 14:11:52.000000000 +0200 -@@ -2848,6 +2848,31 @@ - If you want to compile it as a module, say M here and read - <file:Documentation/modules.txt>. If unsure, say `Y'. - -+PPTP conntrack and NAT support -+CONFIG_IP_NF_PPTP -+ This module adds support for PPTP (Point to Point Tunnelling Protocol, -+ RFC2637) conncection tracking and NAT. -+ -+ If you are running PPTP sessions over a stateful firewall or NAT box, -+ you may want to enable this feature. -+ -+ Please note that not all PPTP modes of operation are supported yet. -+ For more info, read top of the file net/ipv4/netfilter/ip_conntrack_pptp.c -+ -+ If you want to compile it as a module, say M here and read -+ Documentation/modules.txt. If unsure, say `N'. -+ -+GRE protocol conntrack and NAT support -+CONFIG_IP_NF_CT_PROTO_GRE -+ This module adds generic support for connection tracking and NAT of the -+ GRE protocol (RFC1701, RFC2784). Please note that this will only work -+ with GRE connections using the key field of the GRE header. -+ -+ You will need GRE support to enable PPTP support. -+ -+ If you want to compile it as a module, say `M' here and read -+ Documentation/modules.txt. If unsire, say `N'. -+ - User space queueing via NETLINK - CONFIG_IP_NF_QUEUE - Netfilter has the ability to queue packets to user space: the -diff -uNr linux_org/include/linux/netfilter_ipv4/ip_conntrack.h linux/include/linux/netfilter_ipv4/ip_conntrack.h ---- linux_org/include/linux/netfilter_ipv4/ip_conntrack.h 2004-11-24 12:13:57.000000000 +0100 -+++ linux/include/linux/netfilter_ipv4/ip_conntrack.h 2006-10-27 14:11:52.000000000 +0200 -@@ -50,19 +50,23 @@ - - #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_gre gre; - struct ip_ct_tcp tcp; - struct ip_ct_icmp icmp; - }; - - union ip_conntrack_expect_proto { - /* insert expect proto private data here */ -+ struct ip_ct_gre_expect gre; - }; - - /* Add protocol helper include file here */ -+#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> - #include <linux/netfilter_ipv4/ip_conntrack_amanda.h> - - #include <linux/netfilter_ipv4/ip_conntrack_ftp.h> -@@ -71,6 +75,7 @@ - /* per expectation: application helper private data */ - union ip_conntrack_expect_help { - /* insert conntrack helper private data (expect) here */ -+ struct ip_ct_pptp_expect exp_pptp_info; - struct ip_ct_amanda_expect exp_amanda_info; - struct ip_ct_ftp_expect exp_ftp_info; - struct ip_ct_irc_expect exp_irc_info; -@@ -85,16 +90,19 @@ - /* per conntrack: application helper private data */ - union ip_conntrack_help { - /* insert conntrack helper private data (master) here */ -+ struct ip_ct_pptp_master ct_pptp_info; - struct ip_ct_ftp_master ct_ftp_info; - struct ip_ct_irc_master ct_irc_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 - -diff -uNr linux_org/include/linux/netfilter_ipv4/ip_conntrack_pptp.h linux/include/linux/netfilter_ipv4/ip_conntrack_pptp.h ---- linux_org/include/linux/netfilter_ipv4/ip_conntrack_pptp.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/include/linux/netfilter_ipv4/ip_conntrack_pptp.h 2006-10-27 14:11:52.000000000 +0200 -@@ -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 PptpStopSessionReqest *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 */ -diff -uNr linux_org/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h linux/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h ---- linux_org/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h 2006-10-27 14:11:52.000000000 +0200 -@@ -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 */ -diff -uNr linux_org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h ---- linux_org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2003-11-17 02:07:46.000000000 +0100 -+++ linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2006-10-27 14:11:52.000000000 +0200 -@@ -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 @@ - struct { - u_int16_t id; - } icmp; -+ struct { -+ u_int32_t key; -+ } gre; - }; - - /* The manipulable part of the tuple. */ -@@ -44,7 +47,7 @@ - u_int32_t ip; - union { - /* Add other protocols here. */ -- u_int16_t all; -+ u_int64_t all; - - struct { - u_int16_t port; -@@ -55,6 +58,11 @@ - struct { - u_int8_t type, code; - } icmp; -+ struct { -+ u_int16_t protocol; -+ u_int8_t version; -+ u_int32_t key; -+ } gre; - } u; - - /* The protocol. */ -@@ -80,10 +88,16 @@ - #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) - -diff -uNr linux_org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h.orig linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h.orig ---- linux_org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h.orig 1970-01-01 01:00:00.000000000 +0100 -+++ linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h.orig 2003-11-17 02:07:46.000000000 +0100 -@@ -0,0 +1,139 @@ -+#ifndef _IP_CONNTRACK_TUPLE_H -+#define _IP_CONNTRACK_TUPLE_H -+ -+/* A `tuple' is a structure containing the information to uniquely -+ identify a connection. ie. if two packets have the same tuple, they -+ are in the same connection; if not, they are not. -+ -+ We divide the structure along "manipulatable" and -+ "non-manipulatable" lines, for the benefit of the NAT code. -+*/ -+ -+/* The protocol-specific manipulable parts of the tuple: always in -+ network order! */ -+union ip_conntrack_manip_proto -+{ -+ /* Add other protocols here. */ -+ u_int16_t all; -+ -+ struct { -+ u_int16_t port; -+ } tcp; -+ struct { -+ u_int16_t port; -+ } udp; -+ struct { -+ u_int16_t id; -+ } icmp; -+}; -+ -+/* The manipulable part of the tuple. */ -+struct ip_conntrack_manip -+{ -+ u_int32_t ip; -+ union ip_conntrack_manip_proto u; -+}; -+ -+/* This contains the information to distinguish a connection. */ -+struct ip_conntrack_tuple -+{ -+ struct ip_conntrack_manip src; -+ -+ /* These are the parts of the tuple which are fixed. */ -+ struct { -+ u_int32_t ip; -+ union { -+ /* Add other protocols here. */ -+ u_int16_t all; -+ -+ struct { -+ u_int16_t port; -+ } tcp; -+ struct { -+ u_int16_t port; -+ } udp; -+ struct { -+ u_int8_t type, code; -+ } icmp; -+ } u; -+ -+ /* The protocol. */ -+ u_int16_t protonum; -+ } dst; -+}; -+ -+/* This is optimized opposed to a memset of the whole structure. Everything we -+ * really care about is the source/destination unions */ -+#define IP_CT_TUPLE_U_BLANK(tuple) \ -+ do { \ -+ (tuple)->src.u.all = 0; \ -+ (tuple)->dst.u.all = 0; \ -+ } while (0) -+ -+enum ip_conntrack_dir -+{ -+ IP_CT_DIR_ORIGINAL, -+ IP_CT_DIR_REPLY, -+ IP_CT_DIR_MAX -+}; -+ -+#ifdef __KERNEL__ -+ -+#define DUMP_TUPLE(tp) \ -+DEBUGP("tuple %p: %u %u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu\n", \ -+ (tp), (tp)->dst.protonum, \ -+ NIPQUAD((tp)->src.ip), ntohs((tp)->src.u.all), \ -+ NIPQUAD((tp)->dst.ip), ntohs((tp)->dst.u.all)) -+ -+#define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL) -+ -+/* If we're the first tuple, it's the original dir. */ -+#define DIRECTION(h) ((enum ip_conntrack_dir)(&(h)->ctrack->tuplehash[1] == (h))) -+ -+/* Connections have two entries in the hash table: one for each way */ -+struct ip_conntrack_tuple_hash -+{ -+ struct list_head list; -+ -+ struct ip_conntrack_tuple tuple; -+ -+ /* this == &ctrack->tuplehash[DIRECTION(this)]. */ -+ struct ip_conntrack *ctrack; -+}; -+ -+#endif /* __KERNEL__ */ -+ -+static inline int ip_ct_tuple_src_equal(const struct ip_conntrack_tuple *t1, -+ const struct ip_conntrack_tuple *t2) -+{ -+ return t1->src.ip == t2->src.ip -+ && t1->src.u.all == t2->src.u.all; -+} -+ -+static inline int ip_ct_tuple_dst_equal(const struct ip_conntrack_tuple *t1, -+ const struct ip_conntrack_tuple *t2) -+{ -+ return t1->dst.ip == t2->dst.ip -+ && t1->dst.u.all == t2->dst.u.all -+ && t1->dst.protonum == t2->dst.protonum; -+} -+ -+static inline int ip_ct_tuple_equal(const struct ip_conntrack_tuple *t1, -+ const struct ip_conntrack_tuple *t2) -+{ -+ return ip_ct_tuple_src_equal(t1, t2) && ip_ct_tuple_dst_equal(t1, t2); -+} -+ -+static inline int ip_ct_tuple_mask_cmp(const struct ip_conntrack_tuple *t, -+ const struct ip_conntrack_tuple *tuple, -+ const struct ip_conntrack_tuple *mask) -+{ -+ return !(((t->src.ip ^ tuple->src.ip) & mask->src.ip) -+ || ((t->dst.ip ^ tuple->dst.ip) & mask->dst.ip) -+ || ((t->src.u.all ^ tuple->src.u.all) & mask->src.u.all) -+ || ((t->dst.u.all ^ tuple->dst.u.all) & mask->dst.u.all) -+ || ((t->dst.protonum ^ tuple->dst.protonum) -+ & mask->dst.protonum)); -+} -+ -+#endif /* _IP_CONNTRACK_TUPLE_H */ -diff -uNr linux_org/include/linux/netfilter_ipv4/ip_nat_pptp.h linux/include/linux/netfilter_ipv4/ip_nat_pptp.h ---- linux_org/include/linux/netfilter_ipv4/ip_nat_pptp.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/include/linux/netfilter_ipv4/ip_nat_pptp.h 2006-10-27 14:11:52.000000000 +0200 -@@ -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 */ -diff -uNr linux_org/net/ipv4/netfilter/Config.in linux/net/ipv4/netfilter/Config.in ---- linux_org/net/ipv4/netfilter/Config.in 2003-08-13 19:19:30.000000000 +0200 -+++ linux/net/ipv4/netfilter/Config.in 2006-10-27 14:11:52.000000000 +0200 -@@ -7,6 +7,11 @@ - tristate 'Connection tracking (required for masq/NAT)' CONFIG_IP_NF_CONNTRACK - if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then - dep_tristate ' FTP protocol support' CONFIG_IP_NF_FTP $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_CONNTRACK -+ if [ "$CONFIG_IP_NF_PPTP" != "n" ]; then -+ bool ' PPTP verbose debug' CONFIG_IP_NF_PPTP_DEBUG -+ fi - 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 -@@ -67,6 +72,20 @@ - fi - fi - bool ' NAT of local connections (READ HELP)' CONFIG_IP_NF_NAT_LOCAL -+ 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_EXPERIMENTAL" = "y" ]; then - dep_tristate ' Basic SNMP-ALG support (EXPERIMENTAL)' CONFIG_IP_NF_NAT_SNMP_BASIC $CONFIG_IP_NF_NAT - fi -diff -uNr linux_org/net/ipv4/netfilter/Makefile linux/net/ipv4/netfilter/Makefile ---- linux_org/net/ipv4/netfilter/Makefile 2003-08-13 19:19:30.000000000 +0200 -+++ linux/net/ipv4/netfilter/Makefile 2006-10-27 14:11:52.000000000 +0200 -@@ -30,8 +30,21 @@ - - # connection tracking - obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o -+ -+# connection tracking protocol helpers -+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 -+ -+# NAT protocol helpers -+obj-$(CONFIG_IP_NF_NAT_PROTO_GRE) += ip_nat_proto_gre.o - - # connection tracking helpers -+obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o -+ifdef CONFIG_IP_NF_NAT_PPTP -+ export-objs += ip_conntrack_pptp.o -+endif - obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o - ifdef CONFIG_IP_NF_AMANDA - export-objs += ip_conntrack_amanda.o -@@ -49,6 +62,7 @@ - endif - - # NAT helpers -+obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o - 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 -diff -uNr linux_org/net/ipv4/netfilter/ip_conntrack_core.c linux/net/ipv4/netfilter/ip_conntrack_core.c ---- linux_org/net/ipv4/netfilter/ip_conntrack_core.c 2004-11-24 12:14:04.000000000 +0100 -+++ linux/net/ipv4/netfilter/ip_conntrack_core.c 2006-10-27 14:11:52.000000000 +0200 -@@ -142,6 +142,8 @@ - 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); -@@ -157,6 +159,8 @@ - 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); - } - -@@ -945,8 +949,8 @@ - * 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, -@@ -1063,15 +1067,14 @@ - - 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 */ -diff -uNr linux_org/net/ipv4/netfilter/ip_conntrack_core.c.orig linux/net/ipv4/netfilter/ip_conntrack_core.c.orig ---- linux_org/net/ipv4/netfilter/ip_conntrack_core.c.orig 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/ipv4/netfilter/ip_conntrack_core.c.orig 2004-11-24 12:14:04.000000000 +0100 -@@ -0,0 +1,1446 @@ -+/* Connection state tracking for netfilter. This is separated from, -+ but required by, the NAT layer; it can also be used by an iptables -+ extension. */ -+ -+/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General -+ * Public Licence. -+ * -+ * 23 Apr 2001: Harald Welte <laforge@gnumonks.org> -+ * - new API and handling of conntrack/nat helpers -+ * - now capable of multiple expectations for one master -+ * 16 Jul 2002: Harald Welte <laforge@gnumonks.org> -+ * - add usage/reference counts to ip_conntrack_expect -+ * - export ip_conntrack[_expect]_{find_get,put} functions -+ * */ -+ -+#include <linux/version.h> -+#include <linux/config.h> -+#include <linux/types.h> -+#include <linux/ip.h> -+#include <linux/netfilter.h> -+#include <linux/netfilter_ipv4.h> -+#include <linux/module.h> -+#include <linux/skbuff.h> -+#include <linux/proc_fs.h> -+#include <linux/vmalloc.h> -+#include <linux/brlock.h> -+#include <net/checksum.h> -+#include <linux/stddef.h> -+#include <linux/sysctl.h> -+#include <linux/slab.h> -+#include <linux/random.h> -+#include <linux/jhash.h> -+/* For ERR_PTR(). Yeah, I know... --RR */ -+#include <linux/fs.h> -+ -+/* This rwlock protects the main hash table, protocol/helper/expected -+ registrations, conntrack timers*/ -+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock) -+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock) -+ -+#include <linux/netfilter_ipv4/ip_conntrack.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/listhelp.h> -+ -+#define IP_CONNTRACK_VERSION "2.1" -+ -+#if 0 -+#define DEBUGP printk -+#else -+#define DEBUGP(format, args...) -+#endif -+ -+DECLARE_RWLOCK(ip_conntrack_lock); -+DECLARE_RWLOCK(ip_conntrack_expect_tuple_lock); -+ -+void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL; -+LIST_HEAD(ip_conntrack_expect_list); -+LIST_HEAD(protocol_list); -+static LIST_HEAD(helpers); -+unsigned int ip_conntrack_htable_size = 0; -+int ip_conntrack_max = 0; -+static atomic_t ip_conntrack_count = ATOMIC_INIT(0); -+struct list_head *ip_conntrack_hash; -+static kmem_cache_t *ip_conntrack_cachep; -+ -+extern struct ip_conntrack_protocol ip_conntrack_generic_protocol; -+ -+static inline int proto_cmpfn(const struct ip_conntrack_protocol *curr, -+ u_int8_t protocol) -+{ -+ return protocol == curr->proto; -+} -+ -+struct ip_conntrack_protocol *__ip_ct_find_proto(u_int8_t protocol) -+{ -+ struct ip_conntrack_protocol *p; -+ -+ MUST_BE_READ_LOCKED(&ip_conntrack_lock); -+ p = LIST_FIND(&protocol_list, proto_cmpfn, -+ struct ip_conntrack_protocol *, protocol); -+ if (!p) -+ p = &ip_conntrack_generic_protocol; -+ -+ return p; -+} -+ -+struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol) -+{ -+ struct ip_conntrack_protocol *p; -+ -+ READ_LOCK(&ip_conntrack_lock); -+ p = __ip_ct_find_proto(protocol); -+ READ_UNLOCK(&ip_conntrack_lock); -+ return p; -+} -+ -+inline void -+ip_conntrack_put(struct ip_conntrack *ct) -+{ -+ IP_NF_ASSERT(ct); -+ IP_NF_ASSERT(ct->infos[0].master); -+ /* nf_conntrack_put wants to go via an info struct, so feed it -+ one at random. */ -+ nf_conntrack_put(&ct->infos[0]); -+} -+ -+static int ip_conntrack_hash_rnd_initted; -+static unsigned int ip_conntrack_hash_rnd; -+ -+static u_int32_t -+hash_conntrack(const struct ip_conntrack_tuple *tuple) -+{ -+#if 0 -+ dump_tuple(tuple); -+#endif -+ return (jhash_3words(tuple->src.ip, -+ (tuple->dst.ip ^ tuple->dst.protonum), -+ (tuple->src.u.all | (tuple->dst.u.all << 16)), -+ ip_conntrack_hash_rnd) % ip_conntrack_htable_size); -+} -+ -+inline int -+get_tuple(const struct iphdr *iph, size_t len, -+ struct ip_conntrack_tuple *tuple, -+ struct ip_conntrack_protocol *protocol) -+{ -+ int ret; -+ -+ /* Never happen */ -+ if (iph->frag_off & htons(IP_OFFSET)) { -+ printk("ip_conntrack_core: Frag of proto %u.\n", -+ iph->protocol); -+ return 0; -+ } -+ /* Guarantee 8 protocol bytes: if more wanted, use len param */ -+ else if (iph->ihl * 4 + 8 > len) -+ return 0; -+ -+ tuple->src.ip = iph->saddr; -+ tuple->dst.ip = iph->daddr; -+ tuple->dst.protonum = iph->protocol; -+ -+ ret = protocol->pkt_to_tuple((u_int32_t *)iph + iph->ihl, -+ len - 4*iph->ihl, -+ tuple); -+ return ret; -+} -+ -+static int -+invert_tuple(struct ip_conntrack_tuple *inverse, -+ const struct ip_conntrack_tuple *orig, -+ const struct ip_conntrack_protocol *protocol) -+{ -+ inverse->src.ip = orig->dst.ip; -+ inverse->dst.ip = orig->src.ip; -+ inverse->dst.protonum = orig->dst.protonum; -+ -+ return protocol->invert_tuple(inverse, orig); -+} -+ -+ -+/* ip_conntrack_expect helper functions */ -+ -+/* Compare tuple parts depending on mask. */ -+static inline int expect_cmp(const struct ip_conntrack_expect *i, -+ const struct ip_conntrack_tuple *tuple) -+{ -+ MUST_BE_READ_LOCKED(&ip_conntrack_expect_tuple_lock); -+ return ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask); -+} -+ -+static void -+destroy_expect(struct ip_conntrack_expect *exp) -+{ -+ DEBUGP("destroy_expect(%p) use=%d\n", exp, atomic_read(&exp->use)); -+ IP_NF_ASSERT(atomic_read(&exp->use) == 0); -+ IP_NF_ASSERT(!timer_pending(&exp->timeout)); -+ -+ kfree(exp); -+} -+ -+inline void ip_conntrack_expect_put(struct ip_conntrack_expect *exp) -+{ -+ IP_NF_ASSERT(exp); -+ -+ if (atomic_dec_and_test(&exp->use)) { -+ /* usage count dropped to zero */ -+ destroy_expect(exp); -+ } -+} -+ -+static inline struct ip_conntrack_expect * -+__ip_ct_expect_find(const struct ip_conntrack_tuple *tuple) -+{ -+ MUST_BE_READ_LOCKED(&ip_conntrack_lock); -+ MUST_BE_READ_LOCKED(&ip_conntrack_expect_tuple_lock); -+ return LIST_FIND(&ip_conntrack_expect_list, expect_cmp, -+ struct ip_conntrack_expect *, tuple); -+} -+ -+/* Find a expectation corresponding to a tuple. */ -+struct ip_conntrack_expect * -+ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple) -+{ -+ struct ip_conntrack_expect *exp; -+ -+ READ_LOCK(&ip_conntrack_lock); -+ READ_LOCK(&ip_conntrack_expect_tuple_lock); -+ exp = __ip_ct_expect_find(tuple); -+ if (exp) -+ atomic_inc(&exp->use); -+ READ_UNLOCK(&ip_conntrack_expect_tuple_lock); -+ READ_UNLOCK(&ip_conntrack_lock); -+ -+ return exp; -+} -+ -+/* remove one specific expectation from all lists and drop refcount, -+ * does _NOT_ delete the timer. */ -+static void __unexpect_related(struct ip_conntrack_expect *expect) -+{ -+ DEBUGP("unexpect_related(%p)\n", expect); -+ MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); -+ -+ /* we're not allowed to unexpect a confirmed expectation! */ -+ IP_NF_ASSERT(!expect->sibling); -+ -+ /* delete from global and local lists */ -+ list_del(&expect->list); -+ list_del(&expect->expected_list); -+ -+ /* decrement expect-count of master conntrack */ -+ if (expect->expectant) -+ expect->expectant->expecting--; -+ -+ ip_conntrack_expect_put(expect); -+} -+ -+/* remove one specific expecatation from all lists, drop refcount -+ * and expire timer. -+ * This function can _NOT_ be called for confirmed expects! */ -+static void unexpect_related(struct ip_conntrack_expect *expect) -+{ -+ IP_NF_ASSERT(expect->expectant); -+ IP_NF_ASSERT(expect->expectant->helper); -+ /* if we are supposed to have a timer, but we can't delete -+ * it: race condition. __unexpect_related will -+ * be calledd by timeout function */ -+ if (expect->expectant->helper->timeout -+ && !del_timer(&expect->timeout)) -+ return; -+ -+ __unexpect_related(expect); -+} -+ -+/* delete all unconfirmed expectations for this conntrack */ -+static void remove_expectations(struct ip_conntrack *ct, int drop_refcount) -+{ -+ struct list_head *exp_entry, *next; -+ struct ip_conntrack_expect *exp; -+ -+ DEBUGP("remove_expectations(%p)\n", ct); -+ -+ list_for_each_safe(exp_entry, next, &ct->sibling_list) { -+ exp = list_entry(exp_entry, struct ip_conntrack_expect, -+ expected_list); -+ -+ /* we skip established expectations, as we want to delete -+ * the un-established ones only */ -+ if (exp->sibling) { -+ DEBUGP("remove_expectations: skipping established %p of %p\n", exp->sibling, ct); -+ if (drop_refcount) { -+ /* Indicate that this expectations parent is dead */ -+ ip_conntrack_put(exp->expectant); -+ exp->expectant = NULL; -+ } -+ continue; -+ } -+ -+ IP_NF_ASSERT(list_inlist(&ip_conntrack_expect_list, exp)); -+ IP_NF_ASSERT(exp->expectant == ct); -+ -+ /* delete expectation from global and private lists */ -+ unexpect_related(exp); -+ } -+} -+ -+static void -+clean_from_lists(struct ip_conntrack *ct) -+{ -+ unsigned int ho, hr; -+ -+ DEBUGP("clean_from_lists(%p)\n", ct); -+ MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); -+ -+ ho = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); -+ hr = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); -+ LIST_DELETE(&ip_conntrack_hash[ho], &ct->tuplehash[IP_CT_DIR_ORIGINAL]); -+ LIST_DELETE(&ip_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]); -+ -+ /* Destroy all un-established, pending expectations */ -+ remove_expectations(ct, 1); -+} -+ -+static void -+destroy_conntrack(struct nf_conntrack *nfct) -+{ -+ struct ip_conntrack *ct = (struct ip_conntrack *)nfct, *master = NULL; -+ struct ip_conntrack_protocol *proto; -+ -+ DEBUGP("destroy_conntrack(%p)\n", ct); -+ IP_NF_ASSERT(atomic_read(&nfct->use) == 0); -+ IP_NF_ASSERT(!timer_pending(&ct->timeout)); -+ -+ /* To make sure we don't get any weird locking issues here: -+ * destroy_conntrack() MUST NOT be called with a write lock -+ * to ip_conntrack_lock!!! -HW */ -+ proto = ip_ct_find_proto(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum); -+ if (proto && proto->destroy) -+ proto->destroy(ct); -+ -+ if (ip_conntrack_destroyed) -+ ip_conntrack_destroyed(ct); -+ -+ WRITE_LOCK(&ip_conntrack_lock); -+ /* Make sure don't leave any orphaned expectations lying around */ -+ if (ct->expecting) -+ remove_expectations(ct, 1); -+ -+ /* Delete our master expectation */ -+ if (ct->master) { -+ if (ct->master->expectant) { -+ /* can't call __unexpect_related here, -+ * since it would screw up expect_list */ -+ list_del(&ct->master->expected_list); -+ master = ct->master->expectant; -+ } -+ kfree(ct->master); -+ } -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ -+ if (master) -+ ip_conntrack_put(master); -+ -+ DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct); -+ kmem_cache_free(ip_conntrack_cachep, ct); -+ atomic_dec(&ip_conntrack_count); -+} -+ -+static void death_by_timeout(unsigned long ul_conntrack) -+{ -+ struct ip_conntrack *ct = (void *)ul_conntrack; -+ -+ WRITE_LOCK(&ip_conntrack_lock); -+ clean_from_lists(ct); -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ ip_conntrack_put(ct); -+} -+ -+static inline int -+conntrack_tuple_cmp(const struct ip_conntrack_tuple_hash *i, -+ const struct ip_conntrack_tuple *tuple, -+ const struct ip_conntrack *ignored_conntrack) -+{ -+ MUST_BE_READ_LOCKED(&ip_conntrack_lock); -+ return i->ctrack != ignored_conntrack -+ && ip_ct_tuple_equal(tuple, &i->tuple); -+} -+ -+static struct ip_conntrack_tuple_hash * -+__ip_conntrack_find(const struct ip_conntrack_tuple *tuple, -+ const struct ip_conntrack *ignored_conntrack) -+{ -+ struct ip_conntrack_tuple_hash *h; -+ unsigned int hash = hash_conntrack(tuple); -+ -+ MUST_BE_READ_LOCKED(&ip_conntrack_lock); -+ h = LIST_FIND(&ip_conntrack_hash[hash], -+ conntrack_tuple_cmp, -+ struct ip_conntrack_tuple_hash *, -+ tuple, ignored_conntrack); -+ return h; -+} -+ -+/* Find a connection corresponding to a tuple. */ -+struct ip_conntrack_tuple_hash * -+ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple, -+ const struct ip_conntrack *ignored_conntrack) -+{ -+ struct ip_conntrack_tuple_hash *h; -+ -+ READ_LOCK(&ip_conntrack_lock); -+ h = __ip_conntrack_find(tuple, ignored_conntrack); -+ if (h) -+ atomic_inc(&h->ctrack->ct_general.use); -+ READ_UNLOCK(&ip_conntrack_lock); -+ -+ return h; -+} -+ -+static inline struct ip_conntrack * -+__ip_conntrack_get(struct nf_ct_info *nfct, enum ip_conntrack_info *ctinfo) -+{ -+ struct ip_conntrack *ct -+ = (struct ip_conntrack *)nfct->master; -+ -+ /* ctinfo is the index of the nfct inside the conntrack */ -+ *ctinfo = nfct - ct->infos; -+ IP_NF_ASSERT(*ctinfo >= 0 && *ctinfo < IP_CT_NUMBER); -+ return ct; -+} -+ -+/* Return conntrack and conntrack_info given skb->nfct->master */ -+struct ip_conntrack * -+ip_conntrack_get(struct sk_buff *skb, enum ip_conntrack_info *ctinfo) -+{ -+ if (skb->nfct) -+ return __ip_conntrack_get(skb->nfct, ctinfo); -+ return NULL; -+} -+ -+/* Confirm a connection given skb->nfct; places it in hash table */ -+int -+__ip_conntrack_confirm(struct nf_ct_info *nfct) -+{ -+ unsigned int hash, repl_hash; -+ struct ip_conntrack *ct; -+ enum ip_conntrack_info ctinfo; -+ -+ ct = __ip_conntrack_get(nfct, &ctinfo); -+ -+ /* ipt_REJECT uses ip_conntrack_attach to attach related -+ ICMP/TCP RST packets in other direction. Actual packet -+ which created connection will be IP_CT_NEW or for an -+ expected connection, IP_CT_RELATED. */ -+ if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) -+ return NF_ACCEPT; -+ -+ hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); -+ repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); -+ -+ /* We're not in hash table, and we refuse to set up related -+ connections for unconfirmed conns. But packet copies and -+ REJECT will give spurious warnings here. */ -+ /* IP_NF_ASSERT(atomic_read(&ct->ct_general.use) == 1); */ -+ -+ /* No external references means noone else could have -+ confirmed us. */ -+ IP_NF_ASSERT(!is_confirmed(ct)); -+ DEBUGP("Confirming conntrack %p\n", ct); -+ -+ WRITE_LOCK(&ip_conntrack_lock); -+ /* See if there's one in the list already, including reverse: -+ NAT could have grabbed it without realizing, since we're -+ not in the hash. If there is, we lost race. */ -+ if (!LIST_FIND(&ip_conntrack_hash[hash], -+ conntrack_tuple_cmp, -+ struct ip_conntrack_tuple_hash *, -+ &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, NULL) -+ && !LIST_FIND(&ip_conntrack_hash[repl_hash], -+ conntrack_tuple_cmp, -+ struct ip_conntrack_tuple_hash *, -+ &ct->tuplehash[IP_CT_DIR_REPLY].tuple, NULL)) { -+ list_prepend(&ip_conntrack_hash[hash], -+ &ct->tuplehash[IP_CT_DIR_ORIGINAL]); -+ list_prepend(&ip_conntrack_hash[repl_hash], -+ &ct->tuplehash[IP_CT_DIR_REPLY]); -+ /* Timer relative to confirmation time, not original -+ setting time, otherwise we'd get timer wrap in -+ weird delay cases. */ -+ ct->timeout.expires += jiffies; -+ add_timer(&ct->timeout); -+ atomic_inc(&ct->ct_general.use); -+ set_bit(IPS_CONFIRMED_BIT, &ct->status); -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ return NF_ACCEPT; -+ } -+ -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ return NF_DROP; -+} -+ -+/* Returns true if a connection correspondings to the tuple (required -+ for NAT). */ -+int -+ip_conntrack_tuple_taken(const struct ip_conntrack_tuple *tuple, -+ const struct ip_conntrack *ignored_conntrack) -+{ -+ struct ip_conntrack_tuple_hash *h; -+ -+ READ_LOCK(&ip_conntrack_lock); -+ h = __ip_conntrack_find(tuple, ignored_conntrack); -+ READ_UNLOCK(&ip_conntrack_lock); -+ -+ return h != NULL; -+} -+ -+/* Returns conntrack if it dealt with ICMP, and filled in skb fields */ -+struct ip_conntrack * -+icmp_error_track(struct sk_buff *skb, -+ enum ip_conntrack_info *ctinfo, -+ unsigned int hooknum) -+{ -+ const struct iphdr *iph = skb->nh.iph; -+ struct icmphdr *hdr; -+ struct ip_conntrack_tuple innertuple, origtuple; -+ struct iphdr *inner; -+ size_t datalen; -+ struct ip_conntrack_protocol *innerproto; -+ struct ip_conntrack_tuple_hash *h; -+ -+ IP_NF_ASSERT(iph->protocol == IPPROTO_ICMP); -+ IP_NF_ASSERT(skb->nfct == NULL); -+ -+ hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl); -+ inner = (struct iphdr *)(hdr + 1); -+ datalen = skb->len - iph->ihl*4 - sizeof(*hdr); -+ -+ if (skb->len < iph->ihl * 4 + sizeof(*hdr) + sizeof(*iph)) { -+ DEBUGP("icmp_error_track: too short\n"); -+ return NULL; -+ } -+ -+ if (hdr->type != ICMP_DEST_UNREACH -+ && hdr->type != ICMP_SOURCE_QUENCH -+ && hdr->type != ICMP_TIME_EXCEEDED -+ && hdr->type != ICMP_PARAMETERPROB -+ && hdr->type != ICMP_REDIRECT) -+ return NULL; -+ -+ /* Ignore ICMP's containing fragments (shouldn't happen) */ -+ if (inner->frag_off & htons(IP_OFFSET)) { -+ DEBUGP("icmp_error_track: fragment of proto %u\n", -+ inner->protocol); -+ return NULL; -+ } -+ -+ /* Ignore it if the checksum's bogus. */ -+ if (ip_compute_csum((unsigned char *)hdr, sizeof(*hdr) + datalen)) { -+ DEBUGP("icmp_error_track: bad csum\n"); -+ return NULL; -+ } -+ -+ innerproto = ip_ct_find_proto(inner->protocol); -+ /* Are they talking about one of our connections? */ -+ if (inner->ihl * 4 + 8 > datalen -+ || !get_tuple(inner, datalen, &origtuple, innerproto)) { -+ DEBUGP("icmp_error: ! get_tuple p=%u (%u*4+%u dlen=%u)\n", -+ inner->protocol, inner->ihl, 8, -+ datalen); -+ return NULL; -+ } -+ -+ /* Ordinarily, we'd expect the inverted tupleproto, but it's -+ been preserved inside the ICMP. */ -+ if (!invert_tuple(&innertuple, &origtuple, innerproto)) { -+ DEBUGP("icmp_error_track: Can't invert tuple\n"); -+ return NULL; -+ } -+ -+ *ctinfo = IP_CT_RELATED; -+ -+ h = ip_conntrack_find_get(&innertuple, NULL); -+ if (!h) { -+ /* Locally generated ICMPs will match inverted if they -+ haven't been SNAT'ed yet */ -+ /* FIXME: NAT code has to handle half-done double NAT --RR */ -+ if (hooknum == NF_IP_LOCAL_OUT) -+ h = ip_conntrack_find_get(&origtuple, NULL); -+ -+ if (!h) { -+ DEBUGP("icmp_error_track: no match\n"); -+ return NULL; -+ } -+ /* Reverse direction from that found */ -+ if (DIRECTION(h) != IP_CT_DIR_REPLY) -+ *ctinfo += IP_CT_IS_REPLY; -+ } else { -+ if (DIRECTION(h) == IP_CT_DIR_REPLY) -+ *ctinfo += IP_CT_IS_REPLY; -+ } -+ -+ /* Update skb to refer to this connection */ -+ skb->nfct = &h->ctrack->infos[*ctinfo]; -+ return h->ctrack; -+} -+ -+/* There's a small race here where we may free a just-assured -+ connection. Too bad: we're in trouble anyway. */ -+static inline int unreplied(const struct ip_conntrack_tuple_hash *i) -+{ -+ return !(test_bit(IPS_ASSURED_BIT, &i->ctrack->status)); -+} -+ -+static int early_drop(struct list_head *chain) -+{ -+ /* Traverse backwards: gives us oldest, which is roughly LRU */ -+ struct ip_conntrack_tuple_hash *h; -+ int dropped = 0; -+ -+ READ_LOCK(&ip_conntrack_lock); -+ h = LIST_FIND_B(chain, unreplied, struct ip_conntrack_tuple_hash *); -+ if (h) -+ atomic_inc(&h->ctrack->ct_general.use); -+ READ_UNLOCK(&ip_conntrack_lock); -+ -+ if (!h) -+ return dropped; -+ -+ if (del_timer(&h->ctrack->timeout)) { -+ death_by_timeout((unsigned long)h->ctrack); -+ dropped = 1; -+ } -+ ip_conntrack_put(h->ctrack); -+ return dropped; -+} -+ -+static inline int helper_cmp(const struct ip_conntrack_helper *i, -+ const struct ip_conntrack_tuple *rtuple) -+{ -+ return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask); -+} -+ -+struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple) -+{ -+ return LIST_FIND(&helpers, helper_cmp, -+ struct ip_conntrack_helper *, -+ tuple); -+} -+ -+/* Allocate a new conntrack: we return -ENOMEM if classification -+ failed due to stress. Otherwise it really is unclassifiable. */ -+static struct ip_conntrack_tuple_hash * -+init_conntrack(const struct ip_conntrack_tuple *tuple, -+ struct ip_conntrack_protocol *protocol, -+ struct sk_buff *skb) -+{ -+ struct ip_conntrack *conntrack; -+ struct ip_conntrack_tuple repl_tuple; -+ size_t hash; -+ struct ip_conntrack_expect *expected; -+ int i; -+ static unsigned int drop_next = 0; -+ -+ if (!ip_conntrack_hash_rnd_initted) { -+ get_random_bytes(&ip_conntrack_hash_rnd, 4); -+ ip_conntrack_hash_rnd_initted = 1; -+ } -+ -+ hash = hash_conntrack(tuple); -+ -+ if (ip_conntrack_max && -+ atomic_read(&ip_conntrack_count) >= ip_conntrack_max) { -+ /* Try dropping from random chain, or else from the -+ chain about to put into (in case they're trying to -+ bomb one hash chain). */ -+ unsigned int next = (drop_next++)%ip_conntrack_htable_size; -+ -+ if (!early_drop(&ip_conntrack_hash[next]) -+ && !early_drop(&ip_conntrack_hash[hash])) { -+ if (net_ratelimit()) -+ printk(KERN_WARNING -+ "ip_conntrack: table full, dropping" -+ " packet.\n"); -+ return ERR_PTR(-ENOMEM); -+ } -+ } -+ -+ if (!invert_tuple(&repl_tuple, tuple, protocol)) { -+ DEBUGP("Can't invert tuple.\n"); -+ return NULL; -+ } -+ -+ conntrack = kmem_cache_alloc(ip_conntrack_cachep, GFP_ATOMIC); -+ if (!conntrack) { -+ DEBUGP("Can't allocate conntrack.\n"); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ memset(conntrack, 0, sizeof(*conntrack)); -+ atomic_set(&conntrack->ct_general.use, 1); -+ conntrack->ct_general.destroy = destroy_conntrack; -+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple; -+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].ctrack = conntrack; -+ conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple; -+ conntrack->tuplehash[IP_CT_DIR_REPLY].ctrack = conntrack; -+ for (i=0; i < IP_CT_NUMBER; i++) -+ conntrack->infos[i].master = &conntrack->ct_general; -+ -+ if (!protocol->new(conntrack, skb->nh.iph, skb->len)) { -+ kmem_cache_free(ip_conntrack_cachep, conntrack); -+ return NULL; -+ } -+ /* Don't set timer yet: wait for confirmation */ -+ init_timer(&conntrack->timeout); -+ conntrack->timeout.data = (unsigned long)conntrack; -+ conntrack->timeout.function = death_by_timeout; -+ -+ INIT_LIST_HEAD(&conntrack->sibling_list); -+ -+ WRITE_LOCK(&ip_conntrack_lock); -+ /* Need finding and deleting of expected ONLY if we win race */ -+ READ_LOCK(&ip_conntrack_expect_tuple_lock); -+ expected = LIST_FIND(&ip_conntrack_expect_list, expect_cmp, -+ struct ip_conntrack_expect *, tuple); -+ READ_UNLOCK(&ip_conntrack_expect_tuple_lock); -+ -+ /* If master is not in hash table yet (ie. packet hasn't left -+ this machine yet), how can other end know about expected? -+ Hence these are not the droids you are looking for (if -+ master ct never got confirmed, we'd hold a reference to it -+ and weird things would happen to future packets). */ -+ if (expected && !is_confirmed(expected->expectant)) -+ expected = NULL; -+ -+ /* Look up the conntrack helper for master connections only */ -+ if (!expected) -+ conntrack->helper = ip_ct_find_helper(&repl_tuple); -+ -+ /* If the expectation is dying, then this is a looser. */ -+ if (expected -+ && expected->expectant->helper->timeout -+ && ! del_timer(&expected->timeout)) -+ expected = NULL; -+ -+ if (expected) { -+ DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", -+ conntrack, expected); -+ /* Welcome, Mr. Bond. We've been expecting you... */ -+ __set_bit(IPS_EXPECTED_BIT, &conntrack->status); -+ conntrack->master = expected; -+ expected->sibling = conntrack; -+ LIST_DELETE(&ip_conntrack_expect_list, expected); -+ expected->expectant->expecting--; -+ nf_conntrack_get(&master_ct(conntrack)->infos[0]); -+ } -+ atomic_inc(&ip_conntrack_count); -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ -+ if (expected && expected->expectfn) -+ expected->expectfn(conntrack); -+ return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL]; -+} -+ -+/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */ -+static inline struct ip_conntrack * -+resolve_normal_ct(struct sk_buff *skb, -+ struct ip_conntrack_protocol *proto, -+ int *set_reply, -+ unsigned int hooknum, -+ enum ip_conntrack_info *ctinfo) -+{ -+ struct ip_conntrack_tuple tuple; -+ struct ip_conntrack_tuple_hash *h; -+ -+ IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0); -+ -+ if (!get_tuple(skb->nh.iph, skb->len, &tuple, proto)) -+ return NULL; -+ -+ /* look for tuple match */ -+ h = ip_conntrack_find_get(&tuple, NULL); -+ if (!h) { -+ h = init_conntrack(&tuple, proto, skb); -+ if (!h) -+ return NULL; -+ if (IS_ERR(h)) -+ return (void *)h; -+ } -+ -+ /* It exists; we have (non-exclusive) reference. */ -+ if (DIRECTION(h) == IP_CT_DIR_REPLY) { -+ *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY; -+ /* Please set reply bit if this packet OK */ -+ *set_reply = 1; -+ } else { -+ /* Once we've had two way comms, always ESTABLISHED. */ -+ if (test_bit(IPS_SEEN_REPLY_BIT, &h->ctrack->status)) { -+ DEBUGP("ip_conntrack_in: normal packet for %p\n", -+ h->ctrack); -+ *ctinfo = IP_CT_ESTABLISHED; -+ } else if (test_bit(IPS_EXPECTED_BIT, &h->ctrack->status)) { -+ DEBUGP("ip_conntrack_in: related packet for %p\n", -+ h->ctrack); -+ *ctinfo = IP_CT_RELATED; -+ } else { -+ DEBUGP("ip_conntrack_in: new packet for %p\n", -+ h->ctrack); -+ *ctinfo = IP_CT_NEW; -+ } -+ *set_reply = 0; -+ } -+ skb->nfct = &h->ctrack->infos[*ctinfo]; -+ return h->ctrack; -+} -+ -+/* Netfilter hook itself. */ -+unsigned int ip_conntrack_in(unsigned int hooknum, -+ struct sk_buff **pskb, -+ const struct net_device *in, -+ const struct net_device *out, -+ int (*okfn)(struct sk_buff *)) -+{ -+ struct ip_conntrack *ct; -+ enum ip_conntrack_info ctinfo; -+ struct ip_conntrack_protocol *proto; -+ int set_reply; -+ int ret; -+ -+ /* FIXME: Do this right please. --RR */ -+ (*pskb)->nfcache |= NFC_UNKNOWN; -+ -+/* Doesn't cover locally-generated broadcast, so not worth it. */ -+#if 0 -+ /* Ignore broadcast: no `connection'. */ -+ if ((*pskb)->pkt_type == PACKET_BROADCAST) { -+ printk("Broadcast packet!\n"); -+ return NF_ACCEPT; -+ } else if (((*pskb)->nh.iph->daddr & htonl(0x000000FF)) -+ == htonl(0x000000FF)) { -+ printk("Should bcast: %u.%u.%u.%u->%u.%u.%u.%u (sk=%p, ptype=%u)\n", -+ NIPQUAD((*pskb)->nh.iph->saddr), -+ NIPQUAD((*pskb)->nh.iph->daddr), -+ (*pskb)->sk, (*pskb)->pkt_type); -+ } -+#endif -+ -+ /* Previously seen (loopback)? Ignore. Do this before -+ fragment check. */ -+ if ((*pskb)->nfct) -+ return NF_ACCEPT; -+ -+ /* Gather fragments. */ -+ if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { -+ *pskb = ip_ct_gather_frags(*pskb); -+ if (!*pskb) -+ return NF_STOLEN; -+ } -+ -+ proto = ip_ct_find_proto((*pskb)->nh.iph->protocol); -+ -+ /* It may be an icmp error... */ -+ if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP -+ && icmp_error_track(*pskb, &ctinfo, hooknum)) -+ return NF_ACCEPT; -+ -+ if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) -+ /* Not valid part of a connection */ -+ return NF_ACCEPT; -+ -+ if (IS_ERR(ct)) -+ /* Too stressed to deal. */ -+ return NF_DROP; -+ -+ IP_NF_ASSERT((*pskb)->nfct); -+ -+ ret = proto->packet(ct, (*pskb)->nh.iph, (*pskb)->len, ctinfo); -+ if (ret == -1) { -+ /* Invalid */ -+ nf_conntrack_put((*pskb)->nfct); -+ (*pskb)->nfct = NULL; -+ return NF_ACCEPT; -+ } -+ -+ if (ret != NF_DROP && ct->helper) { -+ ret = ct->helper->help((*pskb)->nh.iph, (*pskb)->len, -+ ct, ctinfo); -+ if (ret == -1) { -+ /* Invalid */ -+ nf_conntrack_put((*pskb)->nfct); -+ (*pskb)->nfct = NULL; -+ return NF_ACCEPT; -+ } -+ } -+ if (set_reply) -+ set_bit(IPS_SEEN_REPLY_BIT, &ct->status); -+ -+ return ret; -+} -+ -+int invert_tuplepr(struct ip_conntrack_tuple *inverse, -+ const struct ip_conntrack_tuple *orig) -+{ -+ return invert_tuple(inverse, orig, ip_ct_find_proto(orig->dst.protonum)); -+} -+ -+static inline int resent_expect(const struct ip_conntrack_expect *i, -+ const struct ip_conntrack_tuple *tuple, -+ const struct ip_conntrack_tuple *mask) -+{ -+ DEBUGP("resent_expect\n"); -+ DEBUGP(" tuple: "); DUMP_TUPLE(&i->tuple); -+ DEBUGP("ct_tuple: "); DUMP_TUPLE(&i->ct_tuple); -+ DEBUGP("test tuple: "); DUMP_TUPLE(tuple); -+ return (((i->ct_tuple.dst.protonum == 0 && ip_ct_tuple_equal(&i->tuple, tuple)) -+ || (i->ct_tuple.dst.protonum && ip_ct_tuple_equal(&i->ct_tuple, tuple))) -+ && ip_ct_tuple_equal(&i->mask, mask)); -+} -+ -+/* Would two expected things clash? */ -+static inline int expect_clash(const struct ip_conntrack_expect *i, -+ const struct ip_conntrack_tuple *tuple, -+ const struct ip_conntrack_tuple *mask) -+{ -+ /* Part covered by intersection of masks must be unequal, -+ otherwise they clash */ -+ struct ip_conntrack_tuple intersect_mask -+ = { { i->mask.src.ip & mask->src.ip, -+ { i->mask.src.u.all & mask->src.u.all } }, -+ { i->mask.dst.ip & mask->dst.ip, -+ { i->mask.dst.u.all & mask->dst.u.all }, -+ i->mask.dst.protonum & mask->dst.protonum } }; -+ -+ return ip_ct_tuple_mask_cmp(&i->tuple, tuple, &intersect_mask); -+} -+ -+inline void ip_conntrack_unexpect_related(struct ip_conntrack_expect *expect) -+{ -+ WRITE_LOCK(&ip_conntrack_lock); -+ unexpect_related(expect); -+ WRITE_UNLOCK(&ip_conntrack_lock); -+} -+ -+static void expectation_timed_out(unsigned long ul_expect) -+{ -+ struct ip_conntrack_expect *expect = (void *) ul_expect; -+ -+ DEBUGP("expectation %p timed out\n", expect); -+ WRITE_LOCK(&ip_conntrack_lock); -+ __unexpect_related(expect); -+ WRITE_UNLOCK(&ip_conntrack_lock); -+} -+ -+/* Add a related connection. */ -+int ip_conntrack_expect_related(struct ip_conntrack *related_to, -+ struct ip_conntrack_expect *expect) -+{ -+ struct ip_conntrack_expect *old, *new; -+ int ret = 0; -+ -+ WRITE_LOCK(&ip_conntrack_lock); -+ /* Because of the write lock, no reader can walk the lists, -+ * 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); -+ -+ old = LIST_FIND(&ip_conntrack_expect_list, resent_expect, -+ struct ip_conntrack_expect *, &expect->tuple, -+ &expect->mask); -+ if (old) { -+ /* Helper private data may contain offsets but no pointers -+ pointing into the payload - otherwise we should have to copy -+ the data filled out by the helper over the old one */ -+ DEBUGP("expect_related: resent packet\n"); -+ if (related_to->helper->timeout) { -+ if (!del_timer(&old->timeout)) { -+ /* expectation is dying. Fall through */ -+ old = NULL; -+ } else { -+ old->timeout.expires = jiffies + -+ related_to->helper->timeout * HZ; -+ add_timer(&old->timeout); -+ } -+ } -+ -+ if (old) { -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ return -EEXIST; -+ } -+ } else if (related_to->helper->max_expected && -+ related_to->expecting >= related_to->helper->max_expected) { -+ /* old == NULL */ -+ if (!(related_to->helper->flags & -+ IP_CT_HELPER_F_REUSE_EXPECT)) { -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ if (net_ratelimit()) -+ printk(KERN_WARNING -+ "ip_conntrack: max number of expected " -+ "connections %i of %s reached for " -+ "%u.%u.%u.%u->%u.%u.%u.%u\n", -+ related_to->helper->max_expected, -+ related_to->helper->name, -+ NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), -+ NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip)); -+ return -EPERM; -+ } -+ DEBUGP("ip_conntrack: max number of expected " -+ "connections %i of %s reached for " -+ "%u.%u.%u.%u->%u.%u.%u.%u, reusing\n", -+ related_to->helper->max_expected, -+ related_to->helper->name, -+ NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), -+ NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip)); -+ -+ /* choose the the oldest expectation to evict */ -+ list_for_each_entry(old, &related_to->sibling_list, -+ expected_list) -+ if (old->sibling == NULL) -+ break; -+ -+ /* We cannot fail since related_to->expecting is the number -+ * of unconfirmed expectations */ -+ IP_NF_ASSERT(old && old->sibling == NULL); -+ -+ /* newnat14 does not reuse the real allocated memory -+ * structures but rather unexpects the old and -+ * allocates a new. unexpect_related will decrement -+ * related_to->expecting. -+ */ -+ unexpect_related(old); -+ ret = -EPERM; -+ } else if (LIST_FIND(&ip_conntrack_expect_list, expect_clash, -+ struct ip_conntrack_expect *, &expect->tuple, -+ &expect->mask)) { -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ DEBUGP("expect_related: busy!\n"); -+ return -EBUSY; -+ } -+ -+ new = (struct ip_conntrack_expect *) -+ kmalloc(sizeof(struct ip_conntrack_expect), GFP_ATOMIC); -+ if (!new) { -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ DEBUGP("expect_relaed: OOM allocating expect\n"); -+ return -ENOMEM; -+ } -+ -+ DEBUGP("new expectation %p of conntrack %p\n", new, related_to); -+ memcpy(new, expect, sizeof(*expect)); -+ new->expectant = related_to; -+ new->sibling = NULL; -+ atomic_set(&new->use, 1); -+ -+ /* add to expected list for this connection */ -+ list_add_tail(&new->expected_list, &related_to->sibling_list); -+ /* add to global list of expectations */ -+ list_prepend(&ip_conntrack_expect_list, &new->list); -+ /* add and start timer if required */ -+ if (related_to->helper->timeout) { -+ init_timer(&new->timeout); -+ new->timeout.data = (unsigned long)new; -+ new->timeout.function = expectation_timed_out; -+ new->timeout.expires = jiffies + -+ related_to->helper->timeout * HZ; -+ add_timer(&new->timeout); -+ } -+ related_to->expecting++; -+ -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ -+ return ret; -+} -+ -+/* Change tuple in an existing expectation */ -+int ip_conntrack_change_expect(struct ip_conntrack_expect *expect, -+ struct ip_conntrack_tuple *newtuple) -+{ -+ int ret; -+ -+ 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); -+ 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) -+ && LIST_FIND(&ip_conntrack_expect_list, expect_clash, -+ struct ip_conntrack_expect *, newtuple, &expect->mask)) { -+ /* Force NAT to find an unused tuple */ -+ ret = -1; -+ } else { -+ memcpy(&expect->ct_tuple, &expect->tuple, sizeof(expect->tuple)); -+ memcpy(&expect->tuple, newtuple, sizeof(expect->tuple)); -+ ret = 0; -+ } -+ } else { -+ /* Resent packet */ -+ DEBUGP("change expect: resent packet\n"); -+ if (ip_ct_tuple_equal(&expect->tuple, newtuple)) { -+ ret = 0; -+ } else { -+ /* Force NAT to choose again the same port */ -+ ret = -1; -+ } -+ } -+ WRITE_UNLOCK(&ip_conntrack_expect_tuple_lock); -+ -+ return ret; -+} -+ -+/* Alter reply tuple (maybe alter helper). If it's already taken, -+ return 0 and don't do alteration. */ -+int ip_conntrack_alter_reply(struct ip_conntrack *conntrack, -+ const struct ip_conntrack_tuple *newreply) -+{ -+ WRITE_LOCK(&ip_conntrack_lock); -+ if (__ip_conntrack_find(newreply, conntrack)) { -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ return 0; -+ } -+ /* Should be unconfirmed, so not in hash table yet */ -+ IP_NF_ASSERT(!is_confirmed(conntrack)); -+ -+ DEBUGP("Altering reply tuple of %p to ", conntrack); -+ DUMP_TUPLE(newreply); -+ -+ conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; -+ if (!conntrack->master && list_empty(&conntrack->sibling_list)) -+ conntrack->helper = ip_ct_find_helper(newreply); -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ -+ return 1; -+} -+ -+int ip_conntrack_helper_register(struct ip_conntrack_helper *me) -+{ -+ MOD_INC_USE_COUNT; -+ -+ WRITE_LOCK(&ip_conntrack_lock); -+ list_prepend(&helpers, me); -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ -+ return 0; -+} -+ -+static inline int unhelp(struct ip_conntrack_tuple_hash *i, -+ const struct ip_conntrack_helper *me) -+{ -+ if (i->ctrack->helper == me) { -+ /* Get rid of any expected. */ -+ remove_expectations(i->ctrack, 0); -+ /* And *then* set helper to NULL */ -+ i->ctrack->helper = NULL; -+ } -+ return 0; -+} -+ -+void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me) -+{ -+ unsigned int i; -+ -+ /* Need write lock here, to delete helper. */ -+ WRITE_LOCK(&ip_conntrack_lock); -+ LIST_DELETE(&helpers, me); -+ -+ /* Get rid of expecteds, set helpers to NULL. */ -+ for (i = 0; i < ip_conntrack_htable_size; i++) -+ LIST_FIND_W(&ip_conntrack_hash[i], unhelp, -+ struct ip_conntrack_tuple_hash *, me); -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ -+ /* Someone could be still looking at the helper in a bh. */ -+ br_write_lock_bh(BR_NETPROTO_LOCK); -+ br_write_unlock_bh(BR_NETPROTO_LOCK); -+ -+ MOD_DEC_USE_COUNT; -+} -+ -+/* Refresh conntrack for this many jiffies. */ -+void ip_ct_refresh(struct ip_conntrack *ct, unsigned long extra_jiffies) -+{ -+ IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct); -+ -+ WRITE_LOCK(&ip_conntrack_lock); -+ /* If not in hash table, timer will not be active yet */ -+ if (!is_confirmed(ct)) -+ ct->timeout.expires = extra_jiffies; -+ else { -+ /* Need del_timer for race avoidance (may already be dying). */ -+ if (del_timer(&ct->timeout)) { -+ ct->timeout.expires = jiffies + extra_jiffies; -+ add_timer(&ct->timeout); -+ } -+ } -+ WRITE_UNLOCK(&ip_conntrack_lock); -+} -+ -+/* Returns new sk_buff, or NULL */ -+struct sk_buff * -+ip_ct_gather_frags(struct sk_buff *skb) -+{ -+ struct sock *sk = skb->sk; -+#ifdef CONFIG_NETFILTER_DEBUG -+ unsigned int olddebug = skb->nf_debug; -+#endif -+ if (sk) { -+ sock_hold(sk); -+ skb_orphan(skb); -+ } -+ -+ local_bh_disable(); -+ skb = ip_defrag(skb); -+ local_bh_enable(); -+ -+ if (!skb) { -+ if (sk) sock_put(sk); -+ return skb; -+ } else if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) { -+ kfree_skb(skb); -+ if (sk) sock_put(sk); -+ return NULL; -+ } -+ -+ if (sk) { -+ skb_set_owner_w(skb, sk); -+ sock_put(sk); -+ } -+ -+ ip_send_check(skb->nh.iph); -+ skb->nfcache |= NFC_ALTERED; -+#ifdef CONFIG_NETFILTER_DEBUG -+ /* Packet path as if nothing had happened. */ -+ skb->nf_debug = olddebug; -+#endif -+ return skb; -+} -+ -+/* Used by ipt_REJECT. */ -+static void ip_conntrack_attach(struct sk_buff *nskb, struct nf_ct_info *nfct) -+{ -+ struct ip_conntrack *ct; -+ enum ip_conntrack_info ctinfo; -+ -+ ct = __ip_conntrack_get(nfct, &ctinfo); -+ -+ /* This ICMP is in reverse direction to the packet which -+ caused it */ -+ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) -+ ctinfo = IP_CT_RELATED + IP_CT_IS_REPLY; -+ else -+ ctinfo = IP_CT_RELATED; -+ -+ /* Attach new skbuff, and increment count */ -+ nskb->nfct = &ct->infos[ctinfo]; -+ atomic_inc(&ct->ct_general.use); -+} -+ -+static inline int -+do_kill(const struct ip_conntrack_tuple_hash *i, -+ int (*kill)(const struct ip_conntrack *i, void *data), -+ void *data) -+{ -+ return kill(i->ctrack, data); -+} -+ -+/* Bring out ya dead! */ -+static struct ip_conntrack_tuple_hash * -+get_next_corpse(int (*kill)(const struct ip_conntrack *i, void *data), -+ void *data, unsigned int *bucket) -+{ -+ struct ip_conntrack_tuple_hash *h = NULL; -+ -+ READ_LOCK(&ip_conntrack_lock); -+ for (; !h && *bucket < ip_conntrack_htable_size; (*bucket)++) { -+ h = LIST_FIND(&ip_conntrack_hash[*bucket], do_kill, -+ struct ip_conntrack_tuple_hash *, kill, data); -+ } -+ if (h) -+ atomic_inc(&h->ctrack->ct_general.use); -+ READ_UNLOCK(&ip_conntrack_lock); -+ -+ return h; -+} -+ -+void -+ip_ct_selective_cleanup(int (*kill)(const struct ip_conntrack *i, void *data), -+ void *data) -+{ -+ struct ip_conntrack_tuple_hash *h; -+ unsigned int bucket = 0; -+ -+ while ((h = get_next_corpse(kill, data, &bucket)) != NULL) { -+ /* Time to push up daises... */ -+ if (del_timer(&h->ctrack->timeout)) -+ death_by_timeout((unsigned long)h->ctrack); -+ /* ... else the timer will get him soon. */ -+ -+ ip_conntrack_put(h->ctrack); -+ } -+} -+ -+/* Fast function for those who don't want to parse /proc (and I don't -+ blame them). */ -+/* Reversing the socket's dst/src point of view gives us the reply -+ mapping. */ -+static int -+getorigdst(struct sock *sk, int optval, void *user, int *len) -+{ -+ struct ip_conntrack_tuple_hash *h; -+ struct ip_conntrack_tuple tuple; -+ -+ IP_CT_TUPLE_U_BLANK(&tuple); -+ tuple.src.ip = sk->rcv_saddr; -+ tuple.src.u.tcp.port = sk->sport; -+ tuple.dst.ip = sk->daddr; -+ tuple.dst.u.tcp.port = sk->dport; -+ tuple.dst.protonum = IPPROTO_TCP; -+ -+ /* We only do TCP at the moment: is there a better way? */ -+ if (strcmp(sk->prot->name, "TCP") != 0) { -+ DEBUGP("SO_ORIGINAL_DST: Not a TCP socket\n"); -+ return -ENOPROTOOPT; -+ } -+ -+ if ((unsigned int) *len < sizeof(struct sockaddr_in)) { -+ DEBUGP("SO_ORIGINAL_DST: len %u not %u\n", -+ *len, sizeof(struct sockaddr_in)); -+ return -EINVAL; -+ } -+ -+ h = ip_conntrack_find_get(&tuple, NULL); -+ if (h) { -+ struct sockaddr_in sin; -+ -+ sin.sin_family = AF_INET; -+ sin.sin_port = h->ctrack->tuplehash[IP_CT_DIR_ORIGINAL] -+ .tuple.dst.u.tcp.port; -+ sin.sin_addr.s_addr = h->ctrack->tuplehash[IP_CT_DIR_ORIGINAL] -+ .tuple.dst.ip; -+ -+ DEBUGP("SO_ORIGINAL_DST: %u.%u.%u.%u %u\n", -+ NIPQUAD(sin.sin_addr.s_addr), ntohs(sin.sin_port)); -+ ip_conntrack_put(h->ctrack); -+ if (copy_to_user(user, &sin, sizeof(sin)) != 0) -+ return -EFAULT; -+ else -+ return 0; -+ } -+ DEBUGP("SO_ORIGINAL_DST: Can't find %u.%u.%u.%u/%u-%u.%u.%u.%u/%u.\n", -+ NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port), -+ NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port)); -+ return -ENOENT; -+} -+ -+static struct nf_sockopt_ops so_getorigdst -+= { { NULL, NULL }, PF_INET, -+ 0, 0, NULL, /* Setsockopts */ -+ SO_ORIGINAL_DST, SO_ORIGINAL_DST+1, &getorigdst, -+ 0, NULL }; -+ -+static int kill_all(const struct ip_conntrack *i, void *data) -+{ -+ return 1; -+} -+ -+/* Mishearing the voices in his head, our hero wonders how he's -+ supposed to kill the mall. */ -+void ip_conntrack_cleanup(void) -+{ -+ ip_ct_attach = NULL; -+ /* This makes sure all current packets have passed through -+ netfilter framework. Roll on, two-stage module -+ delete... */ -+ br_write_lock_bh(BR_NETPROTO_LOCK); -+ br_write_unlock_bh(BR_NETPROTO_LOCK); -+ -+ i_see_dead_people: -+ ip_ct_selective_cleanup(kill_all, NULL); -+ if (atomic_read(&ip_conntrack_count) != 0) { -+ schedule(); -+ goto i_see_dead_people; -+ } -+ -+ kmem_cache_destroy(ip_conntrack_cachep); -+ vfree(ip_conntrack_hash); -+ nf_unregister_sockopt(&so_getorigdst); -+} -+ -+static int hashsize = 0; -+MODULE_PARM(hashsize, "i"); -+ -+int __init ip_conntrack_init(void) -+{ -+ unsigned int i; -+ int ret; -+ -+ /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB -+ * machine has 256 buckets. >= 1GB machines have 8192 buckets. */ -+ if (hashsize) { -+ ip_conntrack_htable_size = hashsize; -+ } else { -+ ip_conntrack_htable_size -+ = (((num_physpages << PAGE_SHIFT) / 16384) -+ / sizeof(struct list_head)); -+ if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE)) -+ ip_conntrack_htable_size = 8192; -+ if (ip_conntrack_htable_size < 16) -+ ip_conntrack_htable_size = 16; -+ } -+ ip_conntrack_max = 8 * ip_conntrack_htable_size; -+ -+ printk("ip_conntrack version %s (%u buckets, %d max)" -+ " - %Zd bytes per conntrack\n", IP_CONNTRACK_VERSION, -+ ip_conntrack_htable_size, ip_conntrack_max, -+ sizeof(struct ip_conntrack)); -+ -+ ret = nf_register_sockopt(&so_getorigdst); -+ if (ret != 0) { -+ printk(KERN_ERR "Unable to register netfilter socket option\n"); -+ return ret; -+ } -+ -+ ip_conntrack_hash = vmalloc(sizeof(struct list_head) -+ * ip_conntrack_htable_size); -+ if (!ip_conntrack_hash) { -+ printk(KERN_ERR "Unable to create ip_conntrack_hash\n"); -+ goto err_unreg_sockopt; -+ } -+ -+ ip_conntrack_cachep = kmem_cache_create("ip_conntrack", -+ sizeof(struct ip_conntrack), 0, -+ SLAB_HWCACHE_ALIGN, NULL, NULL); -+ if (!ip_conntrack_cachep) { -+ printk(KERN_ERR "Unable to create ip_conntrack slab cache\n"); -+ goto err_free_hash; -+ } -+ /* Don't NEED lock here, but good form anyway. */ -+ WRITE_LOCK(&ip_conntrack_lock); -+ /* Sew in builtin protocols. */ -+ list_append(&protocol_list, &ip_conntrack_protocol_tcp); -+ list_append(&protocol_list, &ip_conntrack_protocol_udp); -+ list_append(&protocol_list, &ip_conntrack_protocol_icmp); -+ WRITE_UNLOCK(&ip_conntrack_lock); -+ -+ for (i = 0; i < ip_conntrack_htable_size; i++) -+ INIT_LIST_HEAD(&ip_conntrack_hash[i]); -+ -+ /* For use by ipt_REJECT */ -+ ip_ct_attach = ip_conntrack_attach; -+ return ret; -+ -+err_free_hash: -+ vfree(ip_conntrack_hash); -+err_unreg_sockopt: -+ nf_unregister_sockopt(&so_getorigdst); -+ -+ return -ENOMEM; -+} -diff -uNr linux_org/net/ipv4/netfilter/ip_conntrack_pptp.c linux/net/ipv4/netfilter/ip_conntrack_pptp.c ---- linux_org/net/ipv4/netfilter/ip_conntrack_pptp.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/ipv4/netfilter/ip_conntrack_pptp.c 2006-10-27 14:11:52.000000000 +0200 -@@ -0,0 +1,637 @@ -+/* -+ * 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; -+ -+ /* 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(exp->sibling, 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.u.gre.protocol = __constant_htons(GRE_PROTOCOL_PPTP); -+ exp.tuple.dst.u.gre.version = GRE_VERSION_PPTP; -+ 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.u.gre.version = 0xff; -+ exp.mask.dst.u.gre.protocol = 0xffff; -+ 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); -diff -uNr linux_org/net/ipv4/netfilter/ip_conntrack_pptp_priv.h linux/net/ipv4/netfilter/ip_conntrack_pptp_priv.h ---- linux_org/net/ipv4/netfilter/ip_conntrack_pptp_priv.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/ipv4/netfilter/ip_conntrack_pptp_priv.h 2006-10-27 14:11:52.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 -uNr linux_org/net/ipv4/netfilter/ip_conntrack_proto_gre.c linux/net/ipv4/netfilter/ip_conntrack_proto_gre.c ---- linux_org/net/ipv4/netfilter/ip_conntrack_proto_gre.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/ipv4/netfilter/ip_conntrack_proto_gre.c 2006-10-27 14:11:52.000000000 +0200 -@@ -0,0 +1,343 @@ -+/* -+ * 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), \ -+ (x)->dst.u.gre.version, \ -+ ntohs((x)->dst.u.gre.protocol)) -+#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.protocol = orig->dst.u.gre.protocol; -+ tuple->dst.u.gre.version = orig->dst.u.gre.version; -+ -+ 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 */ -+ -+ tuple->dst.u.gre.version = grehdr->version; -+ tuple->dst.u.gre.protocol = grehdr->protocol; -+ -+ 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", -+ tuple->dst.u.gre.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, "version=%d protocol=0x%04x srckey=0x%x dstkey=0x%x ", -+ tuple->dst.u.gre.version, -+ ntohs(tuple->dst.u.gre.protocol), -+ 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(ct, ct->proto.gre.stream_timeout); -+ /* Also, more likely to be important, and not a probe. */ -+ set_bit(IPS_ASSURED_BIT, &ct->status); -+ } else -+ ip_ct_refresh(ct, ct->proto.gre.timeout); -+ -+ return NF_ACCEPT; -+} -+ -+/* Called when a new connection for this protocol found. */ -+static int gre_new(struct ip_conntrack *ct, -+ struct iphdr *iph, size_t len) -+{ -+ DEBUGP(": "); -+ DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); -+ -+ /* initialize to sane value. Ideally a conntrack helper -+ * (e.g. in case of pptp) is increasing them */ -+ ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT; -+ ct->proto.gre.timeout = GRE_TIMEOUT; -+ -+ 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); -diff -uNr linux_org/net/ipv4/netfilter/ip_nat_core.c linux/net/ipv4/netfilter/ip_nat_core.c ---- linux_org/net/ipv4/netfilter/ip_nat_core.c 2004-11-24 12:14:04.000000000 +0100 -+++ linux/net/ipv4/netfilter/ip_nat_core.c 2006-10-27 14:11:52.000000000 +0200 -@@ -430,7 +430,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. */ - -@@ -572,9 +572,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 -uNr linux_org/net/ipv4/netfilter/ip_nat_core.c.orig linux/net/ipv4/netfilter/ip_nat_core.c.orig ---- linux_org/net/ipv4/netfilter/ip_nat_core.c.orig 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/ipv4/netfilter/ip_nat_core.c.orig 2004-11-24 12:14:04.000000000 +0100 -@@ -0,0 +1,1014 @@ -+/* NAT for netfilter; shared with compatibility layer. */ -+ -+/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General -+ Public Licence. */ -+#include <linux/version.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/timer.h> -+#include <linux/skbuff.h> -+#include <linux/netfilter_ipv4.h> -+#include <linux/brlock.h> -+#include <linux/vmalloc.h> -+#include <net/checksum.h> -+#include <net/icmp.h> -+#include <net/ip.h> -+#include <net/tcp.h> /* For tcp_prot in getorigdst */ -+ -+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock) -+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock) -+ -+#include <linux/netfilter_ipv4/ip_conntrack.h> -+#include <linux/netfilter_ipv4/ip_conntrack_core.h> -+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h> -+#include <linux/netfilter_ipv4/ip_nat.h> -+#include <linux/netfilter_ipv4/ip_nat_protocol.h> -+#include <linux/netfilter_ipv4/ip_nat_core.h> -+#include <linux/netfilter_ipv4/ip_nat_helper.h> -+#include <linux/netfilter_ipv4/ip_conntrack_helper.h> -+#include <linux/netfilter_ipv4/listhelp.h> -+ -+#if 0 -+#define DEBUGP printk -+#else -+#define DEBUGP(format, args...) -+#endif -+ -+DECLARE_RWLOCK(ip_nat_lock); -+DECLARE_RWLOCK_EXTERN(ip_conntrack_lock); -+ -+/* Calculated at init based on memory size */ -+static unsigned int ip_nat_htable_size; -+ -+static struct list_head *bysource; -+static struct list_head *byipsproto; -+LIST_HEAD(protos); -+LIST_HEAD(helpers); -+ -+extern struct ip_nat_protocol unknown_nat_protocol; -+ -+/* We keep extra hashes for each conntrack, for fast searching. */ -+static inline size_t -+hash_by_ipsproto(u_int32_t src, u_int32_t dst, u_int16_t proto) -+{ -+ /* Modified src and dst, to ensure we don't create two -+ identical streams. */ -+ return (src + dst + proto) % ip_nat_htable_size; -+} -+ -+static inline size_t -+hash_by_src(const struct ip_conntrack_manip *manip, u_int16_t proto) -+{ -+ /* Original src, to ensure we map it consistently if poss. */ -+ return (manip->ip + manip->u.all + proto) % ip_nat_htable_size; -+} -+ -+/* Noone using conntrack by the time this called. */ -+static void ip_nat_cleanup_conntrack(struct ip_conntrack *conn) -+{ -+ struct ip_nat_info *info = &conn->nat.info; -+ unsigned int hs, hp; -+ -+ if (!info->initialized) -+ return; -+ -+ IP_NF_ASSERT(info->bysource.conntrack); -+ IP_NF_ASSERT(info->byipsproto.conntrack); -+ -+ hs = hash_by_src(&conn->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src, -+ conn->tuplehash[IP_CT_DIR_ORIGINAL] -+ .tuple.dst.protonum); -+ -+ hp = hash_by_ipsproto(conn->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip, -+ conn->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip, -+ conn->tuplehash[IP_CT_DIR_REPLY] -+ .tuple.dst.protonum); -+ -+ WRITE_LOCK(&ip_nat_lock); -+ LIST_DELETE(&bysource[hs], &info->bysource); -+ LIST_DELETE(&byipsproto[hp], &info->byipsproto); -+ WRITE_UNLOCK(&ip_nat_lock); -+} -+ -+/* We do checksum mangling, so if they were wrong before they're still -+ * wrong. Also works for incomplete packets (eg. ICMP dest -+ * unreachables.) */ -+u_int16_t -+ip_nat_cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck) -+{ -+ u_int32_t diffs[] = { oldvalinv, newval }; -+ return csum_fold(csum_partial((char *)diffs, sizeof(diffs), -+ oldcheck^0xFFFF)); -+} -+ -+static inline int cmp_proto(const struct ip_nat_protocol *i, int proto) -+{ -+ return i->protonum == proto; -+} -+ -+struct ip_nat_protocol * -+find_nat_proto(u_int16_t protonum) -+{ -+ struct ip_nat_protocol *i; -+ -+ MUST_BE_READ_LOCKED(&ip_nat_lock); -+ i = LIST_FIND(&protos, cmp_proto, struct ip_nat_protocol *, protonum); -+ if (!i) -+ i = &unknown_nat_protocol; -+ return i; -+} -+ -+/* Is this tuple already taken? (not by us) */ -+int -+ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, -+ const struct ip_conntrack *ignored_conntrack) -+{ -+ /* Conntrack tracking doesn't keep track of outgoing tuples; only -+ incoming ones. NAT means they don't have a fixed mapping, -+ so we invert the tuple and look for the incoming reply. -+ -+ We could keep a separate hash if this proves too slow. */ -+ struct ip_conntrack_tuple reply; -+ -+ invert_tuplepr(&reply, tuple); -+ return ip_conntrack_tuple_taken(&reply, ignored_conntrack); -+} -+ -+/* Does tuple + the source manip come within the range mr */ -+static int -+in_range(const struct ip_conntrack_tuple *tuple, -+ const struct ip_conntrack_manip *manip, -+ const struct ip_nat_multi_range *mr) -+{ -+ struct ip_nat_protocol *proto = find_nat_proto(tuple->dst.protonum); -+ unsigned int i; -+ struct ip_conntrack_tuple newtuple = { *manip, tuple->dst }; -+ -+ for (i = 0; i < mr->rangesize; i++) { -+ /* If we are allowed to map IPs, then we must be in the -+ range specified, otherwise we must be unchanged. */ -+ if (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS) { -+ if (ntohl(newtuple.src.ip) < ntohl(mr->range[i].min_ip) -+ || (ntohl(newtuple.src.ip) -+ > ntohl(mr->range[i].max_ip))) -+ continue; -+ } else { -+ if (newtuple.src.ip != tuple->src.ip) -+ continue; -+ } -+ -+ if ((mr->range[i].flags & IP_NAT_RANGE_PROTO_SPECIFIED) -+ && proto->in_range(&newtuple, IP_NAT_MANIP_SRC, -+ &mr->range[i].min, &mr->range[i].max)) -+ return 1; -+ } -+ return 0; -+} -+ -+static inline int -+src_cmp(const struct ip_nat_hash *i, -+ const struct ip_conntrack_tuple *tuple, -+ const struct ip_nat_multi_range *mr) -+{ -+ return (i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum -+ == tuple->dst.protonum -+ && i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip -+ == tuple->src.ip -+ && i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.all -+ == tuple->src.u.all -+ && in_range(tuple, -+ &i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL] -+ .tuple.src, -+ mr)); -+} -+ -+/* Only called for SRC manip */ -+static struct ip_conntrack_manip * -+find_appropriate_src(const struct ip_conntrack_tuple *tuple, -+ const struct ip_nat_multi_range *mr) -+{ -+ unsigned int h = hash_by_src(&tuple->src, tuple->dst.protonum); -+ struct ip_nat_hash *i; -+ -+ MUST_BE_READ_LOCKED(&ip_nat_lock); -+ i = LIST_FIND(&bysource[h], src_cmp, struct ip_nat_hash *, tuple, mr); -+ if (i) -+ return &i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src; -+ else -+ return NULL; -+} -+ -+#ifdef CONFIG_IP_NF_NAT_LOCAL -+/* If it's really a local destination manip, it may need to do a -+ source manip too. */ -+static int -+do_extra_mangle(u_int32_t var_ip, u_int32_t *other_ipp) -+{ -+ struct rtable *rt; -+ -+ /* FIXME: IPTOS_TOS(iph->tos) --RR */ -+ if (ip_route_output(&rt, var_ip, 0, 0, 0) != 0) { -+ DEBUGP("do_extra_mangle: Can't get route to %u.%u.%u.%u\n", -+ NIPQUAD(var_ip)); -+ return 0; -+ } -+ -+ *other_ipp = rt->rt_src; -+ ip_rt_put(rt); -+ return 1; -+} -+#endif -+ -+/* Simple way to iterate through all. */ -+static inline int fake_cmp(const struct ip_nat_hash *i, -+ u_int32_t src, u_int32_t dst, u_int16_t protonum, -+ unsigned int *score, -+ const struct ip_conntrack *conntrack) -+{ -+ /* Compare backwards: we're dealing with OUTGOING tuples, and -+ inside the conntrack is the REPLY tuple. Don't count this -+ conntrack. */ -+ if (i->conntrack != conntrack -+ && i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip == dst -+ && i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip == src -+ && (i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum -+ == protonum)) -+ (*score)++; -+ return 0; -+} -+ -+static inline unsigned int -+count_maps(u_int32_t src, u_int32_t dst, u_int16_t protonum, -+ const struct ip_conntrack *conntrack) -+{ -+ unsigned int score = 0; -+ unsigned int h; -+ -+ MUST_BE_READ_LOCKED(&ip_nat_lock); -+ h = hash_by_ipsproto(src, dst, protonum); -+ LIST_FIND(&byipsproto[h], fake_cmp, struct ip_nat_hash *, -+ src, dst, protonum, &score, conntrack); -+ -+ return score; -+} -+ -+/* For [FUTURE] fragmentation handling, we want the least-used -+ src-ip/dst-ip/proto triple. Fairness doesn't come into it. Thus -+ if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports -+ 1-65535, we don't do pro-rata allocation based on ports; we choose -+ the ip with the lowest src-ip/dst-ip/proto usage. -+ -+ If an allocation then fails (eg. all 6 ports used in the 1.2.3.4 -+ range), we eliminate that and try again. This is not the most -+ efficient approach, but if you're worried about that, don't hand us -+ ranges you don't really have. */ -+static struct ip_nat_range * -+find_best_ips_proto(struct ip_conntrack_tuple *tuple, -+ const struct ip_nat_multi_range *mr, -+ const struct ip_conntrack *conntrack, -+ unsigned int hooknum) -+{ -+ unsigned int i; -+ struct { -+ const struct ip_nat_range *range; -+ unsigned int score; -+ struct ip_conntrack_tuple tuple; -+ } best = { NULL, 0xFFFFFFFF }; -+ u_int32_t *var_ipp, *other_ipp, saved_ip, orig_dstip; -+ static unsigned int randomness = 0; -+ -+ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) { -+ var_ipp = &tuple->src.ip; -+ saved_ip = tuple->dst.ip; -+ other_ipp = &tuple->dst.ip; -+ } else { -+ var_ipp = &tuple->dst.ip; -+ saved_ip = tuple->src.ip; -+ other_ipp = &tuple->src.ip; -+ } -+ /* Don't do do_extra_mangle unless neccessary (overrides -+ explicit socket bindings, for example) */ -+ orig_dstip = tuple->dst.ip; -+ -+ IP_NF_ASSERT(mr->rangesize >= 1); -+ for (i = 0; i < mr->rangesize; i++) { -+ /* Host order */ -+ u_int32_t minip, maxip, j; -+ -+ /* Don't do ranges which are already eliminated. */ -+ if (mr->range[i].flags & IP_NAT_RANGE_FULL) { -+ continue; -+ } -+ -+ if (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS) { -+ minip = ntohl(mr->range[i].min_ip); -+ maxip = ntohl(mr->range[i].max_ip); -+ } else -+ minip = maxip = ntohl(*var_ipp); -+ -+ randomness++; -+ for (j = 0; j < maxip - minip + 1; j++) { -+ unsigned int score; -+ -+ *var_ipp = htonl(minip + (randomness + j) -+ % (maxip - minip + 1)); -+ -+ /* Reset the other ip in case it was mangled by -+ * do_extra_mangle last time. */ -+ *other_ipp = saved_ip; -+ -+#ifdef CONFIG_IP_NF_NAT_LOCAL -+ if (hooknum == NF_IP_LOCAL_OUT -+ && *var_ipp != orig_dstip -+ && !do_extra_mangle(*var_ipp, other_ipp)) { -+ DEBUGP("Range %u %u.%u.%u.%u rt failed!\n", -+ i, NIPQUAD(*var_ipp)); -+ /* Can't route? This whole range part is -+ * probably screwed, but keep trying -+ * anyway. */ -+ continue; -+ } -+#endif -+ -+ /* Count how many others map onto this. */ -+ score = count_maps(tuple->src.ip, tuple->dst.ip, -+ tuple->dst.protonum, conntrack); -+ if (score < best.score) { -+ /* Optimization: doesn't get any better than -+ this. */ -+ if (score == 0) -+ return (struct ip_nat_range *) -+ &mr->range[i]; -+ -+ best.score = score; -+ best.tuple = *tuple; -+ best.range = &mr->range[i]; -+ } -+ } -+ } -+ *tuple = best.tuple; -+ -+ /* Discard const. */ -+ return (struct ip_nat_range *)best.range; -+} -+ -+/* Fast version doesn't iterate through hash chains, but only handles -+ common case of single IP address (null NAT, masquerade) */ -+static struct ip_nat_range * -+find_best_ips_proto_fast(struct ip_conntrack_tuple *tuple, -+ const struct ip_nat_multi_range *mr, -+ const struct ip_conntrack *conntrack, -+ unsigned int hooknum) -+{ -+ if (mr->rangesize != 1 -+ || (mr->range[0].flags & IP_NAT_RANGE_FULL) -+ || ((mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) -+ && mr->range[0].min_ip != mr->range[0].max_ip)) -+ return find_best_ips_proto(tuple, mr, conntrack, hooknum); -+ -+ if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { -+ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) -+ tuple->src.ip = mr->range[0].min_ip; -+ else { -+ /* Only do extra mangle when required (breaks -+ socket binding) */ -+#ifdef CONFIG_IP_NF_NAT_LOCAL -+ if (tuple->dst.ip != mr->range[0].min_ip -+ && hooknum == NF_IP_LOCAL_OUT -+ && !do_extra_mangle(mr->range[0].min_ip, -+ &tuple->src.ip)) -+ return NULL; -+#endif -+ tuple->dst.ip = mr->range[0].min_ip; -+ } -+ } -+ -+ /* Discard const. */ -+ return (struct ip_nat_range *)&mr->range[0]; -+} -+ -+static int -+get_unique_tuple(struct ip_conntrack_tuple *tuple, -+ const struct ip_conntrack_tuple *orig_tuple, -+ const struct ip_nat_multi_range *mrr, -+ struct ip_conntrack *conntrack, -+ unsigned int hooknum) -+{ -+ struct ip_nat_protocol *proto -+ = find_nat_proto(orig_tuple->dst.protonum); -+ struct ip_nat_range *rptr; -+ unsigned int i; -+ int ret; -+ -+ /* We temporarily use flags for marking full parts, but we -+ always clean up afterwards */ -+ struct ip_nat_multi_range *mr = (void *)mrr; -+ -+ /* 1) If this srcip/proto/src-proto-part is currently mapped, -+ and that same mapping gives a unique tuple within the given -+ range, use that. -+ -+ This is only required for source (ie. NAT/masq) mappings. -+ So far, we don't do local source mappings, so multiple -+ manips not an issue. */ -+ if (hooknum == NF_IP_POST_ROUTING) { -+ struct ip_conntrack_manip *manip; -+ -+ manip = find_appropriate_src(orig_tuple, mr); -+ if (manip) { -+ /* Apply same source manipulation. */ -+ *tuple = ((struct ip_conntrack_tuple) -+ { *manip, orig_tuple->dst }); -+ DEBUGP("get_unique_tuple: Found current src map\n"); -+ return 1; -+ } -+ } -+ -+ /* 2) Select the least-used IP/proto combination in the given -+ range. -+ */ -+ *tuple = *orig_tuple; -+ while ((rptr = find_best_ips_proto_fast(tuple, mr, conntrack, hooknum)) -+ != NULL) { -+ DEBUGP("Found best for "); DUMP_TUPLE(tuple); -+ /* 3) The per-protocol part of the manip is made to -+ map into the range to make a unique tuple. */ -+ -+ /* Only bother mapping if it's not already in range -+ and unique */ -+ if ((!(rptr->flags & IP_NAT_RANGE_PROTO_SPECIFIED) -+ || proto->in_range(tuple, HOOK2MANIP(hooknum), -+ &rptr->min, &rptr->max)) -+ && !ip_nat_used_tuple(tuple, conntrack)) { -+ ret = 1; -+ goto clear_fulls; -+ } else { -+ if (proto->unique_tuple(tuple, rptr, -+ HOOK2MANIP(hooknum), -+ conntrack)) { -+ /* Must be unique. */ -+ IP_NF_ASSERT(!ip_nat_used_tuple(tuple, -+ conntrack)); -+ ret = 1; -+ goto clear_fulls; -+ } else if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { -+ /* Try implicit source NAT; protocol -+ may be able to play with ports to -+ make it unique. */ -+ struct ip_nat_range r -+ = { IP_NAT_RANGE_MAP_IPS, -+ tuple->src.ip, tuple->src.ip, -+ { 0 }, { 0 } }; -+ DEBUGP("Trying implicit mapping\n"); -+ if (proto->unique_tuple(tuple, &r, -+ IP_NAT_MANIP_SRC, -+ conntrack)) { -+ /* Must be unique. */ -+ IP_NF_ASSERT(!ip_nat_used_tuple -+ (tuple, conntrack)); -+ ret = 1; -+ goto clear_fulls; -+ } -+ } -+ DEBUGP("Protocol can't get unique tuple %u.\n", -+ hooknum); -+ } -+ -+ /* Eliminate that from range, and try again. */ -+ rptr->flags |= IP_NAT_RANGE_FULL; -+ *tuple = *orig_tuple; -+ } -+ -+ ret = 0; -+ -+ clear_fulls: -+ /* Clear full flags. */ -+ IP_NF_ASSERT(mr->rangesize >= 1); -+ for (i = 0; i < mr->rangesize; i++) -+ mr->range[i].flags &= ~IP_NAT_RANGE_FULL; -+ -+ return ret; -+} -+ -+static inline int -+helper_cmp(const struct ip_nat_helper *helper, -+ const struct ip_conntrack_tuple *tuple) -+{ -+ return ip_ct_tuple_mask_cmp(tuple, &helper->tuple, &helper->mask); -+} -+ -+/* Where to manip the reply packets (will be reverse manip). */ -+static unsigned int opposite_hook[NF_IP_NUMHOOKS] -+= { [NF_IP_PRE_ROUTING] = NF_IP_POST_ROUTING, -+ [NF_IP_POST_ROUTING] = NF_IP_PRE_ROUTING, -+#ifdef CONFIG_IP_NF_NAT_LOCAL -+ [NF_IP_LOCAL_OUT] = NF_IP_LOCAL_IN, -+ [NF_IP_LOCAL_IN] = NF_IP_LOCAL_OUT, -+#endif -+}; -+ -+unsigned int -+ip_nat_setup_info(struct ip_conntrack *conntrack, -+ const struct ip_nat_multi_range *mr, -+ unsigned int hooknum) -+{ -+ struct ip_conntrack_tuple new_tuple, inv_tuple, reply; -+ struct ip_conntrack_tuple orig_tp; -+ struct ip_nat_info *info = &conntrack->nat.info; -+ int in_hashes = info->initialized; -+ -+ MUST_BE_WRITE_LOCKED(&ip_nat_lock); -+ IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING -+ || hooknum == NF_IP_POST_ROUTING -+ || hooknum == NF_IP_LOCAL_IN -+ || hooknum == NF_IP_LOCAL_OUT); -+ IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS); -+ IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); -+ -+ /* What we've got will look like inverse of reply. Normally -+ this is what is in the conntrack, except for prior -+ manipulations (future optimization: if num_manips == 0, -+ orig_tp = -+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */ -+ invert_tuplepr(&orig_tp, -+ &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple); -+ -+#if 0 -+ { -+ unsigned int i; -+ -+ DEBUGP("Hook %u (%s), ", hooknum, -+ HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST"); -+ DUMP_TUPLE(&orig_tp); -+ DEBUGP("Range %p: ", mr); -+ for (i = 0; i < mr->rangesize; i++) { -+ DEBUGP("%u:%s%s%s %u.%u.%u.%u - %u.%u.%u.%u %u - %u\n", -+ i, -+ (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS) -+ ? " MAP_IPS" : "", -+ (mr->range[i].flags -+ & IP_NAT_RANGE_PROTO_SPECIFIED) -+ ? " PROTO_SPECIFIED" : "", -+ (mr->range[i].flags & IP_NAT_RANGE_FULL) -+ ? " FULL" : "", -+ NIPQUAD(mr->range[i].min_ip), -+ NIPQUAD(mr->range[i].max_ip), -+ mr->range[i].min.all, -+ mr->range[i].max.all); -+ } -+ } -+#endif -+ -+ do { -+ if (!get_unique_tuple(&new_tuple, &orig_tp, mr, conntrack, -+ hooknum)) { -+ DEBUGP("ip_nat_setup_info: Can't get unique for %p.\n", -+ conntrack); -+ return NF_DROP; -+ } -+ -+#if 0 -+ DEBUGP("Hook %u (%s) %p\n", hooknum, -+ HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST", -+ conntrack); -+ DEBUGP("Original: "); -+ DUMP_TUPLE(&orig_tp); -+ DEBUGP("New: "); -+ DUMP_TUPLE(&new_tuple); -+#endif -+ -+ /* We now have two tuples (SRCIP/SRCPT/DSTIP/DSTPT): -+ the original (A/B/C/D') and the mangled one (E/F/G/H'). -+ -+ We're only allowed to work with the SRC per-proto -+ part, so we create inverses of both to start, then -+ derive the other fields we need. */ -+ -+ /* Reply connection: simply invert the new tuple -+ (G/H/E/F') */ -+ invert_tuplepr(&reply, &new_tuple); -+ -+ /* Alter conntrack table so it recognizes replies. -+ If fail this race (reply tuple now used), repeat. */ -+ } while (!ip_conntrack_alter_reply(conntrack, &reply)); -+ -+ /* FIXME: We can simply used existing conntrack reply tuple -+ here --RR */ -+ /* Create inverse of original: C/D/A/B' */ -+ invert_tuplepr(&inv_tuple, &orig_tp); -+ -+ /* Has source changed?. */ -+ if (!ip_ct_tuple_src_equal(&new_tuple, &orig_tp)) { -+ /* In this direction, a source manip. */ -+ info->manips[info->num_manips++] = -+ ((struct ip_nat_info_manip) -+ { IP_CT_DIR_ORIGINAL, hooknum, -+ IP_NAT_MANIP_SRC, new_tuple.src }); -+ -+ IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS); -+ -+ /* In the reverse direction, a destination manip. */ -+ info->manips[info->num_manips++] = -+ ((struct ip_nat_info_manip) -+ { IP_CT_DIR_REPLY, opposite_hook[hooknum], -+ IP_NAT_MANIP_DST, orig_tp.src }); -+ IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS); -+ } -+ -+ /* Has destination changed? */ -+ if (!ip_ct_tuple_dst_equal(&new_tuple, &orig_tp)) { -+ /* In this direction, a destination manip */ -+ info->manips[info->num_manips++] = -+ ((struct ip_nat_info_manip) -+ { IP_CT_DIR_ORIGINAL, hooknum, -+ IP_NAT_MANIP_DST, reply.src }); -+ -+ IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS); -+ -+ /* In the reverse direction, a source manip. */ -+ info->manips[info->num_manips++] = -+ ((struct ip_nat_info_manip) -+ { IP_CT_DIR_REPLY, opposite_hook[hooknum], -+ IP_NAT_MANIP_SRC, inv_tuple.src }); -+ IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS); -+ } -+ -+ /* If there's a helper, assign it; based on new tuple. */ -+ if (!conntrack->master) -+ info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, -+ &reply); -+ -+ /* It's done. */ -+ info->initialized |= (1 << HOOK2MANIP(hooknum)); -+ -+ if (in_hashes) { -+ IP_NF_ASSERT(info->bysource.conntrack); -+ replace_in_hashes(conntrack, info); -+ } else { -+ place_in_hashes(conntrack, info); -+ } -+ -+ return NF_ACCEPT; -+} -+ -+void replace_in_hashes(struct ip_conntrack *conntrack, -+ struct ip_nat_info *info) -+{ -+ /* Source has changed, so replace in hashes. */ -+ unsigned int srchash -+ = hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL] -+ .tuple.src, -+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL] -+ .tuple.dst.protonum); -+ /* We place packet as seen OUTGOUNG in byips_proto hash -+ (ie. reverse dst and src of reply packet. */ -+ unsigned int ipsprotohash -+ = hash_by_ipsproto(conntrack->tuplehash[IP_CT_DIR_REPLY] -+ .tuple.dst.ip, -+ conntrack->tuplehash[IP_CT_DIR_REPLY] -+ .tuple.src.ip, -+ conntrack->tuplehash[IP_CT_DIR_REPLY] -+ .tuple.dst.protonum); -+ -+ IP_NF_ASSERT(info->bysource.conntrack == conntrack); -+ MUST_BE_WRITE_LOCKED(&ip_nat_lock); -+ -+ list_del(&info->bysource.list); -+ list_del(&info->byipsproto.list); -+ -+ list_prepend(&bysource[srchash], &info->bysource); -+ list_prepend(&byipsproto[ipsprotohash], &info->byipsproto); -+} -+ -+void place_in_hashes(struct ip_conntrack *conntrack, -+ struct ip_nat_info *info) -+{ -+ unsigned int srchash -+ = hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL] -+ .tuple.src, -+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL] -+ .tuple.dst.protonum); -+ /* We place packet as seen OUTGOUNG in byips_proto hash -+ (ie. reverse dst and src of reply packet. */ -+ unsigned int ipsprotohash -+ = hash_by_ipsproto(conntrack->tuplehash[IP_CT_DIR_REPLY] -+ .tuple.dst.ip, -+ conntrack->tuplehash[IP_CT_DIR_REPLY] -+ .tuple.src.ip, -+ conntrack->tuplehash[IP_CT_DIR_REPLY] -+ .tuple.dst.protonum); -+ -+ IP_NF_ASSERT(!info->bysource.conntrack); -+ -+ MUST_BE_WRITE_LOCKED(&ip_nat_lock); -+ info->byipsproto.conntrack = conntrack; -+ info->bysource.conntrack = conntrack; -+ -+ list_prepend(&bysource[srchash], &info->bysource); -+ list_prepend(&byipsproto[ipsprotohash], &info->byipsproto); -+} -+ -+static void -+manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len, -+ const struct ip_conntrack_manip *manip, -+ enum ip_nat_manip_type maniptype, -+ __u32 *nfcache) -+{ -+ *nfcache |= NFC_ALTERED; -+ find_nat_proto(proto)->manip_pkt(iph, len, manip, maniptype); -+ -+ if (maniptype == IP_NAT_MANIP_SRC) { -+ iph->check = ip_nat_cheat_check(~iph->saddr, manip->ip, -+ iph->check); -+ iph->saddr = manip->ip; -+ } else { -+ iph->check = ip_nat_cheat_check(~iph->daddr, manip->ip, -+ iph->check); -+ iph->daddr = manip->ip; -+ } -+#if 0 -+ if (ip_fast_csum((u8 *)iph, iph->ihl) != 0) -+ DEBUGP("IP: checksum on packet bad.\n"); -+ -+ if (proto == IPPROTO_TCP) { -+ void *th = (u_int32_t *)iph + iph->ihl; -+ if (tcp_v4_check(th, len - 4*iph->ihl, iph->saddr, iph->daddr, -+ csum_partial((char *)th, len-4*iph->ihl, 0))) -+ DEBUGP("TCP: checksum on packet bad\n"); -+ } -+#endif -+} -+ -+static inline int exp_for_packet(struct ip_conntrack_expect *exp, -+ struct sk_buff **pskb) -+{ -+ struct ip_conntrack_protocol *proto; -+ int ret = 1; -+ -+ MUST_BE_READ_LOCKED(&ip_conntrack_lock); -+ proto = __ip_ct_find_proto((*pskb)->nh.iph->protocol); -+ if (proto->exp_matches_pkt) -+ ret = proto->exp_matches_pkt(exp, pskb); -+ -+ return ret; -+} -+ -+/* Do packet manipulations according to binding. */ -+unsigned int -+do_bindings(struct ip_conntrack *ct, -+ enum ip_conntrack_info ctinfo, -+ struct ip_nat_info *info, -+ unsigned int hooknum, -+ struct sk_buff **pskb) -+{ -+ unsigned int i; -+ struct ip_nat_helper *helper; -+ enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); -+ int is_tcp = (*pskb)->nh.iph->protocol == IPPROTO_TCP; -+ -+ /* Need nat lock to protect against modification, but neither -+ conntrack (referenced) and helper (deleted with -+ synchronize_bh()) can vanish. */ -+ READ_LOCK(&ip_nat_lock); -+ for (i = 0; i < info->num_manips; i++) { -+ /* raw socket (tcpdump) may have clone of incoming -+ skb: don't disturb it --RR */ -+ if (skb_cloned(*pskb) && !(*pskb)->sk) { -+ struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); -+ if (!nskb) { -+ READ_UNLOCK(&ip_nat_lock); -+ return NF_DROP; -+ } -+ kfree_skb(*pskb); -+ *pskb = nskb; -+ } -+ -+ if (info->manips[i].direction == dir -+ && info->manips[i].hooknum == hooknum) { -+ DEBUGP("Mangling %p: %s to %u.%u.%u.%u %u\n", -+ *pskb, -+ info->manips[i].maniptype == IP_NAT_MANIP_SRC -+ ? "SRC" : "DST", -+ NIPQUAD(info->manips[i].manip.ip), -+ htons(info->manips[i].manip.u.all)); -+ manip_pkt((*pskb)->nh.iph->protocol, -+ (*pskb)->nh.iph, -+ (*pskb)->len, -+ &info->manips[i].manip, -+ info->manips[i].maniptype, -+ &(*pskb)->nfcache); -+ } -+ } -+ helper = info->helper; -+ READ_UNLOCK(&ip_nat_lock); -+ -+ if (helper) { -+ struct ip_conntrack_expect *exp = NULL; -+ struct list_head *cur_item; -+ int ret = NF_ACCEPT; -+ int helper_called = 0; -+ -+ DEBUGP("do_bindings: helper existing for (%p)\n", ct); -+ -+ /* Always defragged for helpers */ -+ IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off -+ & htons(IP_MF|IP_OFFSET))); -+ -+ /* Have to grab read lock before sibling_list traversal */ -+ READ_LOCK(&ip_conntrack_lock); -+ list_for_each_prev(cur_item, &ct->sibling_list) { -+ exp = list_entry(cur_item, struct ip_conntrack_expect, -+ expected_list); -+ -+ /* if this expectation is already established, skip */ -+ if (exp->sibling) -+ continue; -+ -+ if (exp_for_packet(exp, pskb)) { -+ /* FIXME: May be true multiple times in the -+ * case of UDP!! */ -+ DEBUGP("calling nat helper (exp=%p) for packet\n", exp); -+ ret = helper->help(ct, exp, info, ctinfo, -+ hooknum, pskb); -+ if (ret != NF_ACCEPT) { -+ READ_UNLOCK(&ip_conntrack_lock); -+ return ret; -+ } -+ helper_called = 1; -+ } -+ } -+ /* Helper might want to manip the packet even when there is no -+ * matching expectation for this packet */ -+ if (!helper_called && helper->flags & IP_NAT_HELPER_F_ALWAYS) { -+ DEBUGP("calling nat helper for packet without expectation\n"); -+ ret = helper->help(ct, NULL, info, ctinfo, -+ hooknum, pskb); -+ if (ret != NF_ACCEPT) { -+ READ_UNLOCK(&ip_conntrack_lock); -+ return ret; -+ } -+ } -+ READ_UNLOCK(&ip_conntrack_lock); -+ -+ /* Adjust sequence number only once per packet -+ * (helper is called at all hooks) */ -+ if (is_tcp && (hooknum == NF_IP_POST_ROUTING -+ || hooknum == NF_IP_LOCAL_IN)) { -+ DEBUGP("ip_nat_core: adjusting sequence number\n"); -+ /* future: put this in a l4-proto specific function, -+ * and call this function here. */ -+ ip_nat_seq_adjust(*pskb, ct, ctinfo); -+ } -+ -+ return ret; -+ -+ } else -+ return NF_ACCEPT; -+ -+ /* not reached */ -+} -+ -+unsigned int -+icmp_reply_translation(struct sk_buff *skb, -+ struct ip_conntrack *conntrack, -+ unsigned int hooknum, -+ int dir) -+{ -+ struct iphdr *iph = skb->nh.iph; -+ struct icmphdr *hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl); -+ struct iphdr *inner = (struct iphdr *)(hdr + 1); -+ size_t datalen = skb->len - ((void *)inner - (void *)iph); -+ unsigned int i; -+ struct ip_nat_info *info = &conntrack->nat.info; -+ -+ IP_NF_ASSERT(skb->len >= iph->ihl*4 + sizeof(struct icmphdr)); -+ /* Must be RELATED */ -+ IP_NF_ASSERT(skb->nfct -+ - ((struct ip_conntrack *)skb->nfct->master)->infos -+ == IP_CT_RELATED -+ || skb->nfct -+ - ((struct ip_conntrack *)skb->nfct->master)->infos -+ == IP_CT_RELATED+IP_CT_IS_REPLY); -+ -+ /* Redirects on non-null nats must be dropped, else they'll -+ start talking to each other without our translation, and be -+ confused... --RR */ -+ if (hdr->type == ICMP_REDIRECT) { -+ /* Don't care about races here. */ -+ if (info->initialized -+ != ((1 << IP_NAT_MANIP_SRC) | (1 << IP_NAT_MANIP_DST)) -+ || info->num_manips != 0) -+ return NF_DROP; -+ } -+ -+ DEBUGP("icmp_reply_translation: translating error %p hook %u dir %s\n", -+ skb, hooknum, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); -+ /* Note: May not be from a NAT'd host, but probably safest to -+ do translation always as if it came from the host itself -+ (even though a "host unreachable" coming from the host -+ itself is a bit weird). -+ -+ More explanation: some people use NAT for anonymizing. -+ Also, CERT recommends dropping all packets from private IP -+ addresses (although ICMP errors from internal links with -+ such addresses are not too uncommon, as Alan Cox points -+ out) */ -+ -+ READ_LOCK(&ip_nat_lock); -+ for (i = 0; i < info->num_manips; i++) { -+ DEBUGP("icmp_reply: manip %u dir %s hook %u\n", -+ i, info->manips[i].direction == IP_CT_DIR_ORIGINAL ? -+ "ORIG" : "REPLY", info->manips[i].hooknum); -+ -+ if (info->manips[i].direction != dir) -+ continue; -+ -+ /* Mapping the inner packet is just like a normal -+ packet, except it was never src/dst reversed, so -+ where we would normally apply a dst manip, we apply -+ a src, and vice versa. */ -+ if (info->manips[i].hooknum == hooknum) { -+ DEBUGP("icmp_reply: inner %s -> %u.%u.%u.%u %u\n", -+ info->manips[i].maniptype == IP_NAT_MANIP_SRC -+ ? "DST" : "SRC", -+ NIPQUAD(info->manips[i].manip.ip), -+ ntohs(info->manips[i].manip.u.udp.port)); -+ manip_pkt(inner->protocol, inner, -+ skb->len - ((void *)inner - (void *)iph), -+ &info->manips[i].manip, -+ !info->manips[i].maniptype, -+ &skb->nfcache); -+ /* Outer packet needs to have IP header NATed like -+ it's a reply. */ -+ -+ /* Use mapping to map outer packet: 0 give no -+ per-proto mapping */ -+ DEBUGP("icmp_reply: outer %s -> %u.%u.%u.%u\n", -+ info->manips[i].maniptype == IP_NAT_MANIP_SRC -+ ? "SRC" : "DST", -+ NIPQUAD(info->manips[i].manip.ip)); -+ manip_pkt(0, iph, skb->len, -+ &info->manips[i].manip, -+ info->manips[i].maniptype, -+ &skb->nfcache); -+ } -+ } -+ READ_UNLOCK(&ip_nat_lock); -+ -+ /* Since we mangled inside ICMP packet, recalculate its -+ checksum from scratch. (Hence the handling of incorrect -+ checksums in conntrack, so we don't accidentally fix one.) */ -+ hdr->checksum = 0; -+ hdr->checksum = ip_compute_csum((unsigned char *)hdr, -+ sizeof(*hdr) + datalen); -+ -+ return NF_ACCEPT; -+} -+ -+int __init ip_nat_init(void) -+{ -+ size_t i; -+ -+ /* Leave them the same for the moment. */ -+ ip_nat_htable_size = ip_conntrack_htable_size; -+ -+ /* One vmalloc for both hash tables */ -+ bysource = vmalloc(sizeof(struct list_head) * ip_nat_htable_size*2); -+ if (!bysource) { -+ return -ENOMEM; -+ } -+ byipsproto = bysource + ip_nat_htable_size; -+ -+ /* Sew in builtin protocols. */ -+ WRITE_LOCK(&ip_nat_lock); -+ list_append(&protos, &ip_nat_protocol_tcp); -+ list_append(&protos, &ip_nat_protocol_udp); -+ list_append(&protos, &ip_nat_protocol_icmp); -+ WRITE_UNLOCK(&ip_nat_lock); -+ -+ for (i = 0; i < ip_nat_htable_size; i++) { -+ INIT_LIST_HEAD(&bysource[i]); -+ INIT_LIST_HEAD(&byipsproto[i]); -+ } -+ -+ /* FIXME: Man, this is a hack. <SIGH> */ -+ IP_NF_ASSERT(ip_conntrack_destroyed == NULL); -+ ip_conntrack_destroyed = &ip_nat_cleanup_conntrack; -+ -+ return 0; -+} -+ -+/* Clear NAT section of all conntracks, in case we're loaded again. */ -+static int clean_nat(const struct ip_conntrack *i, void *data) -+{ -+ memset((void *)&i->nat, 0, sizeof(i->nat)); -+ return 0; -+} -+ -+/* Not __exit: called from ip_nat_standalone.c:init_or_cleanup() --RR */ -+void ip_nat_cleanup(void) -+{ -+ ip_ct_selective_cleanup(&clean_nat, NULL); -+ ip_conntrack_destroyed = NULL; -+ vfree(bysource); -+} -diff -uNr linux_org/net/ipv4/netfilter/ip_nat_pptp.c linux/net/ipv4/netfilter/ip_nat_pptp.c ---- linux_org/net/ipv4/netfilter/ip_nat_pptp.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/ipv4/netfilter/ip_nat_pptp.c 2006-10-27 14:11:52.000000000 +0200 -@@ -0,0 +1,475 @@ -+/* -+ * ip_nat_pptp.c - Version 1.5 -+ * -+ * NAT support for PPTP (Point to Point Tunneling Protocol). -+ * PPTP is a a protocol for creating virtual private networks. -+ * It is a specification defined by Microsoft and some vendors -+ * working with Microsoft. PPTP is built on top of a modified -+ * version of the Internet Generic Routing Encapsulation Protocol. -+ * GRE is defined in RFC 1701 and RFC 1702. Documentation of -+ * PPTP can be found in RFC 2637 -+ * -+ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> -+ * -+ * Development of this code funded by Astaro AG (http://www.astaro.com/) -+ * -+ * TODO: - Support for multiple calls within one session -+ * (needs netfilter newnat code) -+ * - NAT to a unique tuple, not to TCP source port -+ * (needs netfilter tuple reservation) -+ * -+ * Changes: -+ * 2002-02-10 - Version 1.3 -+ * - Use ip_nat_mangle_tcp_packet() because of cloned skb's -+ * in local connections (Philip Craig <philipc@snapgear.com>) -+ * - add checks for magicCookie and pptp version -+ * - make argument list of pptp_{out,in}bound_packet() shorter -+ * - move to C99 style initializers -+ * - print version number at module loadtime -+ * 2003-09-22 - Version 1.5 -+ * - use SNATed tcp sourceport as callid, since we get called before -+ * TCP header is mangled (Philip Craig <philipc@snapgear.com>) -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/ip.h> -+#include <linux/tcp.h> -+#include <net/tcp.h> -+#include <linux/netfilter_ipv4/ip_nat.h> -+#include <linux/netfilter_ipv4/ip_nat_rule.h> -+#include <linux/netfilter_ipv4/ip_nat_helper.h> -+#include <linux/netfilter_ipv4/ip_nat_pptp.h> -+#include <linux/netfilter_ipv4/ip_conntrack_helper.h> -+#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> -+#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> -+ -+#define IP_NAT_PPTP_VERSION "1.5" -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); -+MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP"); -+ -+ -+#if 0 -+#include "ip_conntrack_pptp_priv.h" -+#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ -+ ": " format, ## args) -+#else -+#define DEBUGP(format, args...) -+#endif -+ -+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"); -+ } -+ ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig, &t); -+ ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, &inv_t); -+ break; -+ case PPTP_IN_CALL_CONNECT: -+ pcid = &pptpReq.iccon->peersCallID; -+ if (!oldexp) -+ break; -+ old_dst_ip = oldexp->tuple.dst.ip; -+ t = oldexp->tuple; -+ -+ /* alter expectation, no need for callID */ -+ if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) { -+ /* expectation for PNS->PAC direction */ -+ t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; -+ } else { -+ /* expectation for PAC->PNS direction */ -+ t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; -+ } -+ -+ if (!ip_conntrack_change_expect(oldexp, &t)) { -+ DEBUGP("successfully changed expect\n"); -+ } else { -+ DEBUGP("can't change expect\n"); -+ } -+ break; -+ case PPTP_IN_CALL_REQUEST: -+ /* only need to nat in case PAC is behind NAT box */ -+ break; -+ case PPTP_WAN_ERROR_NOTIFY: -+ pcid = &pptpReq.wanerr->peersCallID; -+ break; -+ case PPTP_CALL_DISCONNECT_NOTIFY: -+ pcid = &pptpReq.disc->callID; -+ break; -+ -+ default: -+ DEBUGP("unknown inbound packet %s\n", -+ (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]); -+ /* fall through */ -+ -+ case PPTP_START_SESSION_REQUEST: -+ case PPTP_START_SESSION_REPLY: -+ case PPTP_STOP_SESSION_REQUEST: -+ case PPTP_STOP_SESSION_REPLY: -+ case PPTP_ECHO_REQUEST: -+ case PPTP_ECHO_REPLY: -+ /* no need to alter packet */ -+ return NF_ACCEPT; -+ } -+ -+ /* mangle packet */ -+ IP_NF_ASSERT(pcid); -+ DEBUGP("altering peer call id from 0x%04x to 0x%04x\n", -+ ntohs(*pcid), ntohs(new_pcid)); -+ ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)pcid - (void *)pptph, -+ sizeof(new_pcid), (char *)&new_pcid, -+ sizeof(new_pcid)); -+ -+ if (new_cid) { -+ IP_NF_ASSERT(cid); -+ DEBUGP("altering call id from 0x%04x to 0x%04x\n", -+ ntohs(*cid), ntohs(new_cid)); -+ ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, -+ (void *)cid - (void *)pptph, -+ sizeof(new_cid), (char *)&new_cid, -+ sizeof(new_cid)); -+ } -+ -+ /* great, at least we don't need to resize packets */ -+ return NF_ACCEPT; -+} -+ -+ -+static unsigned int tcp_help(struct ip_conntrack *ct, -+ struct ip_conntrack_expect *exp, -+ struct ip_nat_info *info, -+ enum ip_conntrack_info ctinfo, -+ unsigned int hooknum, struct sk_buff **pskb) -+{ -+ struct iphdr *iph = (*pskb)->nh.iph; -+ struct tcphdr *tcph = (void *) iph + iph->ihl*4; -+ unsigned int datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4; -+ struct pptp_pkt_hdr *pptph; -+ -+ int dir; -+ -+ DEBUGP("entering\n"); -+ -+ /* Only mangle things once: DST for original direction -+ and SRC for reply direction. */ -+ dir = CTINFO2DIR(ctinfo); -+ if (!((HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC -+ && dir == IP_CT_DIR_ORIGINAL) -+ || (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST -+ && dir == IP_CT_DIR_REPLY))) { -+ DEBUGP("Not touching dir %s at hook %s\n", -+ dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", -+ hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" -+ : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" -+ : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" -+ : hooknum == NF_IP_LOCAL_IN ? "INPUT" : "???"); -+ return NF_ACCEPT; -+ } -+ -+ /* if packet is too small, just skip it */ -+ if (datalen < sizeof(struct pptp_pkt_hdr)+ -+ sizeof(struct PptpControlHeader)) { -+ DEBUGP("pptp packet too short\n"); -+ return NF_ACCEPT; -+ } -+ -+ pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4); -+ -+ /* if it's not a control message, we can't handle it */ -+ if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || -+ ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { -+ DEBUGP("not a pptp control packet\n"); -+ return NF_ACCEPT; -+ } -+ -+ LOCK_BH(&ip_pptp_lock); -+ -+ if (dir == IP_CT_DIR_ORIGINAL) { -+ /* reuqests sent by client to server (PNS->PAC) */ -+ pptp_outbound_pkt(pskb, ct, ctinfo, exp); -+ } else { -+ /* response from the server to the client (PAC->PNS) */ -+ pptp_inbound_pkt(pskb, ct, ctinfo, exp); -+ } -+ -+ UNLOCK_BH(&ip_pptp_lock); -+ -+ return NF_ACCEPT; -+} -+ -+/* nat helper struct for control connection */ -+static struct ip_nat_helper pptp_tcp_helper = { -+ .list = { NULL, NULL }, -+ .name = "pptp", -+ .flags = IP_NAT_HELPER_F_ALWAYS, -+ .me = THIS_MODULE, -+ .tuple = { .src = { .ip = 0, -+ .u = { .tcp = { .port = -+ __constant_htons(PPTP_CONTROL_PORT) } -+ } -+ }, -+ .dst = { .ip = 0, -+ .u = { .all = 0 }, -+ .protonum = IPPROTO_TCP -+ } -+ }, -+ -+ .mask = { .src = { .ip = 0, -+ .u = { .tcp = { .port = 0xFFFF } } -+ }, -+ .dst = { .ip = 0, -+ .u = { .all = 0 }, -+ .protonum = 0xFFFF -+ } -+ }, -+ .help = tcp_help, -+ .expect = pptp_nat_expected -+}; -+ -+ -+static int __init init(void) -+{ -+ DEBUGP("%s: registering NAT helper\n", __FILE__); -+ if (ip_nat_helper_register(&pptp_tcp_helper)) { -+ printk(KERN_ERR "Unable to register NAT application helper " -+ "for pptp\n"); -+ return -EIO; -+ } -+ -+ printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION); -+ return 0; -+} -+ -+static void __exit fini(void) -+{ -+ DEBUGP("cleanup_module\n" ); -+ ip_nat_helper_unregister(&pptp_tcp_helper); -+ printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION); -+} -+ -+module_init(init); -+module_exit(fini); -diff -uNr linux_org/net/ipv4/netfilter/ip_nat_proto_gre.c linux/net/ipv4/netfilter/ip_nat_proto_gre.c ---- linux_org/net/ipv4/netfilter/ip_nat_proto_gre.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/ipv4/netfilter/ip_nat_proto_gre.c 2006-10-27 14:11:52.000000000 +0200 -@@ -0,0 +1,225 @@ -+/* -+ * 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)) { -+ -+ switch (tuple->dst.u.gre.version) { -+ case 0: -+ DEBUGP("NATing GRE version 0 (ct=%p)\n", -+ conntrack); -+ min = 1; -+ range_size = 0xffffffff; -+ break; -+ case GRE_VERSION_PPTP: -+ DEBUGP("%p: NATing GRE PPTP\n", -+ conntrack); -+ min = 1; -+ range_size = 0xffff; -+ break; -+ default: -+ printk(KERN_WARNING "nat_gre: unknown GRE version\n"); -+ return 0; -+ break; -+ } -+ -+ } 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->dst.u.gre.version) -+ len += sprintf(buffer + len, "version=%d ", -+ ntohs(match->dst.u.gre.version)); -+ -+ if (mask->dst.u.gre.protocol) -+ len += sprintf(buffer + len, "protocol=0x%x ", -+ ntohs(match->dst.u.gre.protocol)); -+ -+ 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); |