diff options
-rw-r--r-- | packages/linux/linux-mtx-1-2.4.27/48-pptp.patch | 5092 | ||||
-rw-r--r-- | packages/linux/linux-mtx-1_2.4.27.bb | 1 | ||||
-rw-r--r-- | packages/linux/linux-mtx-2-2.4.27/45-acm-tty-and-sb2.patch | 721 | ||||
-rw-r--r-- | packages/linux/linux-mtx-2-2.4.27/45-acm-tty.patch | 252 | ||||
-rw-r--r-- | packages/linux/linux-mtx-2_2.4.27.bb | 2 |
5 files changed, 5815 insertions, 253 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 new file mode 100644 index 0000000000..5896f90370 --- /dev/null +++ b/packages/linux/linux-mtx-1-2.4.27/48-pptp.patch @@ -0,0 +1,5092 @@ +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); diff --git a/packages/linux/linux-mtx-1_2.4.27.bb b/packages/linux/linux-mtx-1_2.4.27.bb index e46c3c723d..09916c9022 100644 --- a/packages/linux/linux-mtx-1_2.4.27.bb +++ b/packages/linux/linux-mtx-1_2.4.27.bb @@ -36,6 +36,7 @@ SRC_URI = "cvs://cvs:cvs@ftp.linux-mips.org/home/cvs;module=linux;tag=linux_2_4_ file://27-usbd-amd-pb1x00-kit-23may2003-usbd.diff;patch=1 \ file://29-au1000-pci-config-clear-errors.diff;patch=1 \ file://42-usb-ohci-fixes.patch;patch=1 \ + file://48-pptp.patch;patch=1 \ file://defconfig-mtx-1" S = "${WORKDIR}/linux" diff --git a/packages/linux/linux-mtx-2-2.4.27/45-acm-tty-and-sb2.patch b/packages/linux/linux-mtx-2-2.4.27/45-acm-tty-and-sb2.patch new file mode 100644 index 0000000000..4561facb73 --- /dev/null +++ b/packages/linux/linux-mtx-2-2.4.27/45-acm-tty-and-sb2.patch @@ -0,0 +1,721 @@ +--- linux/drivers/usb/acm.c-orig 2007-04-13 18:32:16.352672105 +0200 ++++ linux/drivers/usb/acm.c 2007-04-13 18:33:21.063526545 +0200 +@@ -124,6 +124,9 @@ + #define ACM_CTRL_PARITY 0x20 + #define ACM_CTRL_OVERRUN 0x40 + ++// some devices don't have one comm and one data interface, but only one interface with endpoints for comm and data ++#define SINGLE_IF_ACM 0x01 ++ + /* + * Line speed and caracter encoding. + */ +@@ -139,6 +142,8 @@ + * Internal driver structures. + */ + ++#define TD_SIZE 16384 ++ + struct acm { + struct usb_device *dev; /* the coresponding usb device */ + struct usb_interface *iface; /* the interfaces - +0 control +1 data */ +@@ -153,12 +158,23 @@ + unsigned int minor; /* acm minor number */ + unsigned char throttle; /* throttled by tty layer */ + unsigned char clocal; /* termios CLOCAL */ ++ unsigned long throttle_start; ++ unsigned char resubmit_to_unthrottle; /* Leftover data from last operation */ ++ unsigned char *throttle_data; ++ int td_len; ++ int td_busy; ++ unsigned char used_interfaces; ++ struct semaphore mutex; + }; + ++#define mutex_lock(x) down(x) ++#define mutex_unlock(x) up(x) ++ + /* global params controlling max sizes for read, write, control */ + static int maxszr = 0; + static int maxszw = 0; + static int maxszc = 0; ++static int nonlegacy = 0; + + static struct usb_driver acm_driver; + static struct tty_driver acm_tty_driver; +@@ -167,6 +183,95 @@ + #define ACM_READY(acm) (acm && acm->dev && acm->used) + + /* ++ * Helper functions to optimize throttleing ++ */ ++static int ++acm_fill_tty(struct urb *urb, struct tty_struct *tty, unsigned char *data, int length) ++{ ++ struct acm *acm = urb->context; ++ int n = 0; ++ /*printk("acm_fill_tty: %d bytes\n", length);*/ ++ if (!urb->status && !acm->throttle) { ++ for (n = 0; n < length && !acm->throttle; n++) { ++ /* if we insert more than TTY_FLIPBUF_SIZE characters, ++ * we drop them. */ ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) { ++ tty_flip_buffer_push(tty); ++ } ++ tty_insert_flip_char(tty, data[n], 0); ++ } ++ tty_flip_buffer_push(tty); ++ } ++ /*printk("copied %d bytes.\n", n);*/ ++ return n; ++} ++ ++static int ++acm_shift_if_throttle(unsigned char *data, int *length, int shift_by) ++{ ++ if (shift_by < *length) { ++ dbg("need to shift uncopied %d bytes to front.", *length - shift_by); ++ memmove(data, data + shift_by, *length - shift_by); ++ *length -= shift_by; ++ return 1; ++ } ++ return 0; ++} ++ ++static int ++acm_buffer_if_thottle(struct acm *acm, unsigned char *data, int start, int *length) ++{ ++ int copied = *length; ++ if (start < *length) { ++ int space = TD_SIZE - acm->td_len; ++ int needed = *length - start; ++ copied = (space < needed)? space: needed; ++ dbg("need to push %d to throttle buffer, can copy %d.", ++ needed, copied); ++ memcpy(acm->throttle_data + acm->td_len, data, copied); ++ acm->td_len += copied; ++ *length -= copied; ++ } ++ return copied; ++} ++ ++static int ++acm_empty_throttle(struct urb *urb, struct tty_struct *tty) ++{ ++ unsigned long flags; ++ struct acm *acm = urb->context; ++ ++ save_flags(flags); ++ cli(); ++ ++ if (acm->td_busy) { ++ restore_flags(flags); ++ return 0; ++ } ++ acm->td_busy = 1; ++ restore_flags(flags); ++ ++ if (acm->td_len > 0) { ++ ++ dbg("acm_empty_throttle: trying to empty throttle buffer: %d bytes.", ++ acm->td_len); ++ ++ /* if there has been something left from previous operations ++ * we try to complete this before looking at the urb */ ++ int copied = acm_fill_tty(urb, tty, acm->throttle_data, acm->td_len); ++ if (acm_shift_if_throttle(acm->throttle_data, &acm->td_len, copied)) { ++ /* we were unable to empty the throttle data, so we can't ++ * copy anything more now */ ++ acm->td_busy = 0; ++ return 0; ++ } ++ acm->td_len = 0; ++ } ++ acm->td_busy = 0; ++ return 1; ++} ++ ++/* + * Functions for ACM control messages. + */ + +@@ -174,7 +279,10 @@ + { + int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), + request, USB_RT_ACM, value, acm->iface[0].altsetting[0].bInterfaceNumber, buf, len, HZ * 5); +- dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval); ++ if (retval < 0) ++ err("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval); ++ else ++ dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval); + return retval < 0 ? retval : 0; + } + +@@ -191,10 +299,12 @@ + struct acm *acm = urb->context; + struct usb_ctrlrequest *dr = urb->transfer_buffer; + unsigned char *data = (unsigned char *)(dr + 1); +- int newctrl; ++ int newctrl, s1, s2; + + if (!ACM_READY(acm)) return; + ++ //err("acm_ctrl_irq %p %i", urb, dr->bRequestType); ++ + if (urb->status < 0) { + dbg("nonzero ctrl irq status received: %d", urb->status); + return; +@@ -226,8 +336,15 @@ + + return; + ++ case 0x2a: ++ s1 = le32_to_cpup((__u32 *) data); ++ s2 = le32_to_cpup((__u32 *) (data+4)); ++ ++ dbg("acm.c: ctrl 0x2a: idx %i len %i speed %i %i", dr->wIndex, dr->wLength, s1, s2); ++ return; ++ + default: +- dbg("unknown control event received: request %d index %d len %d data0 %d data1 %d", ++ err("unknown control event received: request %d index %d len %d data0 %d data1 %d", + dr->bRequest, dr->wIndex, dr->wLength, data[0], data[1]); + return; + } +@@ -238,36 +355,39 @@ + struct acm *acm = urb->context; + struct tty_struct *tty = acm->tty; + unsigned char *data = urb->transfer_buffer; +- int i = 0; ++ int copied = 0; ++ int buffered = 0; + + if (!ACM_READY(acm)) return; + +- if (urb->status) +- dbg("nonzero read bulk status received: %d", urb->status); ++ if (urb->status) { ++ err("nonzero read bulk status received: %d", urb->status); ++ } + +- if (!urb->status && !acm->throttle) { +- for (i = 0; i < urb->actual_length && !acm->throttle; i++) { +- /* if we insert more than TTY_FLIPBUF_SIZE characters, +- * we drop them. */ +- if (tty->flip.count >= TTY_FLIPBUF_SIZE) { +- tty_flip_buffer_push(tty); +- } +- tty_insert_flip_char(tty, data[i], 0); +- } +- tty_flip_buffer_push(tty); ++ if (!acm_empty_throttle(urb, tty)) { ++ dbg("could not empty throttle buffer, entering throttle state, acm->td_busy: %d.", acm->td_busy); + } + ++ /* got here, either there was nothing in the throttle data or it could ++ * all be copied without throttleing again */ ++ copied = acm_fill_tty(urb, tty, data, urb->actual_length); + if (acm->throttle) { +- memmove(data, data + i, urb->actual_length - i); +- urb->actual_length -= i; +- return; ++ int length = urb->actual_length; ++ buffered = acm_buffer_if_thottle(acm, data, copied, &urb->actual_length); ++ if (buffered < length - copied ++ && acm_shift_if_throttle(data, &urb->actual_length, copied + buffered)) { ++ dbg("need to resubmit to unthrottle\n"); ++ acm->resubmit_to_unthrottle = 1; ++ return; ++ } + } + + urb->actual_length = 0; + urb->dev = acm->dev; + +- if (usb_submit_urb(urb)) ++ if (usb_submit_urb(urb)) { + dbg("failed resubmitting read urb"); ++ } + } + + static void acm_write_bulk(struct urb *urb) +@@ -283,6 +403,9 @@ + mark_bh(IMMEDIATE_BH); + } + ++static int unlinking_in_progress=0; ++static int closing=0; ++ + static void acm_softint(void *private) + { + struct acm *acm = private; +@@ -306,34 +429,57 @@ + + if (!acm || !acm->dev) return -EINVAL; + ++ mutex_lock (&acm->mutex); ++ + tty->driver_data = acm; + acm->tty = tty; + + MOD_INC_USE_COUNT; + +- lock_kernel(); ++ if ( closing ) ++ err("acm_tty_open: potential possibility of race condition detected"); ++ ++ if ( unlinking_in_progress ) { ++ err("acm_tty_open: cannot open because unlinking_in_progress %i", acm->used); ++ mutex_unlock (&acm->mutex); ++ return -1; ++ } + +- if (acm->used++) { +- unlock_kernel(); +- return 0; +- } ++ if (acm->used) { ++ acm->used++; ++ mutex_unlock (&acm->mutex); ++ return 0; ++ } + +- unlock_kernel(); ++ unlinking_in_progress=1; ++ err("acm_tty_open: %i %p !!", acm->used, tty); ++ ++ acm->resubmit_to_unthrottle = 0; ++ acm->td_len = 0; ++ acm->td_busy = 0; + + acm->ctrlurb.dev = acm->dev; + if (usb_submit_urb(&acm->ctrlurb)) +- dbg("usb_submit_urb(ctrl irq) failed"); ++ dbg("acm open: usb_submit_urb(ctrl irq) failed"); ++ else ++ dbg("acm open: ctrlurb %p submitted", &acm->ctrlurb); ++ ++ acm->used++; ++ acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS); + + acm->readurb.dev = acm->dev; + if (usb_submit_urb(&acm->readurb)) +- dbg("usb_submit_urb(read bulk) failed"); +- +- acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS); ++ dbg("acm open: usb_submit_urb(read bulk) failed"); ++ else ++ dbg("acm open: readurb %p submitted", &acm->readurb); + +- /* force low_latency on so that our tty_push actually forces the data through, ++ /* force low_latency on so that our tty_push actually forces the data through, + otherwise it is scheduled, and with high data rates data can get lost. */ + tty->low_latency = 1; + ++ unlinking_in_progress=0; ++ mutex_unlock (&acm->mutex); ++ + return 0; + } + +@@ -343,19 +489,35 @@ + + if (!acm || !acm->used) return; + +- if (!--acm->used) { +- if (acm->dev) { +- acm_set_control(acm, acm->ctrlout = 0); +- usb_unlink_urb(&acm->ctrlurb); +- usb_unlink_urb(&acm->writeurb); +- usb_unlink_urb(&acm->readurb); +- } else { +- tty_unregister_devfs(&acm_tty_driver, acm->minor); +- acm_table[acm->minor] = NULL; +- kfree(acm); +- } ++ mutex_lock (&acm->mutex); ++ ++ closing = 1; ++ if (--acm->used) { ++ closing=0; ++ MOD_DEC_USE_COUNT; ++ mutex_unlock (&acm->mutex); ++ return; ++ } ++ unlinking_in_progress = 1; ++ ++ err("acm_tty_close: %i %p", acm->used, tty); ++ ++ if (acm->dev) { ++ acm_set_control(acm, acm->ctrlout = 0); ++ usb_unlink_urb(&acm->ctrlurb); ++ usb_unlink_urb(&acm->writeurb); ++ usb_unlink_urb(&acm->readurb); ++ } else { ++ tty_unregister_devfs(&acm_tty_driver, acm->minor); ++ acm_table[acm->minor] = NULL; ++ kfree(acm->throttle_data); ++ kfree(acm); + } ++ ++ closing=0; ++ unlinking_in_progress = 0; + MOD_DEC_USE_COUNT; ++ mutex_unlock (&acm->mutex); + } + + static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) +@@ -363,8 +525,16 @@ + struct acm *acm = tty->driver_data; + + if (!ACM_READY(acm)) return -EINVAL; +- if (acm->writeurb.status == -EINPROGRESS) return 0; +- if (!count) return 0; ++ ++ if (acm->writeurb.status == -EINPROGRESS) { ++ dbg("tty_write in progress"); ++ return 0; ++ } ++ ++ if (!count) { ++ dbg("tty_write: nothing to write"); ++ return 0; ++ } + + count = (count > acm->writesize) ? acm->writesize : count; + +@@ -401,22 +571,44 @@ + { + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return; ++ dbg("acm_tty_throttle ON %ld ---> %ld", jiffies-acm->throttle_start, jiffies); + acm->throttle = 1; ++ acm->throttle_start = jiffies; + } + + static void acm_tty_unthrottle(struct tty_struct *tty) + { + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return; ++ dbg("acm_tty_throttle OFF %ld ---> %ld", jiffies, jiffies-acm->throttle_start); + acm->throttle = 0; +- if (acm->readurb.status != -EINPROGRESS) ++ ++ if (!acm_empty_throttle(&acm->readurb, tty)) { ++ if (acm->td_busy) { ++ printk("***** pending acm_empty_throttle!\n"); ++ } else { ++ dbg("throttle not emptied.\n"); ++ } ++ } ++ ++ if (acm->resubmit_to_unthrottle != 0) { ++ dbg("resubmit_to_unthrottle: acm_read_bulk"); ++ acm->resubmit_to_unthrottle = 0; + acm_read_bulk(&acm->readurb); ++ } + } + + static void acm_tty_break_ctl(struct tty_struct *tty, int state) + { + struct acm *acm = tty->driver_data; ++ + if (!ACM_READY(acm)) return; ++ ++ if (nonlegacy) { ++ err("non-legacy port, skipping acm_tty_break_ctl"); ++ return; ++ } ++ + if (acm_send_break(acm, state ? 0xffff : 0)) + dbg("send break failed"); + } +@@ -455,7 +647,19 @@ + case TIOCMBIC: newctrl &= ~mask; break; + } + +- if (acm->ctrlout == newctrl) return 0; ++ if (acm->ctrlout == newctrl) { ++ dbg("acm_tty_ioctl: set old state %x", newctrl); ++ return 0; ++ } ++ ++ err("acm_tty_ioctl: %s%s%s -> dtr%s rts%s (%lx)", ++ cmd==TIOCMBIC?"Clear":(cmd==TIOCMBIS?"Set":"SET"), ++ mask & ACM_CTRL_DTR ? " DTR":"", ++ mask & ACM_CTRL_RTS ? " RTS":"", ++ newctrl & ACM_CTRL_DTR ? "+":"-", ++ newctrl & ACM_CTRL_RTS ? "+":"-", ++ arg); ++ + return acm_set_control(acm, acm->ctrlout = newctrl); + } + +@@ -483,6 +687,12 @@ + + if (!ACM_READY(acm)) return; + ++ if (nonlegacy) { ++ acm->clocal = ((termios->c_cflag & CLOCAL) != 0); ++ dbg("non-legacy port, skipping acm_tty_set_termios"); ++ return; ++ } ++ + newline.speed = cpu_to_le32p(acm_tty_speed + + (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0)); + newline.stopbits = termios->c_cflag & CSTOPB ? 2 : 0; +@@ -518,34 +727,64 @@ + struct usb_config_descriptor *cfacm; + struct usb_interface_descriptor *ifcom, *ifdata; + struct usb_endpoint_descriptor *epctrl, *epread, *epwrite; +- int readsize, ctrlsize, minor, i, j; ++ int readsize, ctrlsize, minor, i; + unsigned char *buf; ++ unsigned char used_interfaces=2; + + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + + cfacm = dev->config + i; + +- dbg("probing config %d", cfacm->bConfigurationValue); ++ ifcom = cfacm->interface[ifnum].altsetting + 0; ++ ++ if (id->driver_info == SINGLE_IF_ACM) { ++ printk("using single_if_acm\n"); ++ struct usb_endpoint_descriptor *ep=ifcom->endpoint; ++ int k; ++ ++ if (ifcom->bNumEndpoints != 3) { ++ continue; ++ } ++ ++ epctrl = epread = epwrite = NULL; ++ for (k=0; k<3; ++k, ++ep) { ++ if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT && ++ (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { ++ epctrl = ep; ++ } else if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK && ++ (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { ++ epread = ep; ++ } else if ( (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { ++ epwrite = ep; ++ } ++ } + +- for (j = 0; j < cfacm->bNumInterfaces - 1; j++) { ++ if ( !epctrl || !epread || !epwrite ) { ++ dbg("SINGLE_IF_ACM acm_probe inv eps epctrl %s epread %s epwrite %s", epctrl?"ok":"missing", ++ epread?"ok":"missing", epwrite?"ok":"missing"); ++ dbg("SINGLE_IF_ACM Invalid enpoint configuration"); ++ continue; ++ } + +- if (usb_interface_claimed(cfacm->interface + j) || +- usb_interface_claimed(cfacm->interface + j + 1)) ++ used_interfaces = 1; ++ } else { ++ if ((ifnum+1)>=cfacm->bNumInterfaces || usb_interface_claimed(cfacm->interface + ifnum + 1)) { ++ // no data interface available + continue; ++ } + +- ifcom = cfacm->interface[j].altsetting + 0; +- ifdata = cfacm->interface[j + 1].altsetting + 0; ++ ifdata = cfacm->interface[ifnum + 1].altsetting + 0; + + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints < 2) { +- ifcom = cfacm->interface[j + 1].altsetting + 0; +- ifdata = cfacm->interface[j].altsetting + 0; ++ ifcom = cfacm->interface[ifnum + 1].altsetting + 0; ++ ifdata = cfacm->interface[ifnum].altsetting + 0; + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints < 2) + continue; + } + + if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 || +- ifcom->bInterfaceProtocol < 1 || ifcom->bInterfaceProtocol > 6 || +- ifcom->bNumEndpoints < 1) ++ ifcom->bInterfaceProtocol < 1 || ifcom->bInterfaceProtocol > 6 || ++ ifcom->bNumEndpoints < 1) + continue; + + epctrl = ifcom->endpoint + 0; +@@ -553,76 +792,86 @@ + epwrite = ifdata->endpoint + 1; + + if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3 || +- (epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 || +- ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80) ++ (epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 || ++ ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80) + continue; + +- dbg("using interface %d\n", j); +- + if ((epread->bEndpointAddress & 0x80) != 0x80) { + epread = ifdata->endpoint + 1; + epwrite = ifdata->endpoint + 0; + } ++ } + +- usb_set_configuration(dev, cfacm->bConfigurationValue); ++ usb_set_configuration(dev, cfacm->bConfigurationValue); + +- for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); +- if (acm_table[minor]) { +- err("no more free acm devices"); +- return NULL; +- } ++ for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); ++ if (acm_table[minor]) { ++ err("no more free acm devices"); ++ return NULL; ++ } + +- if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { +- err("out of memory"); +- return NULL; +- } +- memset(acm, 0, sizeof(struct acm)); ++ if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { ++ err("out of memory"); ++ return NULL; ++ } ++ memset(acm, 0, sizeof(struct acm)); + +- ctrlsize = (epctrl->wMaxPacketSize > maxszc)? +- epctrl->wMaxPacketSize: maxszc; +- readsize = (epread->wMaxPacketSize > maxszr)? +- epread->wMaxPacketSize: maxszr; +- acm->writesize = (epwrite->wMaxPacketSize > maxszw)? +- epwrite->wMaxPacketSize: maxszw; +- +- acm->iface = cfacm->interface + j; +- acm->minor = minor; +- acm->dev = dev; +- +- acm->tqueue.routine = acm_softint; +- acm->tqueue.data = acm; +- +- if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { +- err("out of memory"); +- kfree(acm); +- return NULL; +- } ++ ctrlsize = (epctrl->wMaxPacketSize > maxszc)? ++ epctrl->wMaxPacketSize: maxszc; ++ readsize = (epread->wMaxPacketSize > maxszr)? ++ epread->wMaxPacketSize: maxszr; ++ acm->writesize = (epwrite->wMaxPacketSize > maxszw)? ++ epwrite->wMaxPacketSize: maxszw; ++ ++ init_MUTEX (&acm->mutex); ++ if (!(acm->throttle_data = kmalloc(TD_SIZE * sizeof (*acm->throttle_data), GFP_KERNEL))) { ++ err("out of memory (throttle_data)"); ++ kfree(acm); ++ return NULL; ++ } ++ acm->iface = cfacm->interface + ifnum; ++ acm->minor = minor; ++ acm->dev = dev; + +- FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), +- buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); ++ acm->used_interfaces = used_interfaces; + +- FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), +- buf += ctrlsize, readsize, acm_read_bulk, acm); +- acm->readurb.transfer_flags |= USB_NO_FSBR; ++ acm->tqueue.routine = acm_softint; ++ acm->tqueue.data = acm; + +- FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), +- buf += readsize, acm->writesize, acm_write_bulk, acm); +- acm->writeurb.transfer_flags |= USB_NO_FSBR; ++ if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { ++ err("out of memory (urb buf)"); ++ kfree(acm); ++ return NULL; ++ } + +- printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor); ++ FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), ++ buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); + +- acm_set_control(acm, acm->ctrlout); ++ FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), ++ buf += ctrlsize, readsize, acm_read_bulk, acm); ++ acm->readurb.transfer_flags |= USB_NO_FSBR; + +- acm->line.speed = cpu_to_le32(9600); +- acm->line.databits = 8; +- acm_set_line(acm, &acm->line); ++ FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), ++ buf += readsize, acm->writesize, acm_write_bulk, acm); ++ acm->writeurb.transfer_flags |= USB_NO_FSBR; + +- usb_driver_claim_interface(&acm_driver, acm->iface + 0, acm); +- usb_driver_claim_interface(&acm_driver, acm->iface + 1, acm); ++ printk(KERN_INFO "ttyACM%d: USB ACM device C %p W %p R %p %x\n", minor, &acm->ctrlurb, ++ &acm->writeurb, &acm->readurb, acm->ctrlout); ++ ++ acm_set_control(acm, acm->ctrlout); + +- tty_register_devfs(&acm_tty_driver, 0, minor); +- return acm_table[minor] = acm; ++ acm->line.speed = cpu_to_le32(9600); ++ acm->line.databits = 8; ++ acm_set_line(acm, &acm->line); ++ ++ if ( acm->used_interfaces == 2 ) { ++ // only just checked interface is claimed automatically, so claim data interface too ++ usb_driver_claim_interface(&acm_driver, acm->iface + 1, acm); + } ++ ++ tty_register_devfs(&acm_tty_driver, 0, minor); ++ ++ return acm_table[minor] = acm; + } + + return NULL; +@@ -646,7 +895,9 @@ + kfree(acm->ctrlurb.transfer_buffer); + + usb_driver_release_interface(&acm_driver, acm->iface + 0); +- usb_driver_release_interface(&acm_driver, acm->iface + 1); ++ if ( acm->used_interfaces == 2 ) { ++ usb_driver_release_interface(&acm_driver, acm->iface + 1); ++ } + + if (!acm->used) { + tty_unregister_devfs(&acm_tty_driver, acm->minor); +@@ -665,6 +916,9 @@ + + static struct usb_device_id acm_ids[] = { + { USB_DEVICE(0x22B8, 0x1005) }, /* Motorola TimePort 280 */ ++ { USB_DEVICE(0x05C6, 0x7001), driver_info: SINGLE_IF_ACM }, /* Siemens HC15/HC25 */ ++ { USB_DEVICE(0x0681, 0x003e), driver_info: SINGLE_IF_ACM }, /* Siemens HC15/HC25 */ ++ { USB_DEVICE(0x22B8, 0x1006) }, + { USB_DEVICE_INFO(USB_CLASS_COMM, 0, 0) }, + { USB_DEVICE_INFO(USB_CLASS_COMM, 2, 0) }, + { } +@@ -735,7 +989,7 @@ + return -1; + } + +- info(DRIVER_VERSION ":" DRIVER_DESC); ++ info(DRIVER_VERSION ":" DRIVER_DESC "(non-legacy %d)", nonlegacy); + + return 0; + } +@@ -759,7 +1013,9 @@ + MODULE_PARM(maxszc, "i"); + MODULE_PARM_DESC(maxszc, "User specified USB endpoint control size"); + ++MODULE_PARM(nonlegacy, "i"); ++MODULE_PARM_DESC(nonlegacy, "Set this to 1 to for use with non-legacy device"); ++ + MODULE_AUTHOR( DRIVER_AUTHOR ); + MODULE_DESCRIPTION( DRIVER_DESC ); + MODULE_LICENSE("GPL"); +- diff --git a/packages/linux/linux-mtx-2-2.4.27/45-acm-tty.patch b/packages/linux/linux-mtx-2-2.4.27/45-acm-tty.patch deleted file mode 100644 index 028d10ad9a..0000000000 --- a/packages/linux/linux-mtx-2-2.4.27/45-acm-tty.patch +++ /dev/null @@ -1,252 +0,0 @@ ---- linux/drivers/usb/acm.c~45-acm-tty.patch 2006-06-07 11:21:21.648422000 +0200 -+++ linux/drivers/usb/acm.c 2006-06-09 17:20:51.735793750 +0200 -@@ -139,6 +139,8 @@ - * Internal driver structures. - */ - -+#define TD_SIZE 16384 -+ - struct acm { - struct usb_device *dev; /* the coresponding usb device */ - struct usb_interface *iface; /* the interfaces - +0 control +1 data */ -@@ -153,6 +155,11 @@ - unsigned int minor; /* acm minor number */ - unsigned char throttle; /* throttled by tty layer */ - unsigned char clocal; /* termios CLOCAL */ -+ unsigned long throttle_start; -+ unsigned char resubmit_to_unthrottle; /* Leftover data from last operation */ -+ unsigned char *throttle_data; -+ int td_len; -+ int td_busy; - }; - - /* global params controlling max sizes for read, write, control */ -@@ -166,6 +173,96 @@ - - #define ACM_READY(acm) (acm && acm->dev && acm->used) - -+ -+/* -+ * Helper functions to optimize throttleing -+ */ -+static int -+acm_fill_tty(struct urb *urb, struct tty_struct *tty, unsigned char *data, int length) -+{ -+ struct acm *acm = urb->context; -+ int n = 0; -+ /*printk("acm_fill_tty: %d bytes\n", length);*/ -+ if (!urb->status && !acm->throttle) { -+ for (n = 0; n < length && !acm->throttle; n++) { -+ /* if we insert more than TTY_FLIPBUF_SIZE characters, -+ * we drop them. */ -+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) { -+ tty_flip_buffer_push(tty); -+ } -+ tty_insert_flip_char(tty, data[n], 0); -+ } -+ tty_flip_buffer_push(tty); -+ } -+ /*printk("copied %d bytes.\n", n);*/ -+ return n; -+} -+ -+static int -+acm_shift_if_throttle(unsigned char *data, int *length, int shift_by) -+{ -+ if (shift_by < *length) { -+ dbg("need to shift uncopied %d bytes to front.", *length - shift_by); -+ memmove(data, data + shift_by, *length - shift_by); -+ *length -= shift_by; -+ return 1; -+ } -+ return 0; -+} -+ -+static int -+acm_buffer_if_thottle(struct acm *acm, unsigned char *data, int start, int *length) -+{ -+ int copied = *length; -+ if (start < *length) { -+ int space = TD_SIZE - acm->td_len; -+ int needed = *length - start; -+ copied = (space < needed)? space: needed; -+ dbg("need to push %d to throttle buffer, can copy %d.", -+ needed, copied); -+ memcpy(acm->throttle_data + acm->td_len, data, copied); -+ acm->td_len += copied; -+ *length -= copied; -+ } -+ return copied; -+} -+ -+static int -+acm_empty_throttle(struct urb *urb, struct tty_struct *tty) -+{ -+ unsigned long flags; -+ struct acm *acm = urb->context; -+ -+ save_flags(flags); -+ cli(); -+ -+ if (acm->td_busy) { -+ restore_flags(flags); -+ return 0; -+ } -+ acm->td_busy = 1; -+ restore_flags(flags); -+ -+ if (acm->td_len > 0) { -+ -+ dbg("acm_empty_throttle: trying to empty throttle buffer: %d bytes.", -+ acm->td_len); -+ -+ /* if there has been something left from previous operations -+ * we try to complete this before looking at the urb */ -+ int copied = acm_fill_tty(urb, tty, acm->throttle_data, acm->td_len); -+ if (acm_shift_if_throttle(acm->throttle_data, &acm->td_len, copied)) { -+ /* we were unable to empty the throttle data, so we can't -+ * copy anything more now */ -+ acm->td_busy = 0; -+ return 0; -+ } -+ acm->td_len = 0; -+ } -+ acm->td_busy = 0; -+ return 1; -+} -+ - /* - * Functions for ACM control messages. - */ -@@ -238,36 +335,40 @@ - struct acm *acm = urb->context; - struct tty_struct *tty = acm->tty; - unsigned char *data = urb->transfer_buffer; -- int i = 0; -+ int copied = 0; -+ int buffered = 0; - - if (!ACM_READY(acm)) return; - -- if (urb->status) -+ if (urb->status) { - dbg("nonzero read bulk status received: %d", urb->status); -+ } - -- if (!urb->status && !acm->throttle) { -- for (i = 0; i < urb->actual_length && !acm->throttle; i++) { -- /* if we insert more than TTY_FLIPBUF_SIZE characters, -- * we drop them. */ -- if (tty->flip.count >= TTY_FLIPBUF_SIZE) { -- tty_flip_buffer_push(tty); -- } -- tty_insert_flip_char(tty, data[i], 0); -- } -- tty_flip_buffer_push(tty); -+ dbg("acm_read_bulk, calling acm_empty_throttle()"); -+ if (!acm_empty_throttle(urb, tty)) { -+ dbg("could not empty throttle buffer, entering throttle state, acm->td_busy: %d.", acm->td_busy); - } - -+ /* got here, either there was nothing in the throttle data or it could -+ * all be copied without throttleing again */ -+ copied = acm_fill_tty(urb, tty, data, urb->actual_length); - if (acm->throttle) { -- memmove(data, data + i, urb->actual_length - i); -- urb->actual_length -= i; -- return; -+ int length = urb->actual_length; -+ buffered = acm_buffer_if_thottle(acm, data, copied, &urb->actual_length); -+ if (buffered < length - copied -+ && acm_shift_if_throttle(data, &urb->actual_length, copied + buffered)) { -+ printk("need to resubmit to unthrottle\n"); -+ acm->resubmit_to_unthrottle = 1; -+ return; -+ } - } - - urb->actual_length = 0; - urb->dev = acm->dev; - -- if (usb_submit_urb(urb)) -+ if (usb_submit_urb(urb)) { - dbg("failed resubmitting read urb"); -+ } - } - - static void acm_write_bulk(struct urb *urb) -@@ -330,7 +431,12 @@ - - acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS); - -- /* force low_latency on so that our tty_push actually forces the data through, -+ acm->resubmit_to_unthrottle = 0; -+ acm->td_len = 0; -+ acm->td_busy = 0; -+ acm->throttle_data = kmalloc(TD_SIZE * sizeof (*acm->throttle_data), GFP_KERNEL); -+ -+ /* force low_latency on so that our tty_push actually forces the data through, - otherwise it is scheduled, and with high data rates data can get lost. */ - tty->low_latency = 1; - -@@ -352,6 +458,7 @@ - } else { - tty_unregister_devfs(&acm_tty_driver, acm->minor); - acm_table[acm->minor] = NULL; -+ kfree(acm->throttle_data); - kfree(acm); - } - } -@@ -363,8 +470,16 @@ - struct acm *acm = tty->driver_data; - - if (!ACM_READY(acm)) return -EINVAL; -- if (acm->writeurb.status == -EINPROGRESS) return 0; -- if (!count) return 0; -+ -+ if (acm->writeurb.status == -EINPROGRESS) { -+ dbg("tty_write in progress"); -+ return 0; -+ } -+ -+ if (!count) { -+ dbg("tty_write: nothing to write"); -+ return 0; -+ } - - count = (count > acm->writesize) ? acm->writesize : count; - -@@ -401,16 +516,32 @@ - { - struct acm *acm = tty->driver_data; - if (!ACM_READY(acm)) return; -+ dbg("acm_tty_throttle ON %ld ---> %ld", jiffies-acm->throttle_start, jiffies); - acm->throttle = 1; -+ acm->throttle_start = jiffies; - } - - static void acm_tty_unthrottle(struct tty_struct *tty) - { - struct acm *acm = tty->driver_data; - if (!ACM_READY(acm)) return; -+ dbg("acm_tty_throttle OFF %ld ---> %ld", jiffies, jiffies-acm->throttle_start); - acm->throttle = 0; -- if (acm->readurb.status != -EINPROGRESS) -+ -+ dbg("acm_tty_unthrottle, calling acm_empty_throttle()"); -+ if (!acm_empty_throttle(&acm->readurb, tty)) { -+ if (acm->td_busy) { -+ printk("***** pending acm_empty_throttle!\n"); -+ } else { -+ dbg("throttle not emptied.\n"); -+ } -+ } -+ -+ if (acm->resubmit_to_unthrottle != 0) { -+ dbg("resubmit_to_unthrottle: acm_read_bulk"); -+ acm->resubmit_to_unthrottle = 0; - acm_read_bulk(&acm->readurb); -+ } - } - - static void acm_tty_break_ctl(struct tty_struct *tty, int state) diff --git a/packages/linux/linux-mtx-2_2.4.27.bb b/packages/linux/linux-mtx-2_2.4.27.bb index 83769faf95..9a64ebb492 100644 --- a/packages/linux/linux-mtx-2_2.4.27.bb +++ b/packages/linux/linux-mtx-2_2.4.27.bb @@ -39,7 +39,7 @@ SRC_URI += "cvs://cvs:cvs@ftp.linux-mips.org/home/cvs;module=linux;tag=linux_2_4 file://42-usb-ohci-fixes.patch;patch=1 \ file://43-usbserial-27-32-backport.diff;patch=1 \ file://44-dbdma-and-au1550_psc.diff;patch=1 \ - file://45-acm-tty.patch;patch=1 \ + file://45-acm-tty-and-sb2.patch;patch=1 \ file://46-otg.patch;patch=1 \ file://47-au1000_eth.patch;patch=1 \ file://48-pptp.patch;patch=1 \ |