Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[NETFILTER]: nf_conntrack/nf_nat: add PPTP helper port

Add nf_conntrack port of the PPtP conntrack/NAT helper. Since there seems
to be no IPv6-capable PPtP implementation the helper only support IPv4.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Patrick McHardy and committed by
David S. Miller
f09943fe 92703eee

+1896
+321
include/linux/netfilter/nf_conntrack_pptp.h
··· 1 + /* PPTP constants and structs */ 2 + #ifndef _NF_CONNTRACK_PPTP_H 3 + #define _NF_CONNTRACK_PPTP_H 4 + 5 + /* state of the control session */ 6 + enum pptp_ctrlsess_state { 7 + PPTP_SESSION_NONE, /* no session present */ 8 + PPTP_SESSION_ERROR, /* some session error */ 9 + PPTP_SESSION_STOPREQ, /* stop_sess request seen */ 10 + PPTP_SESSION_REQUESTED, /* start_sess request seen */ 11 + PPTP_SESSION_CONFIRMED, /* session established */ 12 + }; 13 + 14 + /* state of the call inside the control session */ 15 + enum pptp_ctrlcall_state { 16 + PPTP_CALL_NONE, 17 + PPTP_CALL_ERROR, 18 + PPTP_CALL_OUT_REQ, 19 + PPTP_CALL_OUT_CONF, 20 + PPTP_CALL_IN_REQ, 21 + PPTP_CALL_IN_REP, 22 + PPTP_CALL_IN_CONF, 23 + PPTP_CALL_CLEAR_REQ, 24 + }; 25 + 26 + /* conntrack private data */ 27 + struct nf_ct_pptp_master { 28 + enum pptp_ctrlsess_state sstate; /* session state */ 29 + enum pptp_ctrlcall_state cstate; /* call state */ 30 + __be16 pac_call_id; /* call id of PAC */ 31 + __be16 pns_call_id; /* call id of PNS */ 32 + 33 + /* in pre-2.6.11 this used to be per-expect. Now it is per-conntrack 34 + * and therefore imposes a fixed limit on the number of maps */ 35 + struct nf_ct_gre_keymap *keymap[IP_CT_DIR_MAX]; 36 + }; 37 + 38 + struct nf_nat_pptp { 39 + __be16 pns_call_id; /* NAT'ed PNS call id */ 40 + __be16 pac_call_id; /* NAT'ed PAC call id */ 41 + }; 42 + 43 + #ifdef __KERNEL__ 44 + 45 + #define PPTP_CONTROL_PORT 1723 46 + 47 + #define PPTP_PACKET_CONTROL 1 48 + #define PPTP_PACKET_MGMT 2 49 + 50 + #define PPTP_MAGIC_COOKIE 0x1a2b3c4d 51 + 52 + struct pptp_pkt_hdr { 53 + __u16 packetLength; 54 + __be16 packetType; 55 + __be32 magicCookie; 56 + }; 57 + 58 + /* PptpControlMessageType values */ 59 + #define PPTP_START_SESSION_REQUEST 1 60 + #define PPTP_START_SESSION_REPLY 2 61 + #define PPTP_STOP_SESSION_REQUEST 3 62 + #define PPTP_STOP_SESSION_REPLY 4 63 + #define PPTP_ECHO_REQUEST 5 64 + #define PPTP_ECHO_REPLY 6 65 + #define PPTP_OUT_CALL_REQUEST 7 66 + #define PPTP_OUT_CALL_REPLY 8 67 + #define PPTP_IN_CALL_REQUEST 9 68 + #define PPTP_IN_CALL_REPLY 10 69 + #define PPTP_IN_CALL_CONNECT 11 70 + #define PPTP_CALL_CLEAR_REQUEST 12 71 + #define PPTP_CALL_DISCONNECT_NOTIFY 13 72 + #define PPTP_WAN_ERROR_NOTIFY 14 73 + #define PPTP_SET_LINK_INFO 15 74 + 75 + #define PPTP_MSG_MAX 15 76 + 77 + /* PptpGeneralError values */ 78 + #define PPTP_ERROR_CODE_NONE 0 79 + #define PPTP_NOT_CONNECTED 1 80 + #define PPTP_BAD_FORMAT 2 81 + #define PPTP_BAD_VALUE 3 82 + #define PPTP_NO_RESOURCE 4 83 + #define PPTP_BAD_CALLID 5 84 + #define PPTP_REMOVE_DEVICE_ERROR 6 85 + 86 + struct PptpControlHeader { 87 + __be16 messageType; 88 + __u16 reserved; 89 + }; 90 + 91 + /* FramingCapability Bitmap Values */ 92 + #define PPTP_FRAME_CAP_ASYNC 0x1 93 + #define PPTP_FRAME_CAP_SYNC 0x2 94 + 95 + /* BearerCapability Bitmap Values */ 96 + #define PPTP_BEARER_CAP_ANALOG 0x1 97 + #define PPTP_BEARER_CAP_DIGITAL 0x2 98 + 99 + struct PptpStartSessionRequest { 100 + __be16 protocolVersion; 101 + __u16 reserved1; 102 + __be32 framingCapability; 103 + __be32 bearerCapability; 104 + __be16 maxChannels; 105 + __be16 firmwareRevision; 106 + __u8 hostName[64]; 107 + __u8 vendorString[64]; 108 + }; 109 + 110 + /* PptpStartSessionResultCode Values */ 111 + #define PPTP_START_OK 1 112 + #define PPTP_START_GENERAL_ERROR 2 113 + #define PPTP_START_ALREADY_CONNECTED 3 114 + #define PPTP_START_NOT_AUTHORIZED 4 115 + #define PPTP_START_UNKNOWN_PROTOCOL 5 116 + 117 + struct PptpStartSessionReply { 118 + __be16 protocolVersion; 119 + __u8 resultCode; 120 + __u8 generalErrorCode; 121 + __be32 framingCapability; 122 + __be32 bearerCapability; 123 + __be16 maxChannels; 124 + __be16 firmwareRevision; 125 + __u8 hostName[64]; 126 + __u8 vendorString[64]; 127 + }; 128 + 129 + /* PptpStopReasons */ 130 + #define PPTP_STOP_NONE 1 131 + #define PPTP_STOP_PROTOCOL 2 132 + #define PPTP_STOP_LOCAL_SHUTDOWN 3 133 + 134 + struct PptpStopSessionRequest { 135 + __u8 reason; 136 + __u8 reserved1; 137 + __u16 reserved2; 138 + }; 139 + 140 + /* PptpStopSessionResultCode */ 141 + #define PPTP_STOP_OK 1 142 + #define PPTP_STOP_GENERAL_ERROR 2 143 + 144 + struct PptpStopSessionReply { 145 + __u8 resultCode; 146 + __u8 generalErrorCode; 147 + __u16 reserved1; 148 + }; 149 + 150 + struct PptpEchoRequest { 151 + __be32 identNumber; 152 + }; 153 + 154 + /* PptpEchoReplyResultCode */ 155 + #define PPTP_ECHO_OK 1 156 + #define PPTP_ECHO_GENERAL_ERROR 2 157 + 158 + struct PptpEchoReply { 159 + __be32 identNumber; 160 + __u8 resultCode; 161 + __u8 generalErrorCode; 162 + __u16 reserved; 163 + }; 164 + 165 + /* PptpFramingType */ 166 + #define PPTP_ASYNC_FRAMING 1 167 + #define PPTP_SYNC_FRAMING 2 168 + #define PPTP_DONT_CARE_FRAMING 3 169 + 170 + /* PptpCallBearerType */ 171 + #define PPTP_ANALOG_TYPE 1 172 + #define PPTP_DIGITAL_TYPE 2 173 + #define PPTP_DONT_CARE_BEARER_TYPE 3 174 + 175 + struct PptpOutCallRequest { 176 + __be16 callID; 177 + __be16 callSerialNumber; 178 + __be32 minBPS; 179 + __be32 maxBPS; 180 + __be32 bearerType; 181 + __be32 framingType; 182 + __be16 packetWindow; 183 + __be16 packetProcDelay; 184 + __be16 phoneNumberLength; 185 + __u16 reserved1; 186 + __u8 phoneNumber[64]; 187 + __u8 subAddress[64]; 188 + }; 189 + 190 + /* PptpCallResultCode */ 191 + #define PPTP_OUTCALL_CONNECT 1 192 + #define PPTP_OUTCALL_GENERAL_ERROR 2 193 + #define PPTP_OUTCALL_NO_CARRIER 3 194 + #define PPTP_OUTCALL_BUSY 4 195 + #define PPTP_OUTCALL_NO_DIAL_TONE 5 196 + #define PPTP_OUTCALL_TIMEOUT 6 197 + #define PPTP_OUTCALL_DONT_ACCEPT 7 198 + 199 + struct PptpOutCallReply { 200 + __be16 callID; 201 + __be16 peersCallID; 202 + __u8 resultCode; 203 + __u8 generalErrorCode; 204 + __be16 causeCode; 205 + __be32 connectSpeed; 206 + __be16 packetWindow; 207 + __be16 packetProcDelay; 208 + __be32 physChannelID; 209 + }; 210 + 211 + struct PptpInCallRequest { 212 + __be16 callID; 213 + __be16 callSerialNumber; 214 + __be32 callBearerType; 215 + __be32 physChannelID; 216 + __be16 dialedNumberLength; 217 + __be16 dialingNumberLength; 218 + __u8 dialedNumber[64]; 219 + __u8 dialingNumber[64]; 220 + __u8 subAddress[64]; 221 + }; 222 + 223 + /* PptpInCallResultCode */ 224 + #define PPTP_INCALL_ACCEPT 1 225 + #define PPTP_INCALL_GENERAL_ERROR 2 226 + #define PPTP_INCALL_DONT_ACCEPT 3 227 + 228 + struct PptpInCallReply { 229 + __be16 callID; 230 + __be16 peersCallID; 231 + __u8 resultCode; 232 + __u8 generalErrorCode; 233 + __be16 packetWindow; 234 + __be16 packetProcDelay; 235 + __u16 reserved; 236 + }; 237 + 238 + struct PptpInCallConnected { 239 + __be16 peersCallID; 240 + __u16 reserved; 241 + __be32 connectSpeed; 242 + __be16 packetWindow; 243 + __be16 packetProcDelay; 244 + __be32 callFramingType; 245 + }; 246 + 247 + struct PptpClearCallRequest { 248 + __be16 callID; 249 + __u16 reserved; 250 + }; 251 + 252 + struct PptpCallDisconnectNotify { 253 + __be16 callID; 254 + __u8 resultCode; 255 + __u8 generalErrorCode; 256 + __be16 causeCode; 257 + __u16 reserved; 258 + __u8 callStatistics[128]; 259 + }; 260 + 261 + struct PptpWanErrorNotify { 262 + __be16 peersCallID; 263 + __u16 reserved; 264 + __be32 crcErrors; 265 + __be32 framingErrors; 266 + __be32 hardwareOverRuns; 267 + __be32 bufferOverRuns; 268 + __be32 timeoutErrors; 269 + __be32 alignmentErrors; 270 + }; 271 + 272 + struct PptpSetLinkInfo { 273 + __be16 peersCallID; 274 + __u16 reserved; 275 + __be32 sendAccm; 276 + __be32 recvAccm; 277 + }; 278 + 279 + union pptp_ctrl_union { 280 + struct PptpStartSessionRequest sreq; 281 + struct PptpStartSessionReply srep; 282 + struct PptpStopSessionRequest streq; 283 + struct PptpStopSessionReply strep; 284 + struct PptpOutCallRequest ocreq; 285 + struct PptpOutCallReply ocack; 286 + struct PptpInCallRequest icreq; 287 + struct PptpInCallReply icack; 288 + struct PptpInCallConnected iccon; 289 + struct PptpClearCallRequest clrreq; 290 + struct PptpCallDisconnectNotify disc; 291 + struct PptpWanErrorNotify wanerr; 292 + struct PptpSetLinkInfo setlink; 293 + }; 294 + 295 + /* crap needed for nf_conntrack_compat.h */ 296 + struct nf_conn; 297 + struct nf_conntrack_expect; 298 + enum ip_conntrack_info; 299 + 300 + extern int 301 + (*nf_nat_pptp_hook_outbound)(struct sk_buff **pskb, 302 + struct nf_conn *ct, enum ip_conntrack_info ctinfo, 303 + struct PptpControlHeader *ctlh, 304 + union pptp_ctrl_union *pptpReq); 305 + 306 + extern int 307 + (*nf_nat_pptp_hook_inbound)(struct sk_buff **pskb, 308 + struct nf_conn *ct, enum ip_conntrack_info ctinfo, 309 + struct PptpControlHeader *ctlh, 310 + union pptp_ctrl_union *pptpReq); 311 + 312 + extern void 313 + (*nf_nat_pptp_hook_exp_gre)(struct nf_conntrack_expect *exp_orig, 314 + struct nf_conntrack_expect *exp_reply); 315 + 316 + extern void 317 + (*nf_nat_pptp_hook_expectfn)(struct nf_conn *ct, 318 + struct nf_conntrack_expect *exp); 319 + 320 + #endif /* __KERNEL__ */ 321 + #endif /* _NF_CONNTRACK_PPTP_H */
+112
include/linux/netfilter/nf_conntrack_proto_gre.h
··· 1 + #ifndef _CONNTRACK_PROTO_GRE_H 2 + #define _CONNTRACK_PROTO_GRE_H 3 + #include <asm/byteorder.h> 4 + 5 + /* GRE PROTOCOL HEADER */ 6 + 7 + /* GRE Version field */ 8 + #define GRE_VERSION_1701 0x0 9 + #define GRE_VERSION_PPTP 0x1 10 + 11 + /* GRE Protocol field */ 12 + #define GRE_PROTOCOL_PPTP 0x880B 13 + 14 + /* GRE Flags */ 15 + #define GRE_FLAG_C 0x80 16 + #define GRE_FLAG_R 0x40 17 + #define GRE_FLAG_K 0x20 18 + #define GRE_FLAG_S 0x10 19 + #define GRE_FLAG_A 0x80 20 + 21 + #define GRE_IS_C(f) ((f)&GRE_FLAG_C) 22 + #define GRE_IS_R(f) ((f)&GRE_FLAG_R) 23 + #define GRE_IS_K(f) ((f)&GRE_FLAG_K) 24 + #define GRE_IS_S(f) ((f)&GRE_FLAG_S) 25 + #define GRE_IS_A(f) ((f)&GRE_FLAG_A) 26 + 27 + /* GRE is a mess: Four different standards */ 28 + struct gre_hdr { 29 + #if defined(__LITTLE_ENDIAN_BITFIELD) 30 + __u16 rec:3, 31 + srr:1, 32 + seq:1, 33 + key:1, 34 + routing:1, 35 + csum:1, 36 + version:3, 37 + reserved:4, 38 + ack:1; 39 + #elif defined(__BIG_ENDIAN_BITFIELD) 40 + __u16 csum:1, 41 + routing:1, 42 + key:1, 43 + seq:1, 44 + srr:1, 45 + rec:3, 46 + ack:1, 47 + reserved:4, 48 + version:3; 49 + #else 50 + #error "Adjust your <asm/byteorder.h> defines" 51 + #endif 52 + __be16 protocol; 53 + }; 54 + 55 + /* modified GRE header for PPTP */ 56 + struct gre_hdr_pptp { 57 + __u8 flags; /* bitfield */ 58 + __u8 version; /* should be GRE_VERSION_PPTP */ 59 + __be16 protocol; /* should be GRE_PROTOCOL_PPTP */ 60 + __be16 payload_len; /* size of ppp payload, not inc. gre header */ 61 + __be16 call_id; /* peer's call_id for this session */ 62 + __be32 seq; /* sequence number. Present if S==1 */ 63 + __be32 ack; /* seq number of highest packet recieved by */ 64 + /* sender in this session */ 65 + }; 66 + 67 + struct nf_ct_gre { 68 + unsigned int stream_timeout; 69 + unsigned int timeout; 70 + }; 71 + 72 + #ifdef __KERNEL__ 73 + #include <net/netfilter/nf_conntrack_tuple.h> 74 + 75 + struct nf_conn; 76 + 77 + /* structure for original <-> reply keymap */ 78 + struct nf_ct_gre_keymap { 79 + struct list_head list; 80 + struct nf_conntrack_tuple tuple; 81 + }; 82 + 83 + /* add new tuple->key_reply pair to keymap */ 84 + int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, 85 + struct nf_conntrack_tuple *t); 86 + 87 + /* delete keymap entries */ 88 + void nf_ct_gre_keymap_destroy(struct nf_conn *ct); 89 + 90 + /* get pointer to gre key, if present */ 91 + static inline __be32 *gre_key(struct gre_hdr *greh) 92 + { 93 + if (!greh->key) 94 + return NULL; 95 + if (greh->csum || greh->routing) 96 + return (__be32 *)(greh+sizeof(*greh)+4); 97 + return (__be32 *)(greh+sizeof(*greh)); 98 + } 99 + 100 + /* get pointer ot gre csum, if present */ 101 + static inline __sum16 *gre_csum(struct gre_hdr *greh) 102 + { 103 + if (!greh->csum) 104 + return NULL; 105 + return (__sum16 *)(greh+sizeof(*greh)); 106 + } 107 + 108 + extern void nf_ct_gre_keymap_flush(void); 109 + extern void nf_nat_need_gre(void); 110 + 111 + #endif /* __KERNEL__ */ 112 + #endif /* _CONNTRACK_PROTO_GRE_H */
+2
include/net/netfilter/ipv4/nf_conntrack_ipv4.h
··· 11 11 12 12 #ifdef CONFIG_NF_NAT_NEEDED 13 13 #include <net/netfilter/nf_nat.h> 14 + #include <linux/netfilter/nf_conntrack_pptp.h> 14 15 15 16 /* per conntrack: nat application helper private data */ 16 17 union nf_conntrack_nat_help { 17 18 /* insert nat helper private data here */ 19 + struct nf_nat_pptp nat_pptp_info; 18 20 }; 19 21 20 22 struct nf_conn_nat {
+4
include/net/netfilter/nf_conntrack.h
··· 21 21 22 22 #include <linux/netfilter/nf_conntrack_tcp.h> 23 23 #include <linux/netfilter/nf_conntrack_sctp.h> 24 + #include <linux/netfilter/nf_conntrack_proto_gre.h> 24 25 #include <net/netfilter/ipv4/nf_conntrack_icmp.h> 25 26 #include <net/netfilter/ipv6/nf_conntrack_icmpv6.h> 26 27 ··· 34 33 struct ip_ct_tcp tcp; 35 34 struct ip_ct_icmp icmp; 36 35 struct nf_ct_icmpv6 icmpv6; 36 + struct nf_ct_gre gre; 37 37 }; 38 38 39 39 union nf_conntrack_expect_proto { ··· 43 41 44 42 /* Add protocol helper include file here */ 45 43 #include <linux/netfilter/nf_conntrack_ftp.h> 44 + #include <linux/netfilter/nf_conntrack_pptp.h> 46 45 #include <linux/netfilter/nf_conntrack_h323.h> 47 46 48 47 /* per conntrack: application helper private data */ 49 48 union nf_conntrack_help { 50 49 /* insert conntrack helper private data (master) here */ 51 50 struct nf_ct_ftp_master ct_ftp_info; 51 + struct nf_ct_pptp_master ct_pptp_info; 52 52 struct nf_ct_h323_master ct_h323_info; 53 53 }; 54 54
+2
include/net/netfilter/nf_conntrack_helper.h
··· 34 34 struct nf_conn *ct, 35 35 enum ip_conntrack_info conntrackinfo); 36 36 37 + void (*destroy)(struct nf_conn *ct); 38 + 37 39 int (*to_nfattr)(struct sk_buff *skb, const struct nf_conn *ct); 38 40 }; 39 41
+6
include/net/netfilter/nf_conntrack_tuple.h
··· 49 49 struct { 50 50 __be16 port; 51 51 } sctp; 52 + struct { 53 + __be16 key; /* GRE key is 32bit, PPtP only uses 16bit */ 54 + } gre; 52 55 }; 53 56 54 57 /* The manipulable part of the tuple. */ ··· 87 84 struct { 88 85 __be16 port; 89 86 } sctp; 87 + struct { 88 + __be16 key; 89 + } gre; 90 90 } u; 91 91 92 92 /* The protocol. */
+10
net/ipv4/netfilter/Kconfig
··· 484 484 # <expr> '&&' <expr> (6) 485 485 # 486 486 # (6) Returns the result of min(/expr/, /expr/). 487 + config NF_NAT_PROTO_GRE 488 + tristate 489 + depends on NF_NAT && NF_CT_PROTO_GRE 490 + 487 491 config IP_NF_NAT_FTP 488 492 tristate 489 493 depends on IP_NF_IPTABLES && IP_NF_CONNTRACK && IP_NF_NAT ··· 531 527 depends on IP_NF_NAT!=n && IP_NF_PPTP!=n 532 528 default IP_NF_NAT if IP_NF_PPTP=y 533 529 default m if IP_NF_PPTP=m 530 + 531 + config NF_NAT_PPTP 532 + tristate 533 + depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT 534 + default NF_NAT && NF_CONNTRACK_PPTP 535 + select NF_NAT_PROTO_GRE 534 536 535 537 config IP_NF_NAT_H323 536 538 tristate
+4
net/ipv4/netfilter/Makefile
··· 54 54 obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o 55 55 obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o 56 56 obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o 57 + obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o 58 + 59 + # NAT protocols (nf_nat) 60 + obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o 57 61 58 62 # generic IP tables 59 63 obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o
+315
net/ipv4/netfilter/nf_nat_pptp.c
··· 1 + /* 2 + * nf_nat_pptp.c 3 + * 4 + * NAT support for PPTP (Point to Point Tunneling Protocol). 5 + * PPTP is a a protocol for creating virtual private networks. 6 + * It is a specification defined by Microsoft and some vendors 7 + * working with Microsoft. PPTP is built on top of a modified 8 + * version of the Internet Generic Routing Encapsulation Protocol. 9 + * GRE is defined in RFC 1701 and RFC 1702. Documentation of 10 + * PPTP can be found in RFC 2637 11 + * 12 + * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org> 13 + * 14 + * Development of this code funded by Astaro AG (http://www.astaro.com/) 15 + * 16 + * TODO: - NAT to a unique tuple, not to TCP source port 17 + * (needs netfilter tuple reservation) 18 + */ 19 + 20 + #include <linux/module.h> 21 + #include <linux/tcp.h> 22 + 23 + #include <net/netfilter/nf_nat.h> 24 + #include <net/netfilter/nf_nat_helper.h> 25 + #include <net/netfilter/nf_nat_rule.h> 26 + #include <net/netfilter/nf_conntrack_helper.h> 27 + #include <net/netfilter/nf_conntrack_expect.h> 28 + #include <linux/netfilter/nf_conntrack_proto_gre.h> 29 + #include <linux/netfilter/nf_conntrack_pptp.h> 30 + 31 + #define NF_NAT_PPTP_VERSION "3.0" 32 + 33 + #define REQ_CID(req, off) (*(__be16 *)((char *)(req) + (off))) 34 + 35 + MODULE_LICENSE("GPL"); 36 + MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); 37 + MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP"); 38 + MODULE_ALIAS("ip_nat_pptp"); 39 + 40 + #if 0 41 + extern const char *pptp_msg_name[]; 42 + #define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \ 43 + __FUNCTION__, ## args) 44 + #else 45 + #define DEBUGP(format, args...) 46 + #endif 47 + 48 + static void pptp_nat_expected(struct nf_conn *ct, 49 + struct nf_conntrack_expect *exp) 50 + { 51 + struct nf_conn *master = ct->master; 52 + struct nf_conntrack_expect *other_exp; 53 + struct nf_conntrack_tuple t; 54 + struct nf_ct_pptp_master *ct_pptp_info; 55 + struct nf_nat_pptp *nat_pptp_info; 56 + struct ip_nat_range range; 57 + 58 + ct_pptp_info = &nfct_help(master)->help.ct_pptp_info; 59 + nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info; 60 + 61 + /* And here goes the grand finale of corrosion... */ 62 + if (exp->dir == IP_CT_DIR_ORIGINAL) { 63 + DEBUGP("we are PNS->PAC\n"); 64 + /* therefore, build tuple for PAC->PNS */ 65 + t.src.l3num = AF_INET; 66 + t.src.u3.ip = master->tuplehash[!exp->dir].tuple.src.u3.ip; 67 + t.src.u.gre.key = ct_pptp_info->pac_call_id; 68 + t.dst.u3.ip = master->tuplehash[!exp->dir].tuple.dst.u3.ip; 69 + t.dst.u.gre.key = ct_pptp_info->pns_call_id; 70 + t.dst.protonum = IPPROTO_GRE; 71 + } else { 72 + DEBUGP("we are PAC->PNS\n"); 73 + /* build tuple for PNS->PAC */ 74 + t.src.l3num = AF_INET; 75 + t.src.u3.ip = master->tuplehash[exp->dir].tuple.src.u3.ip; 76 + t.src.u.gre.key = nat_pptp_info->pns_call_id; 77 + t.dst.u3.ip = master->tuplehash[exp->dir].tuple.dst.u3.ip; 78 + t.dst.u.gre.key = nat_pptp_info->pac_call_id; 79 + t.dst.protonum = IPPROTO_GRE; 80 + } 81 + 82 + DEBUGP("trying to unexpect other dir: "); 83 + NF_CT_DUMP_TUPLE(&t); 84 + other_exp = nf_conntrack_expect_find_get(&t); 85 + if (other_exp) { 86 + nf_conntrack_unexpect_related(other_exp); 87 + nf_conntrack_expect_put(other_exp); 88 + DEBUGP("success\n"); 89 + } else { 90 + DEBUGP("not found!\n"); 91 + } 92 + 93 + /* This must be a fresh one. */ 94 + BUG_ON(ct->status & IPS_NAT_DONE_MASK); 95 + 96 + /* Change src to where master sends to */ 97 + range.flags = IP_NAT_RANGE_MAP_IPS; 98 + range.min_ip = range.max_ip 99 + = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; 100 + if (exp->dir == IP_CT_DIR_ORIGINAL) { 101 + range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; 102 + range.min = range.max = exp->saved_proto; 103 + } 104 + /* hook doesn't matter, but it has to do source manip */ 105 + nf_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); 106 + 107 + /* For DST manip, map port here to where it's expected. */ 108 + range.flags = IP_NAT_RANGE_MAP_IPS; 109 + range.min_ip = range.max_ip 110 + = ct->master->tuplehash[!exp->dir].tuple.src.u3.ip; 111 + if (exp->dir == IP_CT_DIR_REPLY) { 112 + range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; 113 + range.min = range.max = exp->saved_proto; 114 + } 115 + /* hook doesn't matter, but it has to do destination manip */ 116 + nf_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); 117 + } 118 + 119 + /* outbound packets == from PNS to PAC */ 120 + static int 121 + pptp_outbound_pkt(struct sk_buff **pskb, 122 + struct nf_conn *ct, 123 + enum ip_conntrack_info ctinfo, 124 + struct PptpControlHeader *ctlh, 125 + union pptp_ctrl_union *pptpReq) 126 + 127 + { 128 + struct nf_ct_pptp_master *ct_pptp_info; 129 + struct nf_nat_pptp *nat_pptp_info; 130 + u_int16_t msg; 131 + __be16 new_callid; 132 + unsigned int cid_off; 133 + 134 + ct_pptp_info = &nfct_help(ct)->help.ct_pptp_info; 135 + nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; 136 + 137 + new_callid = ct_pptp_info->pns_call_id; 138 + 139 + switch (msg = ntohs(ctlh->messageType)) { 140 + case PPTP_OUT_CALL_REQUEST: 141 + cid_off = offsetof(union pptp_ctrl_union, ocreq.callID); 142 + /* FIXME: ideally we would want to reserve a call ID 143 + * here. current netfilter NAT core is not able to do 144 + * this :( For now we use TCP source port. This breaks 145 + * multiple calls within one control session */ 146 + 147 + /* save original call ID in nat_info */ 148 + nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id; 149 + 150 + /* don't use tcph->source since we are at a DSTmanip 151 + * hook (e.g. PREROUTING) and pkt is not mangled yet */ 152 + new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port; 153 + 154 + /* save new call ID in ct info */ 155 + ct_pptp_info->pns_call_id = new_callid; 156 + break; 157 + case PPTP_IN_CALL_REPLY: 158 + cid_off = offsetof(union pptp_ctrl_union, icack.callID); 159 + break; 160 + case PPTP_CALL_CLEAR_REQUEST: 161 + cid_off = offsetof(union pptp_ctrl_union, clrreq.callID); 162 + break; 163 + default: 164 + DEBUGP("unknown outbound packet 0x%04x:%s\n", msg, 165 + (msg <= PPTP_MSG_MAX)? 166 + pptp_msg_name[msg]:pptp_msg_name[0]); 167 + /* fall through */ 168 + case PPTP_SET_LINK_INFO: 169 + /* only need to NAT in case PAC is behind NAT box */ 170 + case PPTP_START_SESSION_REQUEST: 171 + case PPTP_START_SESSION_REPLY: 172 + case PPTP_STOP_SESSION_REQUEST: 173 + case PPTP_STOP_SESSION_REPLY: 174 + case PPTP_ECHO_REQUEST: 175 + case PPTP_ECHO_REPLY: 176 + /* no need to alter packet */ 177 + return NF_ACCEPT; 178 + } 179 + 180 + /* only OUT_CALL_REQUEST, IN_CALL_REPLY, CALL_CLEAR_REQUEST pass 181 + * down to here */ 182 + DEBUGP("altering call id from 0x%04x to 0x%04x\n", 183 + ntohs(REQ_CID(pptpReq, cid_off)), ntohs(new_callid)); 184 + 185 + /* mangle packet */ 186 + if (nf_nat_mangle_tcp_packet(pskb, ct, ctinfo, 187 + cid_off + sizeof(struct pptp_pkt_hdr) + 188 + sizeof(struct PptpControlHeader), 189 + sizeof(new_callid), (char *)&new_callid, 190 + sizeof(new_callid)) == 0) 191 + return NF_DROP; 192 + return NF_ACCEPT; 193 + } 194 + 195 + static void 196 + pptp_exp_gre(struct nf_conntrack_expect *expect_orig, 197 + struct nf_conntrack_expect *expect_reply) 198 + { 199 + struct nf_conn *ct = expect_orig->master; 200 + struct nf_ct_pptp_master *ct_pptp_info; 201 + struct nf_nat_pptp *nat_pptp_info; 202 + 203 + ct_pptp_info = &nfct_help(ct)->help.ct_pptp_info; 204 + nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; 205 + 206 + /* save original PAC call ID in nat_info */ 207 + nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; 208 + 209 + /* alter expectation for PNS->PAC direction */ 210 + expect_orig->saved_proto.gre.key = ct_pptp_info->pns_call_id; 211 + expect_orig->tuple.src.u.gre.key = nat_pptp_info->pns_call_id; 212 + expect_orig->tuple.dst.u.gre.key = ct_pptp_info->pac_call_id; 213 + expect_orig->dir = IP_CT_DIR_ORIGINAL; 214 + 215 + /* alter expectation for PAC->PNS direction */ 216 + expect_reply->saved_proto.gre.key = nat_pptp_info->pns_call_id; 217 + expect_reply->tuple.src.u.gre.key = nat_pptp_info->pac_call_id; 218 + expect_reply->tuple.dst.u.gre.key = ct_pptp_info->pns_call_id; 219 + expect_reply->dir = IP_CT_DIR_REPLY; 220 + } 221 + 222 + /* inbound packets == from PAC to PNS */ 223 + static int 224 + pptp_inbound_pkt(struct sk_buff **pskb, 225 + struct nf_conn *ct, 226 + enum ip_conntrack_info ctinfo, 227 + struct PptpControlHeader *ctlh, 228 + union pptp_ctrl_union *pptpReq) 229 + { 230 + struct nf_nat_pptp *nat_pptp_info; 231 + u_int16_t msg; 232 + __be16 new_pcid; 233 + unsigned int pcid_off; 234 + 235 + nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; 236 + new_pcid = nat_pptp_info->pns_call_id; 237 + 238 + switch (msg = ntohs(ctlh->messageType)) { 239 + case PPTP_OUT_CALL_REPLY: 240 + pcid_off = offsetof(union pptp_ctrl_union, ocack.peersCallID); 241 + break; 242 + case PPTP_IN_CALL_CONNECT: 243 + pcid_off = offsetof(union pptp_ctrl_union, iccon.peersCallID); 244 + break; 245 + case PPTP_IN_CALL_REQUEST: 246 + /* only need to nat in case PAC is behind NAT box */ 247 + return NF_ACCEPT; 248 + case PPTP_WAN_ERROR_NOTIFY: 249 + pcid_off = offsetof(union pptp_ctrl_union, wanerr.peersCallID); 250 + break; 251 + case PPTP_CALL_DISCONNECT_NOTIFY: 252 + pcid_off = offsetof(union pptp_ctrl_union, disc.callID); 253 + break; 254 + case PPTP_SET_LINK_INFO: 255 + pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID); 256 + break; 257 + default: 258 + DEBUGP("unknown inbound packet %s\n", (msg <= PPTP_MSG_MAX)? 259 + pptp_msg_name[msg]:pptp_msg_name[0]); 260 + /* fall through */ 261 + case PPTP_START_SESSION_REQUEST: 262 + case PPTP_START_SESSION_REPLY: 263 + case PPTP_STOP_SESSION_REQUEST: 264 + case PPTP_STOP_SESSION_REPLY: 265 + case PPTP_ECHO_REQUEST: 266 + case PPTP_ECHO_REPLY: 267 + /* no need to alter packet */ 268 + return NF_ACCEPT; 269 + } 270 + 271 + /* only OUT_CALL_REPLY, IN_CALL_CONNECT, IN_CALL_REQUEST, 272 + * WAN_ERROR_NOTIFY, CALL_DISCONNECT_NOTIFY pass down here */ 273 + 274 + /* mangle packet */ 275 + DEBUGP("altering peer call id from 0x%04x to 0x%04x\n", 276 + ntohs(REQ_CID(pptpReq, pcid_off)), ntohs(new_pcid)); 277 + 278 + if (nf_nat_mangle_tcp_packet(pskb, ct, ctinfo, 279 + pcid_off + sizeof(struct pptp_pkt_hdr) + 280 + sizeof(struct PptpControlHeader), 281 + sizeof(new_pcid), (char *)&new_pcid, 282 + sizeof(new_pcid)) == 0) 283 + return NF_DROP; 284 + return NF_ACCEPT; 285 + } 286 + 287 + static int __init nf_nat_helper_pptp_init(void) 288 + { 289 + nf_nat_need_gre(); 290 + 291 + BUG_ON(rcu_dereference(nf_nat_pptp_hook_outbound)); 292 + rcu_assign_pointer(nf_nat_pptp_hook_outbound, pptp_outbound_pkt); 293 + 294 + BUG_ON(rcu_dereference(nf_nat_pptp_hook_inbound)); 295 + rcu_assign_pointer(nf_nat_pptp_hook_inbound, pptp_inbound_pkt); 296 + 297 + BUG_ON(rcu_dereference(nf_nat_pptp_hook_exp_gre)); 298 + rcu_assign_pointer(nf_nat_pptp_hook_exp_gre, pptp_exp_gre); 299 + 300 + BUG_ON(rcu_dereference(nf_nat_pptp_hook_expectfn)); 301 + rcu_assign_pointer(nf_nat_pptp_hook_expectfn, pptp_nat_expected); 302 + return 0; 303 + } 304 + 305 + static void __exit nf_nat_helper_pptp_fini(void) 306 + { 307 + rcu_assign_pointer(nf_nat_pptp_hook_expectfn, NULL); 308 + rcu_assign_pointer(nf_nat_pptp_hook_exp_gre, NULL); 309 + rcu_assign_pointer(nf_nat_pptp_hook_inbound, NULL); 310 + rcu_assign_pointer(nf_nat_pptp_hook_outbound, NULL); 311 + synchronize_rcu(); 312 + } 313 + 314 + module_init(nf_nat_helper_pptp_init); 315 + module_exit(nf_nat_helper_pptp_fini);
+179
net/ipv4/netfilter/nf_nat_proto_gre.c
··· 1 + /* 2 + * nf_nat_proto_gre.c 3 + * 4 + * NAT protocol helper module for GRE. 5 + * 6 + * GRE is a generic encapsulation protocol, which is generally not very 7 + * suited for NAT, as it has no protocol-specific part as port numbers. 8 + * 9 + * It has an optional key field, which may help us distinguishing two 10 + * connections between the same two hosts. 11 + * 12 + * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 13 + * 14 + * PPTP is built on top of a modified version of GRE, and has a mandatory 15 + * field called "CallID", which serves us for the same purpose as the key 16 + * field in plain GRE. 17 + * 18 + * Documentation about PPTP can be found in RFC 2637 19 + * 20 + * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org> 21 + * 22 + * Development of this code funded by Astaro AG (http://www.astaro.com/) 23 + * 24 + */ 25 + 26 + #include <linux/module.h> 27 + #include <linux/skbuff.h> 28 + #include <linux/ip.h> 29 + 30 + #include <net/netfilter/nf_nat.h> 31 + #include <net/netfilter/nf_nat_rule.h> 32 + #include <net/netfilter/nf_nat_protocol.h> 33 + #include <linux/netfilter/nf_conntrack_proto_gre.h> 34 + 35 + MODULE_LICENSE("GPL"); 36 + MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); 37 + MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE"); 38 + 39 + #if 0 40 + #define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \ 41 + __FUNCTION__, ## args) 42 + #else 43 + #define DEBUGP(x, args...) 44 + #endif 45 + 46 + /* is key in given range between min and max */ 47 + static int 48 + gre_in_range(const struct nf_conntrack_tuple *tuple, 49 + enum nf_nat_manip_type maniptype, 50 + const union nf_conntrack_man_proto *min, 51 + const union nf_conntrack_man_proto *max) 52 + { 53 + __be16 key; 54 + 55 + if (maniptype == IP_NAT_MANIP_SRC) 56 + key = tuple->src.u.gre.key; 57 + else 58 + key = tuple->dst.u.gre.key; 59 + 60 + return ntohs(key) >= ntohs(min->gre.key) && 61 + ntohs(key) <= ntohs(max->gre.key); 62 + } 63 + 64 + /* generate unique tuple ... */ 65 + static int 66 + gre_unique_tuple(struct nf_conntrack_tuple *tuple, 67 + const struct nf_nat_range *range, 68 + enum nf_nat_manip_type maniptype, 69 + const struct nf_conn *conntrack) 70 + { 71 + static u_int16_t key; 72 + __be16 *keyptr; 73 + unsigned int min, i, range_size; 74 + 75 + if (maniptype == IP_NAT_MANIP_SRC) 76 + keyptr = &tuple->src.u.gre.key; 77 + else 78 + keyptr = &tuple->dst.u.gre.key; 79 + 80 + if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { 81 + DEBUGP("%p: NATing GRE PPTP\n", conntrack); 82 + min = 1; 83 + range_size = 0xffff; 84 + } else { 85 + min = ntohs(range->min.gre.key); 86 + range_size = ntohs(range->max.gre.key) - min + 1; 87 + } 88 + 89 + DEBUGP("min = %u, range_size = %u\n", min, range_size); 90 + 91 + for (i = 0; i < range_size; i++, key++) { 92 + *keyptr = htons(min + key % range_size); 93 + if (!nf_nat_used_tuple(tuple, conntrack)) 94 + return 1; 95 + } 96 + 97 + DEBUGP("%p: no NAT mapping\n", conntrack); 98 + return 0; 99 + } 100 + 101 + /* manipulate a GRE packet according to maniptype */ 102 + static int 103 + gre_manip_pkt(struct sk_buff **pskb, unsigned int iphdroff, 104 + const struct nf_conntrack_tuple *tuple, 105 + enum nf_nat_manip_type maniptype) 106 + { 107 + struct gre_hdr *greh; 108 + struct gre_hdr_pptp *pgreh; 109 + struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); 110 + unsigned int hdroff = iphdroff + iph->ihl * 4; 111 + 112 + /* pgreh includes two optional 32bit fields which are not required 113 + * to be there. That's where the magic '8' comes from */ 114 + if (!skb_make_writable(pskb, hdroff + sizeof(*pgreh) - 8)) 115 + return 0; 116 + 117 + greh = (void *)(*pskb)->data + hdroff; 118 + pgreh = (struct gre_hdr_pptp *)greh; 119 + 120 + /* we only have destination manip of a packet, since 'source key' 121 + * is not present in the packet itself */ 122 + if (maniptype != IP_NAT_MANIP_DST) 123 + return 1; 124 + switch (greh->version) { 125 + case 0: 126 + if (!greh->key) { 127 + DEBUGP("can't nat GRE w/o key\n"); 128 + break; 129 + } 130 + if (greh->csum) { 131 + /* FIXME: Never tested this code... */ 132 + nf_proto_csum_replace4(gre_csum(greh), *pskb, 133 + *(gre_key(greh)), 134 + tuple->dst.u.gre.key, 0); 135 + } 136 + *(gre_key(greh)) = tuple->dst.u.gre.key; 137 + break; 138 + case GRE_VERSION_PPTP: 139 + DEBUGP("call_id -> 0x%04x\n", ntohs(tuple->dst.u.gre.key)); 140 + pgreh->call_id = tuple->dst.u.gre.key; 141 + break; 142 + default: 143 + DEBUGP("can't nat unknown GRE version\n"); 144 + return 0; 145 + } 146 + return 1; 147 + } 148 + 149 + static struct nf_nat_protocol gre __read_mostly = { 150 + .name = "GRE", 151 + .protonum = IPPROTO_GRE, 152 + .manip_pkt = gre_manip_pkt, 153 + .in_range = gre_in_range, 154 + .unique_tuple = gre_unique_tuple, 155 + #if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ 156 + defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) 157 + .range_to_nfattr = nf_nat_port_range_to_nfattr, 158 + .nfattr_to_range = nf_nat_port_nfattr_to_range, 159 + #endif 160 + }; 161 + 162 + int __init nf_nat_proto_gre_init(void) 163 + { 164 + return nf_nat_protocol_register(&gre); 165 + } 166 + 167 + void __exit nf_nat_proto_gre_fini(void) 168 + { 169 + nf_nat_protocol_unregister(&gre); 170 + } 171 + 172 + module_init(nf_nat_proto_gre_init); 173 + module_exit(nf_nat_proto_gre_fini); 174 + 175 + void nf_nat_need_gre(void) 176 + { 177 + return; 178 + } 179 + EXPORT_SYMBOL_GPL(nf_nat_need_gre);
+23
net/netfilter/Kconfig
··· 120 120 121 121 If unsure, say `N'. 122 122 123 + config NF_CT_PROTO_GRE 124 + tristate 125 + depends on EXPERIMENTAL && NF_CONNTRACK 126 + 123 127 config NF_CT_PROTO_SCTP 124 128 tristate 'SCTP protocol on new connection tracking support (EXPERIMENTAL)' 125 129 depends on EXPERIMENTAL && NF_CONNTRACK ··· 214 210 $ ip -4 address show eth0 215 211 4: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 1000 216 212 inet 172.16.2.252/24 brd 172.16.2.255 scope global eth0 213 + 214 + To compile it as a module, choose M here. If unsure, say N. 215 + 216 + config NF_CONNTRACK_PPTP 217 + tristate "PPtP protocol support (EXPERIMENTAL)" 218 + depends on EXPERIMENTAL && NF_CONNTRACK 219 + select NF_CT_PROTO_GRE 220 + help 221 + This module adds support for PPTP (Point to Point Tunnelling 222 + Protocol, RFC2637) connection tracking and NAT. 223 + 224 + If you are running PPTP sessions over a stateful firewall or NAT 225 + box, you may want to enable this feature. 226 + 227 + Please note that not all PPTP modes of operation are supported yet. 228 + Specifically these limitations exist: 229 + - Blindy assumes that control connections are always established 230 + in PNS->PAC direction. This is a violation of RFC2637. 231 + - Only supports a single call within each session 217 232 218 233 To compile it as a module, choose M here. If unsure, say N. 219 234
+2
net/netfilter/Makefile
··· 14 14 obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o 15 15 16 16 # SCTP protocol connection tracking 17 + obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o 17 18 obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o 18 19 19 20 # netlink interface for nf_conntrack ··· 28 27 obj-$(CONFIG_NF_CONNTRACK_H323) += nf_conntrack_h323.o 29 28 obj-$(CONFIG_NF_CONNTRACK_IRC) += nf_conntrack_irc.o 30 29 obj-$(CONFIG_NF_CONNTRACK_NETBIOS_NS) += nf_conntrack_netbios_ns.o 30 + obj-$(CONFIG_NF_CONNTRACK_PPTP) += nf_conntrack_pptp.o 31 31 32 32 # generic X tables 33 33 obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
+4
net/netfilter/nf_conntrack_core.c
··· 300 300 destroy_conntrack(struct nf_conntrack *nfct) 301 301 { 302 302 struct nf_conn *ct = (struct nf_conn *)nfct; 303 + struct nf_conn_help *help = nfct_help(ct); 303 304 struct nf_conntrack_l3proto *l3proto; 304 305 struct nf_conntrack_l4proto *l4proto; 305 306 ··· 310 309 311 310 nf_conntrack_event(IPCT_DESTROY, ct); 312 311 set_bit(IPS_DYING_BIT, &ct->status); 312 + 313 + if (help && help->helper && help->helper->destroy) 314 + help->helper->destroy(ct); 313 315 314 316 /* To make sure we don't get any weird locking issues here: 315 317 * destroy_conntrack() MUST NOT be called with a write lock
+607
net/netfilter/nf_conntrack_pptp.c
··· 1 + /* 2 + * Connection tracking support for PPTP (Point to Point Tunneling Protocol). 3 + * PPTP is a a protocol for creating virtual private networks. 4 + * It is a specification defined by Microsoft and some vendors 5 + * working with Microsoft. PPTP is built on top of a modified 6 + * version of the Internet Generic Routing Encapsulation Protocol. 7 + * GRE is defined in RFC 1701 and RFC 1702. Documentation of 8 + * PPTP can be found in RFC 2637 9 + * 10 + * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org> 11 + * 12 + * Development of this code funded by Astaro AG (http://www.astaro.com/) 13 + * 14 + * Limitations: 15 + * - We blindly assume that control connections are always 16 + * established in PNS->PAC direction. This is a violation 17 + * of RFFC2673 18 + * - We can only support one single call within each session 19 + * TODO: 20 + * - testing of incoming PPTP calls 21 + */ 22 + 23 + #include <linux/module.h> 24 + #include <linux/skbuff.h> 25 + #include <linux/in.h> 26 + #include <linux/tcp.h> 27 + 28 + #include <net/netfilter/nf_conntrack.h> 29 + #include <net/netfilter/nf_conntrack_core.h> 30 + #include <net/netfilter/nf_conntrack_helper.h> 31 + #include <linux/netfilter/nf_conntrack_proto_gre.h> 32 + #include <linux/netfilter/nf_conntrack_pptp.h> 33 + 34 + #define NF_CT_PPTP_VERSION "3.1" 35 + 36 + MODULE_LICENSE("GPL"); 37 + MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); 38 + MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP"); 39 + MODULE_ALIAS("ip_conntrack_pptp"); 40 + 41 + static DEFINE_SPINLOCK(nf_pptp_lock); 42 + 43 + int 44 + (*nf_nat_pptp_hook_outbound)(struct sk_buff **pskb, 45 + struct nf_conn *ct, enum ip_conntrack_info ctinfo, 46 + struct PptpControlHeader *ctlh, 47 + union pptp_ctrl_union *pptpReq) __read_mostly; 48 + EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_outbound); 49 + 50 + int 51 + (*nf_nat_pptp_hook_inbound)(struct sk_buff **pskb, 52 + struct nf_conn *ct, enum ip_conntrack_info ctinfo, 53 + struct PptpControlHeader *ctlh, 54 + union pptp_ctrl_union *pptpReq) __read_mostly; 55 + EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_inbound); 56 + 57 + void 58 + (*nf_nat_pptp_hook_exp_gre)(struct nf_conntrack_expect *expect_orig, 59 + struct nf_conntrack_expect *expect_reply) 60 + __read_mostly; 61 + EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_exp_gre); 62 + 63 + void 64 + (*nf_nat_pptp_hook_expectfn)(struct nf_conn *ct, 65 + struct nf_conntrack_expect *exp) __read_mostly; 66 + EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn); 67 + 68 + #if 0 69 + /* PptpControlMessageType names */ 70 + const char *pptp_msg_name[] = { 71 + "UNKNOWN_MESSAGE", 72 + "START_SESSION_REQUEST", 73 + "START_SESSION_REPLY", 74 + "STOP_SESSION_REQUEST", 75 + "STOP_SESSION_REPLY", 76 + "ECHO_REQUEST", 77 + "ECHO_REPLY", 78 + "OUT_CALL_REQUEST", 79 + "OUT_CALL_REPLY", 80 + "IN_CALL_REQUEST", 81 + "IN_CALL_REPLY", 82 + "IN_CALL_CONNECT", 83 + "CALL_CLEAR_REQUEST", 84 + "CALL_DISCONNECT_NOTIFY", 85 + "WAN_ERROR_NOTIFY", 86 + "SET_LINK_INFO" 87 + }; 88 + EXPORT_SYMBOL(pptp_msg_name); 89 + #define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args) 90 + #else 91 + #define DEBUGP(format, args...) 92 + #endif 93 + 94 + #define SECS *HZ 95 + #define MINS * 60 SECS 96 + #define HOURS * 60 MINS 97 + 98 + #define PPTP_GRE_TIMEOUT (10 MINS) 99 + #define PPTP_GRE_STREAM_TIMEOUT (5 HOURS) 100 + 101 + static void pptp_expectfn(struct nf_conn *ct, 102 + struct nf_conntrack_expect *exp) 103 + { 104 + typeof(nf_nat_pptp_hook_expectfn) nf_nat_pptp_expectfn; 105 + DEBUGP("increasing timeouts\n"); 106 + 107 + /* increase timeout of GRE data channel conntrack entry */ 108 + ct->proto.gre.timeout = PPTP_GRE_TIMEOUT; 109 + ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT; 110 + 111 + /* Can you see how rusty this code is, compared with the pre-2.6.11 112 + * one? That's what happened to my shiny newnat of 2002 ;( -HW */ 113 + 114 + rcu_read_lock(); 115 + nf_nat_pptp_expectfn = rcu_dereference(nf_nat_pptp_hook_expectfn); 116 + if (nf_nat_pptp_expectfn && ct->status & IPS_NAT_MASK) 117 + nf_nat_pptp_expectfn(ct, exp); 118 + else { 119 + struct nf_conntrack_tuple inv_t; 120 + struct nf_conntrack_expect *exp_other; 121 + 122 + /* obviously this tuple inversion only works until you do NAT */ 123 + nf_ct_invert_tuplepr(&inv_t, &exp->tuple); 124 + DEBUGP("trying to unexpect other dir: "); 125 + NF_CT_DUMP_TUPLE(&inv_t); 126 + 127 + exp_other = nf_conntrack_expect_find_get(&inv_t); 128 + if (exp_other) { 129 + /* delete other expectation. */ 130 + DEBUGP("found\n"); 131 + nf_conntrack_unexpect_related(exp_other); 132 + nf_conntrack_expect_put(exp_other); 133 + } else { 134 + DEBUGP("not found\n"); 135 + } 136 + } 137 + rcu_read_unlock(); 138 + } 139 + 140 + static int destroy_sibling_or_exp(const struct nf_conntrack_tuple *t) 141 + { 142 + struct nf_conntrack_tuple_hash *h; 143 + struct nf_conntrack_expect *exp; 144 + struct nf_conn *sibling; 145 + 146 + DEBUGP("trying to timeout ct or exp for tuple "); 147 + NF_CT_DUMP_TUPLE(t); 148 + 149 + h = nf_conntrack_find_get(t, NULL); 150 + if (h) { 151 + sibling = nf_ct_tuplehash_to_ctrack(h); 152 + DEBUGP("setting timeout of conntrack %p to 0\n", sibling); 153 + sibling->proto.gre.timeout = 0; 154 + sibling->proto.gre.stream_timeout = 0; 155 + if (del_timer(&sibling->timeout)) 156 + sibling->timeout.function((unsigned long)sibling); 157 + nf_ct_put(sibling); 158 + return 1; 159 + } else { 160 + exp = nf_conntrack_expect_find_get(t); 161 + if (exp) { 162 + DEBUGP("unexpect_related of expect %p\n", exp); 163 + nf_conntrack_unexpect_related(exp); 164 + nf_conntrack_expect_put(exp); 165 + return 1; 166 + } 167 + } 168 + return 0; 169 + } 170 + 171 + /* timeout GRE data connections */ 172 + static void pptp_destroy_siblings(struct nf_conn *ct) 173 + { 174 + struct nf_conn_help *help = nfct_help(ct); 175 + struct nf_conntrack_tuple t; 176 + 177 + nf_ct_gre_keymap_destroy(ct); 178 + 179 + /* try original (pns->pac) tuple */ 180 + memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t)); 181 + t.dst.protonum = IPPROTO_GRE; 182 + t.src.u.gre.key = help->help.ct_pptp_info.pns_call_id; 183 + t.dst.u.gre.key = help->help.ct_pptp_info.pac_call_id; 184 + if (!destroy_sibling_or_exp(&t)) 185 + DEBUGP("failed to timeout original pns->pac ct/exp\n"); 186 + 187 + /* try reply (pac->pns) tuple */ 188 + memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t)); 189 + t.dst.protonum = IPPROTO_GRE; 190 + t.src.u.gre.key = help->help.ct_pptp_info.pac_call_id; 191 + t.dst.u.gre.key = help->help.ct_pptp_info.pns_call_id; 192 + if (!destroy_sibling_or_exp(&t)) 193 + DEBUGP("failed to timeout reply pac->pns ct/exp\n"); 194 + } 195 + 196 + /* expect GRE connections (PNS->PAC and PAC->PNS direction) */ 197 + static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid) 198 + { 199 + struct nf_conntrack_expect *exp_orig, *exp_reply; 200 + enum ip_conntrack_dir dir; 201 + int ret = 1; 202 + typeof(nf_nat_pptp_hook_exp_gre) nf_nat_pptp_exp_gre; 203 + 204 + exp_orig = nf_conntrack_expect_alloc(ct); 205 + if (exp_orig == NULL) 206 + goto out; 207 + 208 + exp_reply = nf_conntrack_expect_alloc(ct); 209 + if (exp_reply == NULL) 210 + goto out_put_orig; 211 + 212 + /* original direction, PNS->PAC */ 213 + dir = IP_CT_DIR_ORIGINAL; 214 + nf_conntrack_expect_init(exp_orig, ct->tuplehash[dir].tuple.src.l3num, 215 + &ct->tuplehash[dir].tuple.src.u3, 216 + &ct->tuplehash[dir].tuple.dst.u3, 217 + IPPROTO_GRE, &peer_callid, &callid); 218 + exp_orig->expectfn = pptp_expectfn; 219 + 220 + /* reply direction, PAC->PNS */ 221 + dir = IP_CT_DIR_REPLY; 222 + nf_conntrack_expect_init(exp_reply, ct->tuplehash[dir].tuple.src.l3num, 223 + &ct->tuplehash[dir].tuple.src.u3, 224 + &ct->tuplehash[dir].tuple.dst.u3, 225 + IPPROTO_GRE, &callid, &peer_callid); 226 + exp_reply->expectfn = pptp_expectfn; 227 + 228 + nf_nat_pptp_exp_gre = rcu_dereference(nf_nat_pptp_hook_exp_gre); 229 + if (nf_nat_pptp_exp_gre && ct->status & IPS_NAT_MASK) 230 + nf_nat_pptp_exp_gre(exp_orig, exp_reply); 231 + if (nf_conntrack_expect_related(exp_orig) != 0) 232 + goto out_put_both; 233 + if (nf_conntrack_expect_related(exp_reply) != 0) 234 + goto out_unexpect_orig; 235 + 236 + /* Add GRE keymap entries */ 237 + if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_ORIGINAL, &exp_orig->tuple) != 0) 238 + goto out_unexpect_both; 239 + if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_REPLY, &exp_reply->tuple) != 0) { 240 + nf_ct_gre_keymap_destroy(ct); 241 + goto out_unexpect_both; 242 + } 243 + ret = 0; 244 + 245 + out_put_both: 246 + nf_conntrack_expect_put(exp_reply); 247 + out_put_orig: 248 + nf_conntrack_expect_put(exp_orig); 249 + out: 250 + return ret; 251 + 252 + out_unexpect_both: 253 + nf_conntrack_unexpect_related(exp_reply); 254 + out_unexpect_orig: 255 + nf_conntrack_unexpect_related(exp_orig); 256 + goto out_put_both; 257 + } 258 + 259 + static inline int 260 + pptp_inbound_pkt(struct sk_buff **pskb, 261 + struct PptpControlHeader *ctlh, 262 + union pptp_ctrl_union *pptpReq, 263 + unsigned int reqlen, 264 + struct nf_conn *ct, 265 + enum ip_conntrack_info ctinfo) 266 + { 267 + struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info; 268 + u_int16_t msg; 269 + __be16 cid = 0, pcid = 0; 270 + typeof(nf_nat_pptp_hook_inbound) nf_nat_pptp_inbound; 271 + 272 + msg = ntohs(ctlh->messageType); 273 + DEBUGP("inbound control message %s\n", pptp_msg_name[msg]); 274 + 275 + switch (msg) { 276 + case PPTP_START_SESSION_REPLY: 277 + /* server confirms new control session */ 278 + if (info->sstate < PPTP_SESSION_REQUESTED) 279 + goto invalid; 280 + if (pptpReq->srep.resultCode == PPTP_START_OK) 281 + info->sstate = PPTP_SESSION_CONFIRMED; 282 + else 283 + info->sstate = PPTP_SESSION_ERROR; 284 + break; 285 + 286 + case PPTP_STOP_SESSION_REPLY: 287 + /* server confirms end of control session */ 288 + if (info->sstate > PPTP_SESSION_STOPREQ) 289 + goto invalid; 290 + if (pptpReq->strep.resultCode == PPTP_STOP_OK) 291 + info->sstate = PPTP_SESSION_NONE; 292 + else 293 + info->sstate = PPTP_SESSION_ERROR; 294 + break; 295 + 296 + case PPTP_OUT_CALL_REPLY: 297 + /* server accepted call, we now expect GRE frames */ 298 + if (info->sstate != PPTP_SESSION_CONFIRMED) 299 + goto invalid; 300 + if (info->cstate != PPTP_CALL_OUT_REQ && 301 + info->cstate != PPTP_CALL_OUT_CONF) 302 + goto invalid; 303 + 304 + cid = pptpReq->ocack.callID; 305 + pcid = pptpReq->ocack.peersCallID; 306 + if (info->pns_call_id != pcid) 307 + goto invalid; 308 + DEBUGP("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg], 309 + ntohs(cid), ntohs(pcid)); 310 + 311 + if (pptpReq->ocack.resultCode == PPTP_OUTCALL_CONNECT) { 312 + info->cstate = PPTP_CALL_OUT_CONF; 313 + info->pac_call_id = cid; 314 + exp_gre(ct, cid, pcid); 315 + } else 316 + info->cstate = PPTP_CALL_NONE; 317 + break; 318 + 319 + case PPTP_IN_CALL_REQUEST: 320 + /* server tells us about incoming call request */ 321 + if (info->sstate != PPTP_SESSION_CONFIRMED) 322 + goto invalid; 323 + 324 + cid = pptpReq->icreq.callID; 325 + DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); 326 + info->cstate = PPTP_CALL_IN_REQ; 327 + info->pac_call_id = cid; 328 + break; 329 + 330 + case PPTP_IN_CALL_CONNECT: 331 + /* server tells us about incoming call established */ 332 + if (info->sstate != PPTP_SESSION_CONFIRMED) 333 + goto invalid; 334 + if (info->cstate != PPTP_CALL_IN_REP && 335 + info->cstate != PPTP_CALL_IN_CONF) 336 + goto invalid; 337 + 338 + pcid = pptpReq->iccon.peersCallID; 339 + cid = info->pac_call_id; 340 + 341 + if (info->pns_call_id != pcid) 342 + goto invalid; 343 + 344 + DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(pcid)); 345 + info->cstate = PPTP_CALL_IN_CONF; 346 + 347 + /* we expect a GRE connection from PAC to PNS */ 348 + exp_gre(ct, cid, pcid); 349 + break; 350 + 351 + case PPTP_CALL_DISCONNECT_NOTIFY: 352 + /* server confirms disconnect */ 353 + cid = pptpReq->disc.callID; 354 + DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); 355 + info->cstate = PPTP_CALL_NONE; 356 + 357 + /* untrack this call id, unexpect GRE packets */ 358 + pptp_destroy_siblings(ct); 359 + break; 360 + 361 + case PPTP_WAN_ERROR_NOTIFY: 362 + case PPTP_ECHO_REQUEST: 363 + case PPTP_ECHO_REPLY: 364 + /* I don't have to explain these ;) */ 365 + break; 366 + 367 + default: 368 + goto invalid; 369 + } 370 + 371 + nf_nat_pptp_inbound = rcu_dereference(nf_nat_pptp_hook_inbound); 372 + if (nf_nat_pptp_inbound && ct->status & IPS_NAT_MASK) 373 + return nf_nat_pptp_inbound(pskb, ct, ctinfo, ctlh, pptpReq); 374 + return NF_ACCEPT; 375 + 376 + invalid: 377 + DEBUGP("invalid %s: type=%d cid=%u pcid=%u " 378 + "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n", 379 + msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0], 380 + msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate, 381 + ntohs(info->pns_call_id), ntohs(info->pac_call_id)); 382 + return NF_ACCEPT; 383 + } 384 + 385 + static inline int 386 + pptp_outbound_pkt(struct sk_buff **pskb, 387 + struct PptpControlHeader *ctlh, 388 + union pptp_ctrl_union *pptpReq, 389 + unsigned int reqlen, 390 + struct nf_conn *ct, 391 + enum ip_conntrack_info ctinfo) 392 + { 393 + struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info; 394 + u_int16_t msg; 395 + __be16 cid = 0, pcid = 0; 396 + typeof(nf_nat_pptp_hook_outbound) nf_nat_pptp_outbound; 397 + 398 + msg = ntohs(ctlh->messageType); 399 + DEBUGP("outbound control message %s\n", pptp_msg_name[msg]); 400 + 401 + switch (msg) { 402 + case PPTP_START_SESSION_REQUEST: 403 + /* client requests for new control session */ 404 + if (info->sstate != PPTP_SESSION_NONE) 405 + goto invalid; 406 + info->sstate = PPTP_SESSION_REQUESTED; 407 + break; 408 + 409 + case PPTP_STOP_SESSION_REQUEST: 410 + /* client requests end of control session */ 411 + info->sstate = PPTP_SESSION_STOPREQ; 412 + break; 413 + 414 + case PPTP_OUT_CALL_REQUEST: 415 + /* client initiating connection to server */ 416 + if (info->sstate != PPTP_SESSION_CONFIRMED) 417 + goto invalid; 418 + info->cstate = PPTP_CALL_OUT_REQ; 419 + /* track PNS call id */ 420 + cid = pptpReq->ocreq.callID; 421 + DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); 422 + info->pns_call_id = cid; 423 + break; 424 + 425 + case PPTP_IN_CALL_REPLY: 426 + /* client answers incoming call */ 427 + if (info->cstate != PPTP_CALL_IN_REQ && 428 + info->cstate != PPTP_CALL_IN_REP) 429 + goto invalid; 430 + 431 + cid = pptpReq->icack.callID; 432 + pcid = pptpReq->icack.peersCallID; 433 + if (info->pac_call_id != pcid) 434 + goto invalid; 435 + DEBUGP("%s, CID=%X PCID=%X\n", pptp_msg_name[msg], 436 + ntohs(cid), ntohs(pcid)); 437 + 438 + if (pptpReq->icack.resultCode == PPTP_INCALL_ACCEPT) { 439 + /* part two of the three-way handshake */ 440 + info->cstate = PPTP_CALL_IN_REP; 441 + info->pns_call_id = cid; 442 + } else 443 + info->cstate = PPTP_CALL_NONE; 444 + break; 445 + 446 + case PPTP_CALL_CLEAR_REQUEST: 447 + /* client requests hangup of call */ 448 + if (info->sstate != PPTP_SESSION_CONFIRMED) 449 + goto invalid; 450 + /* FUTURE: iterate over all calls and check if 451 + * call ID is valid. We don't do this without newnat, 452 + * because we only know about last call */ 453 + info->cstate = PPTP_CALL_CLEAR_REQ; 454 + break; 455 + 456 + case PPTP_SET_LINK_INFO: 457 + case PPTP_ECHO_REQUEST: 458 + case PPTP_ECHO_REPLY: 459 + /* I don't have to explain these ;) */ 460 + break; 461 + 462 + default: 463 + goto invalid; 464 + } 465 + 466 + nf_nat_pptp_outbound = rcu_dereference(nf_nat_pptp_hook_outbound); 467 + if (nf_nat_pptp_outbound && ct->status & IPS_NAT_MASK) 468 + return nf_nat_pptp_outbound(pskb, ct, ctinfo, ctlh, pptpReq); 469 + return NF_ACCEPT; 470 + 471 + invalid: 472 + DEBUGP("invalid %s: type=%d cid=%u pcid=%u " 473 + "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n", 474 + msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0], 475 + msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate, 476 + ntohs(info->pns_call_id), ntohs(info->pac_call_id)); 477 + return NF_ACCEPT; 478 + } 479 + 480 + static const unsigned int pptp_msg_size[] = { 481 + [PPTP_START_SESSION_REQUEST] = sizeof(struct PptpStartSessionRequest), 482 + [PPTP_START_SESSION_REPLY] = sizeof(struct PptpStartSessionReply), 483 + [PPTP_STOP_SESSION_REQUEST] = sizeof(struct PptpStopSessionRequest), 484 + [PPTP_STOP_SESSION_REPLY] = sizeof(struct PptpStopSessionReply), 485 + [PPTP_OUT_CALL_REQUEST] = sizeof(struct PptpOutCallRequest), 486 + [PPTP_OUT_CALL_REPLY] = sizeof(struct PptpOutCallReply), 487 + [PPTP_IN_CALL_REQUEST] = sizeof(struct PptpInCallRequest), 488 + [PPTP_IN_CALL_REPLY] = sizeof(struct PptpInCallReply), 489 + [PPTP_IN_CALL_CONNECT] = sizeof(struct PptpInCallConnected), 490 + [PPTP_CALL_CLEAR_REQUEST] = sizeof(struct PptpClearCallRequest), 491 + [PPTP_CALL_DISCONNECT_NOTIFY] = sizeof(struct PptpCallDisconnectNotify), 492 + [PPTP_WAN_ERROR_NOTIFY] = sizeof(struct PptpWanErrorNotify), 493 + [PPTP_SET_LINK_INFO] = sizeof(struct PptpSetLinkInfo), 494 + }; 495 + 496 + /* track caller id inside control connection, call expect_related */ 497 + static int 498 + conntrack_pptp_help(struct sk_buff **pskb, unsigned int protoff, 499 + struct nf_conn *ct, enum ip_conntrack_info ctinfo) 500 + 501 + { 502 + int dir = CTINFO2DIR(ctinfo); 503 + struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info; 504 + struct tcphdr _tcph, *tcph; 505 + struct pptp_pkt_hdr _pptph, *pptph; 506 + struct PptpControlHeader _ctlh, *ctlh; 507 + union pptp_ctrl_union _pptpReq, *pptpReq; 508 + unsigned int tcplen = (*pskb)->len - protoff; 509 + unsigned int datalen, reqlen, nexthdr_off; 510 + int oldsstate, oldcstate; 511 + int ret; 512 + u_int16_t msg; 513 + 514 + /* don't do any tracking before tcp handshake complete */ 515 + if (ctinfo != IP_CT_ESTABLISHED && 516 + ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) 517 + return NF_ACCEPT; 518 + 519 + nexthdr_off = protoff; 520 + tcph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_tcph), &_tcph); 521 + BUG_ON(!tcph); 522 + nexthdr_off += tcph->doff * 4; 523 + datalen = tcplen - tcph->doff * 4; 524 + 525 + pptph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_pptph), &_pptph); 526 + if (!pptph) { 527 + DEBUGP("no full PPTP header, can't track\n"); 528 + return NF_ACCEPT; 529 + } 530 + nexthdr_off += sizeof(_pptph); 531 + datalen -= sizeof(_pptph); 532 + 533 + /* if it's not a control message we can't do anything with it */ 534 + if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || 535 + ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { 536 + DEBUGP("not a control packet\n"); 537 + return NF_ACCEPT; 538 + } 539 + 540 + ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh); 541 + if (!ctlh) 542 + return NF_ACCEPT; 543 + nexthdr_off += sizeof(_ctlh); 544 + datalen -= sizeof(_ctlh); 545 + 546 + reqlen = datalen; 547 + msg = ntohs(ctlh->messageType); 548 + if (msg > 0 && msg <= PPTP_MSG_MAX && reqlen < pptp_msg_size[msg]) 549 + return NF_ACCEPT; 550 + if (reqlen > sizeof(*pptpReq)) 551 + reqlen = sizeof(*pptpReq); 552 + 553 + pptpReq = skb_header_pointer(*pskb, nexthdr_off, reqlen, &_pptpReq); 554 + if (!pptpReq) 555 + return NF_ACCEPT; 556 + 557 + oldsstate = info->sstate; 558 + oldcstate = info->cstate; 559 + 560 + spin_lock_bh(&nf_pptp_lock); 561 + 562 + /* FIXME: We just blindly assume that the control connection is always 563 + * established from PNS->PAC. However, RFC makes no guarantee */ 564 + if (dir == IP_CT_DIR_ORIGINAL) 565 + /* client -> server (PNS -> PAC) */ 566 + ret = pptp_outbound_pkt(pskb, ctlh, pptpReq, reqlen, ct, 567 + ctinfo); 568 + else 569 + /* server -> client (PAC -> PNS) */ 570 + ret = pptp_inbound_pkt(pskb, ctlh, pptpReq, reqlen, ct, 571 + ctinfo); 572 + DEBUGP("sstate: %d->%d, cstate: %d->%d\n", 573 + oldsstate, info->sstate, oldcstate, info->cstate); 574 + spin_unlock_bh(&nf_pptp_lock); 575 + 576 + return ret; 577 + } 578 + 579 + /* control protocol helper */ 580 + static struct nf_conntrack_helper pptp __read_mostly = { 581 + .name = "pptp", 582 + .me = THIS_MODULE, 583 + .max_expected = 2, 584 + .timeout = 5 * 60, 585 + .tuple.src.l3num = AF_INET, 586 + .tuple.src.u.tcp.port = __constant_htons(PPTP_CONTROL_PORT), 587 + .tuple.dst.protonum = IPPROTO_TCP, 588 + .mask.src.l3num = 0xffff, 589 + .mask.src.u.tcp.port = __constant_htons(0xffff), 590 + .mask.dst.protonum = 0xff, 591 + .help = conntrack_pptp_help, 592 + .destroy = pptp_destroy_siblings, 593 + }; 594 + 595 + static int __init nf_conntrack_pptp_init(void) 596 + { 597 + return nf_conntrack_helper_register(&pptp); 598 + } 599 + 600 + static void __exit nf_conntrack_pptp_fini(void) 601 + { 602 + nf_conntrack_helper_unregister(&pptp); 603 + nf_ct_gre_keymap_flush(); 604 + } 605 + 606 + module_init(nf_conntrack_pptp_init); 607 + module_exit(nf_conntrack_pptp_fini);
+305
net/netfilter/nf_conntrack_proto_gre.c
··· 1 + /* 2 + * ip_conntrack_proto_gre.c - Version 3.0 3 + * 4 + * Connection tracking protocol helper module for GRE. 5 + * 6 + * GRE is a generic encapsulation protocol, which is generally not very 7 + * suited for NAT, as it has no protocol-specific part as port numbers. 8 + * 9 + * It has an optional key field, which may help us distinguishing two 10 + * connections between the same two hosts. 11 + * 12 + * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 13 + * 14 + * PPTP is built on top of a modified version of GRE, and has a mandatory 15 + * field called "CallID", which serves us for the same purpose as the key 16 + * field in plain GRE. 17 + * 18 + * Documentation about PPTP can be found in RFC 2637 19 + * 20 + * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org> 21 + * 22 + * Development of this code funded by Astaro AG (http://www.astaro.com/) 23 + * 24 + */ 25 + 26 + #include <linux/module.h> 27 + #include <linux/types.h> 28 + #include <linux/timer.h> 29 + #include <linux/list.h> 30 + #include <linux/seq_file.h> 31 + #include <linux/in.h> 32 + #include <linux/skbuff.h> 33 + 34 + #include <net/netfilter/nf_conntrack_l4proto.h> 35 + #include <net/netfilter/nf_conntrack_helper.h> 36 + #include <net/netfilter/nf_conntrack_core.h> 37 + #include <linux/netfilter/nf_conntrack_proto_gre.h> 38 + #include <linux/netfilter/nf_conntrack_pptp.h> 39 + 40 + #define GRE_TIMEOUT (30 * HZ) 41 + #define GRE_STREAM_TIMEOUT (180 * HZ) 42 + 43 + #if 0 44 + #define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args) 45 + #else 46 + #define DEBUGP(x, args...) 47 + #endif 48 + 49 + static DEFINE_RWLOCK(nf_ct_gre_lock); 50 + static LIST_HEAD(gre_keymap_list); 51 + 52 + void nf_ct_gre_keymap_flush(void) 53 + { 54 + struct list_head *pos, *n; 55 + 56 + write_lock_bh(&nf_ct_gre_lock); 57 + list_for_each_safe(pos, n, &gre_keymap_list) { 58 + list_del(pos); 59 + kfree(pos); 60 + } 61 + write_unlock_bh(&nf_ct_gre_lock); 62 + } 63 + EXPORT_SYMBOL(nf_ct_gre_keymap_flush); 64 + 65 + static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km, 66 + const struct nf_conntrack_tuple *t) 67 + { 68 + return km->tuple.src.l3num == t->src.l3num && 69 + !memcmp(&km->tuple.src.u3, &t->src.u3, sizeof(t->src.u3)) && 70 + !memcmp(&km->tuple.dst.u3, &t->dst.u3, sizeof(t->dst.u3)) && 71 + km->tuple.dst.protonum == t->dst.protonum && 72 + km->tuple.dst.u.all == t->dst.u.all; 73 + } 74 + 75 + /* look up the source key for a given tuple */ 76 + static __be16 gre_keymap_lookup(struct nf_conntrack_tuple *t) 77 + { 78 + struct nf_ct_gre_keymap *km; 79 + __be16 key = 0; 80 + 81 + read_lock_bh(&nf_ct_gre_lock); 82 + list_for_each_entry(km, &gre_keymap_list, list) { 83 + if (gre_key_cmpfn(km, t)) { 84 + key = km->tuple.src.u.gre.key; 85 + break; 86 + } 87 + } 88 + read_unlock_bh(&nf_ct_gre_lock); 89 + 90 + DEBUGP("lookup src key 0x%x for ", key); 91 + NF_CT_DUMP_TUPLE(t); 92 + 93 + return key; 94 + } 95 + 96 + /* add a single keymap entry, associate with specified master ct */ 97 + int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, 98 + struct nf_conntrack_tuple *t) 99 + { 100 + struct nf_conn_help *help = nfct_help(ct); 101 + struct nf_ct_gre_keymap **kmp, *km; 102 + 103 + BUG_ON(strcmp(help->helper->name, "pptp")); 104 + kmp = &help->help.ct_pptp_info.keymap[dir]; 105 + if (*kmp) { 106 + /* check whether it's a retransmission */ 107 + list_for_each_entry(km, &gre_keymap_list, list) { 108 + if (gre_key_cmpfn(km, t) && km == *kmp) 109 + return 0; 110 + } 111 + DEBUGP("trying to override keymap_%s for ct %p\n", 112 + dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct); 113 + return -EEXIST; 114 + } 115 + 116 + km = kmalloc(sizeof(*km), GFP_ATOMIC); 117 + if (!km) 118 + return -ENOMEM; 119 + memcpy(&km->tuple, t, sizeof(*t)); 120 + *kmp = km; 121 + 122 + DEBUGP("adding new entry %p: ", km); 123 + NF_CT_DUMP_TUPLE(&km->tuple); 124 + 125 + write_lock_bh(&nf_ct_gre_lock); 126 + list_add_tail(&km->list, &gre_keymap_list); 127 + write_unlock_bh(&nf_ct_gre_lock); 128 + 129 + return 0; 130 + } 131 + EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add); 132 + 133 + /* destroy the keymap entries associated with specified master ct */ 134 + void nf_ct_gre_keymap_destroy(struct nf_conn *ct) 135 + { 136 + struct nf_conn_help *help = nfct_help(ct); 137 + enum ip_conntrack_dir dir; 138 + 139 + DEBUGP("entering for ct %p\n", ct); 140 + BUG_ON(strcmp(help->helper->name, "pptp")); 141 + 142 + write_lock_bh(&nf_ct_gre_lock); 143 + for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) { 144 + if (help->help.ct_pptp_info.keymap[dir]) { 145 + DEBUGP("removing %p from list\n", 146 + help->help.ct_pptp_info.keymap[dir]); 147 + list_del(&help->help.ct_pptp_info.keymap[dir]->list); 148 + kfree(help->help.ct_pptp_info.keymap[dir]); 149 + help->help.ct_pptp_info.keymap[dir] = NULL; 150 + } 151 + } 152 + write_unlock_bh(&nf_ct_gre_lock); 153 + } 154 + EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy); 155 + 156 + /* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */ 157 + 158 + /* invert gre part of tuple */ 159 + static int gre_invert_tuple(struct nf_conntrack_tuple *tuple, 160 + const struct nf_conntrack_tuple *orig) 161 + { 162 + tuple->dst.u.gre.key = orig->src.u.gre.key; 163 + tuple->src.u.gre.key = orig->dst.u.gre.key; 164 + return 1; 165 + } 166 + 167 + /* gre hdr info to tuple */ 168 + static int gre_pkt_to_tuple(const struct sk_buff *skb, 169 + unsigned int dataoff, 170 + struct nf_conntrack_tuple *tuple) 171 + { 172 + struct gre_hdr_pptp _pgrehdr, *pgrehdr; 173 + __be16 srckey; 174 + struct gre_hdr _grehdr, *grehdr; 175 + 176 + /* first only delinearize old RFC1701 GRE header */ 177 + grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr); 178 + if (!grehdr || grehdr->version != GRE_VERSION_PPTP) { 179 + /* try to behave like "nf_conntrack_proto_generic" */ 180 + tuple->src.u.all = 0; 181 + tuple->dst.u.all = 0; 182 + return 1; 183 + } 184 + 185 + /* PPTP header is variable length, only need up to the call_id field */ 186 + pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr); 187 + if (!pgrehdr) 188 + return 1; 189 + 190 + if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) { 191 + DEBUGP("GRE_VERSION_PPTP but unknown proto\n"); 192 + return 0; 193 + } 194 + 195 + tuple->dst.u.gre.key = pgrehdr->call_id; 196 + srckey = gre_keymap_lookup(tuple); 197 + tuple->src.u.gre.key = srckey; 198 + 199 + return 1; 200 + } 201 + 202 + /* print gre part of tuple */ 203 + static int gre_print_tuple(struct seq_file *s, 204 + const struct nf_conntrack_tuple *tuple) 205 + { 206 + return seq_printf(s, "srckey=0x%x dstkey=0x%x ", 207 + ntohs(tuple->src.u.gre.key), 208 + ntohs(tuple->dst.u.gre.key)); 209 + } 210 + 211 + /* print private data for conntrack */ 212 + static int gre_print_conntrack(struct seq_file *s, 213 + const struct nf_conn *ct) 214 + { 215 + return seq_printf(s, "timeout=%u, stream_timeout=%u ", 216 + (ct->proto.gre.timeout / HZ), 217 + (ct->proto.gre.stream_timeout / HZ)); 218 + } 219 + 220 + /* Returns verdict for packet, and may modify conntrack */ 221 + static int gre_packet(struct nf_conn *ct, 222 + const struct sk_buff *skb, 223 + unsigned int dataoff, 224 + enum ip_conntrack_info ctinfo, 225 + int pf, 226 + unsigned int hooknum) 227 + { 228 + /* If we've seen traffic both ways, this is a GRE connection. 229 + * Extend timeout. */ 230 + if (ct->status & IPS_SEEN_REPLY) { 231 + nf_ct_refresh_acct(ct, ctinfo, skb, 232 + ct->proto.gre.stream_timeout); 233 + /* Also, more likely to be important, and not a probe. */ 234 + set_bit(IPS_ASSURED_BIT, &ct->status); 235 + nf_conntrack_event_cache(IPCT_STATUS, skb); 236 + } else 237 + nf_ct_refresh_acct(ct, ctinfo, skb, 238 + ct->proto.gre.timeout); 239 + 240 + return NF_ACCEPT; 241 + } 242 + 243 + /* Called when a new connection for this protocol found. */ 244 + static int gre_new(struct nf_conn *ct, const struct sk_buff *skb, 245 + unsigned int dataoff) 246 + { 247 + DEBUGP(": "); 248 + NF_CT_DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); 249 + 250 + /* initialize to sane value. Ideally a conntrack helper 251 + * (e.g. in case of pptp) is increasing them */ 252 + ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT; 253 + ct->proto.gre.timeout = GRE_TIMEOUT; 254 + 255 + return 1; 256 + } 257 + 258 + /* Called when a conntrack entry has already been removed from the hashes 259 + * and is about to be deleted from memory */ 260 + static void gre_destroy(struct nf_conn *ct) 261 + { 262 + struct nf_conn *master = ct->master; 263 + DEBUGP(" entering\n"); 264 + 265 + if (!master) 266 + DEBUGP("no master !?!\n"); 267 + else 268 + nf_ct_gre_keymap_destroy(master); 269 + } 270 + 271 + /* protocol helper struct */ 272 + static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 = { 273 + .l3proto = AF_INET, 274 + .l4proto = IPPROTO_GRE, 275 + .name = "gre", 276 + .pkt_to_tuple = gre_pkt_to_tuple, 277 + .invert_tuple = gre_invert_tuple, 278 + .print_tuple = gre_print_tuple, 279 + .print_conntrack = gre_print_conntrack, 280 + .packet = gre_packet, 281 + .new = gre_new, 282 + .destroy = gre_destroy, 283 + .me = THIS_MODULE, 284 + #if defined(CONFIG_NF_CONNTRACK_NETLINK) || \ 285 + defined(CONFIG_NF_CONNTRACK_NETLINK_MODULE) 286 + .tuple_to_nfattr = nf_ct_port_tuple_to_nfattr, 287 + .nfattr_to_tuple = nf_ct_port_nfattr_to_tuple, 288 + #endif 289 + }; 290 + 291 + static int __init nf_ct_proto_gre_init(void) 292 + { 293 + return nf_conntrack_l4proto_register(&nf_conntrack_l4proto_gre4); 294 + } 295 + 296 + static void nf_ct_proto_gre_fini(void) 297 + { 298 + nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4); 299 + nf_ct_gre_keymap_flush(); 300 + } 301 + 302 + module_init(nf_ct_proto_gre_init); 303 + module_exit(nf_ct_proto_gre_fini); 304 + 305 + MODULE_LICENSE("GPL");