2 * ip_nat_pptp.c - Version 2.0
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
12 * (C) 2000-2004 by Harald Welte <laforge@gnumonks.org>
14 * Development of this code funded by Astaro AG (http://www.astaro.com/)
16 * TODO: - Support for multiple calls within one session
17 * (needs netfilter newnat code)
18 * - NAT to a unique tuple, not to TCP source port
19 * (needs netfilter tuple reservation)
22 * 2002-02-10 - Version 1.3
23 * - Use ip_nat_mangle_tcp_packet() because of cloned skb's
24 * in local connections (Philip Craig <philipc@snapgear.com>)
25 * - add checks for magicCookie and pptp version
26 * - make argument list of pptp_{out,in}bound_packet() shorter
27 * - move to C99 style initializers
28 * - print version number at module loadtime
29 * 2003-09-22 - Version 1.5
30 * - use SNATed tcp sourceport as callid, since we get called before
31 * TCP header is mangled (Philip Craig <philipc@snapgear.com>)
32 * 2004-10-22 - Version 2.0
33 * - kernel 2.6.x version
37 #include <linux/config.h>
38 #include <linux/module.h>
40 #include <linux/tcp.h>
42 #include <linux/netfilter_ipv4/ip_nat.h>
43 #include <linux/netfilter_ipv4/ip_nat_rule.h>
44 #include <linux/netfilter_ipv4/ip_nat_helper.h>
45 #include <linux/netfilter_ipv4/ip_nat_pptp.h>
46 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
47 #include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
48 #include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
50 #define IP_NAT_PPTP_VERSION "2.0"
52 MODULE_LICENSE("GPL");
53 MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
54 MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
58 #include "ip_conntrack_pptp_priv.h"
59 #define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
62 #define DEBUGP(format, args...)
66 pptp_nat_expected(struct sk_buff **pskb,
68 struct ip_conntrack *ct,
69 struct ip_nat_info *info)
71 struct ip_conntrack *master = master_ct(ct);
72 struct ip_nat_multi_range mr;
73 struct ip_ct_pptp_master *ct_pptp_info;
74 struct ip_nat_pptp *nat_pptp_info;
75 u_int32_t newip, newcid;
80 IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
82 DEBUGP("we have a connection!\n");
84 LOCK_BH(&ip_pptp_lock);
85 ct_pptp_info = &master->help.ct_pptp_info;
86 nat_pptp_info = &master->nat.help.nat_pptp_info;
88 /* need to alter GRE tuple because conntrack expectfn() used 'wrong'
89 * (unmanipulated) values */
90 if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
91 DEBUGP("completing tuples with NAT info \n");
92 /* we can do this, since we're unconfirmed */
93 if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key ==
94 htonl(ct_pptp_info->pac_call_id)) {
96 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
97 htonl(nat_pptp_info->pns_call_id);
98 ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
99 htonl(nat_pptp_info->pns_call_id);
100 newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
101 newcid = htonl(nat_pptp_info->pac_call_id);
103 /* assume PAC->PNS */
104 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
105 htonl(nat_pptp_info->pac_call_id);
106 ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
107 htonl(nat_pptp_info->pac_call_id);
108 newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
109 newcid = htonl(nat_pptp_info->pns_call_id);
112 if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key ==
113 htonl(ct_pptp_info->pac_call_id)) {
114 /* assume PNS->PAC */
115 newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
116 newcid = htonl(ct_pptp_info->pns_call_id);
119 /* assume PAC->PNS */
120 newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
121 newcid = htonl(ct_pptp_info->pac_call_id);
126 mr.range[0].flags = IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED;
127 mr.range[0].min_ip = mr.range[0].max_ip = newip;
128 mr.range[0].min = mr.range[0].max =
129 ((union ip_conntrack_manip_proto ) { newcid });
130 DEBUGP("change ip to %u.%u.%u.%u\n",
132 DEBUGP("change key to 0x%x\n", ntohl(newcid));
133 ret = ip_nat_setup_info(ct, &mr, hooknum);
135 UNLOCK_BH(&ip_pptp_lock);
141 /* outbound packets == from PNS to PAC */
142 static inline unsigned int
143 pptp_outbound_pkt(struct sk_buff **pskb,
144 struct ip_conntrack *ct,
145 enum ip_conntrack_info ctinfo,
146 struct ip_conntrack_expect *exp)
149 struct iphdr *iph = (*pskb)->nh.iph;
150 struct tcphdr *tcph = (void *) iph + iph->ihl*4;
151 struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *)
152 ((void *)tcph + tcph->doff*4);
154 struct PptpControlHeader *ctlh;
155 union pptp_ctrl_union *pptpReq;
156 struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
157 struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
159 u_int16_t msg, *cid = NULL, new_callid;
161 /* FIXME: size checks !!! */
162 ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
163 pptpReq = (void *) ((void *) ctlh + sizeof(*ctlh));
165 new_callid = htons(ct_pptp_info->pns_call_id);
167 switch (msg = ntohs(ctlh->messageType)) {
168 case PPTP_OUT_CALL_REQUEST:
169 cid = &pptpReq->ocreq.callID;
170 /* FIXME: ideally we would want to reserve a call ID
171 * here. current netfilter NAT core is not able to do
172 * this :( For now we use TCP source port. This breaks
173 * multiple calls within one control session */
175 /* save original call ID in nat_info */
176 nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id;
178 /* don't use tcph->source since we are at a DSTmanip
179 * hook (e.g. PREROUTING) and pkt is not mangled yet */
180 new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port;
182 /* save new call ID in ct info */
183 ct_pptp_info->pns_call_id = ntohs(new_callid);
185 case PPTP_IN_CALL_REPLY:
186 cid = &pptpReq->icreq.callID;
188 case PPTP_CALL_CLEAR_REQUEST:
189 cid = &pptpReq->clrreq.callID;
192 DEBUGP("unknown outbound packet 0x%04x:%s\n", msg,
193 (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
196 case PPTP_SET_LINK_INFO:
197 /* only need to NAT in case PAC is behind NAT box */
198 case PPTP_START_SESSION_REQUEST:
199 case PPTP_START_SESSION_REPLY:
200 case PPTP_STOP_SESSION_REQUEST:
201 case PPTP_STOP_SESSION_REPLY:
202 case PPTP_ECHO_REQUEST:
203 case PPTP_ECHO_REPLY:
204 /* no need to alter packet */
210 DEBUGP("altering call id from 0x%04x to 0x%04x\n",
211 ntohs(*cid), ntohs(new_callid));
214 ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)cid - (void *)pptph,
215 sizeof(new_callid), (char *)&new_callid,
221 /* inbound packets == from PAC to PNS */
222 static inline unsigned int
223 pptp_inbound_pkt(struct sk_buff **pskb,
224 struct ip_conntrack *ct,
225 enum ip_conntrack_info ctinfo,
226 struct ip_conntrack_expect *oldexp)
228 struct iphdr *iph = (*pskb)->nh.iph;
229 struct tcphdr *tcph = (void *) iph + iph->ihl*4;
230 struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *)
231 ((void *)tcph + tcph->doff*4);
233 struct PptpControlHeader *ctlh;
234 union pptp_ctrl_union *pptpReq;
235 struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
236 struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
238 u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL;
239 u_int32_t old_dst_ip;
241 struct ip_conntrack_tuple t, inv_t;
242 struct ip_conntrack_tuple *orig_t, *reply_t;
244 /* FIXME: size checks !!! */
245 ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
246 pptpReq = (void *) ((void *) ctlh + sizeof(*ctlh));
248 new_pcid = htons(nat_pptp_info->pns_call_id);
250 switch (msg = ntohs(ctlh->messageType)) {
251 case PPTP_OUT_CALL_REPLY:
252 pcid = &pptpReq->ocack.peersCallID;
253 cid = &pptpReq->ocack.callID;
255 DEBUGP("outcall but no expectation\n");
258 old_dst_ip = oldexp->tuple.dst.ip;
260 invert_tuplepr(&inv_t, &t);
262 /* save original PAC call ID in nat_info */
263 nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
265 /* alter expectation */
266 orig_t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
267 reply_t = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
268 if (t.src.ip == orig_t->src.ip && t.dst.ip == orig_t->dst.ip) {
269 /* expectation for PNS->PAC direction */
270 t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id);
271 t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id);
272 inv_t.src.ip = reply_t->src.ip;
273 inv_t.dst.ip = reply_t->dst.ip;
274 inv_t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id);
275 inv_t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id);
277 /* expectation for PAC->PNS direction */
278 t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id);
279 t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id);
280 inv_t.src.ip = orig_t->src.ip;
281 inv_t.dst.ip = orig_t->dst.ip;
282 inv_t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id);
283 inv_t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id);
286 if (!ip_conntrack_change_expect(oldexp, &t)) {
287 DEBUGP("successfully changed expect\n");
289 DEBUGP("can't change expect\n");
291 ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig, &t);
292 ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, &inv_t);
294 case PPTP_IN_CALL_CONNECT:
295 pcid = &pptpReq->iccon.peersCallID;
298 old_dst_ip = oldexp->tuple.dst.ip;
301 /* alter expectation, no need for callID */
302 if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) {
303 /* expectation for PNS->PAC direction */
304 t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
306 /* expectation for PAC->PNS direction */
307 t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
310 if (!ip_conntrack_change_expect(oldexp, &t)) {
311 DEBUGP("successfully changed expect\n");
313 DEBUGP("can't change expect\n");
316 case PPTP_IN_CALL_REQUEST:
317 /* only need to nat in case PAC is behind NAT box */
319 case PPTP_WAN_ERROR_NOTIFY:
320 pcid = &pptpReq->wanerr.peersCallID;
322 case PPTP_CALL_DISCONNECT_NOTIFY:
323 pcid = &pptpReq->disc.callID;
327 DEBUGP("unknown inbound packet %s\n",
328 (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
331 case PPTP_START_SESSION_REQUEST:
332 case PPTP_START_SESSION_REPLY:
333 case PPTP_STOP_SESSION_REQUEST:
334 case PPTP_STOP_SESSION_REPLY:
335 case PPTP_ECHO_REQUEST:
336 case PPTP_ECHO_REPLY:
337 /* no need to alter packet */
343 DEBUGP("altering peer call id from 0x%04x to 0x%04x\n",
344 ntohs(*pcid), ntohs(new_pcid));
345 ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)pcid - (void *)pptph,
346 sizeof(new_pcid), (char *)&new_pcid,
351 DEBUGP("altering call id from 0x%04x to 0x%04x\n",
352 ntohs(*cid), ntohs(new_cid));
353 ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
354 (void *)cid - (void *)pptph,
355 sizeof(new_cid), (char *)&new_cid,
359 /* great, at least we don't need to resize packets */
364 static unsigned int tcp_help(struct ip_conntrack *ct,
365 struct ip_conntrack_expect *exp,
366 struct ip_nat_info *info,
367 enum ip_conntrack_info ctinfo,
368 unsigned int hooknum, struct sk_buff **pskb)
370 struct iphdr *iph = (*pskb)->nh.iph;
371 struct tcphdr *tcph = (void *) iph + iph->ihl*4;
372 unsigned int datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4;
373 struct pptp_pkt_hdr *pptph;
377 DEBUGP("entering\n");
379 /* Only mangle things once: DST for original direction
380 and SRC for reply direction. */
381 dir = CTINFO2DIR(ctinfo);
382 if (!((HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
383 && dir == IP_CT_DIR_ORIGINAL)
384 || (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST
385 && dir == IP_CT_DIR_REPLY))) {
386 DEBUGP("Not touching dir %s at hook %s\n",
387 dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
388 hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
389 : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
390 : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT"
391 : hooknum == NF_IP_LOCAL_IN ? "INPUT" : "???");
395 /* if packet is too small, just skip it */
396 if (datalen < sizeof(struct pptp_pkt_hdr)+
397 sizeof(struct PptpControlHeader)) {
398 DEBUGP("pptp packet too short\n");
402 pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4);
404 /* if it's not a control message, we can't handle it */
405 if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
406 ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
407 DEBUGP("not a pptp control packet\n");
411 LOCK_BH(&ip_pptp_lock);
413 if (dir == IP_CT_DIR_ORIGINAL) {
414 /* reuqests sent by client to server (PNS->PAC) */
415 pptp_outbound_pkt(pskb, ct, ctinfo, exp);
417 /* response from the server to the client (PAC->PNS) */
418 pptp_inbound_pkt(pskb, ct, ctinfo, exp);
421 UNLOCK_BH(&ip_pptp_lock);
426 /* nat helper struct for control connection */
427 static struct ip_nat_helper pptp_tcp_helper = {
428 .list = { NULL, NULL },
430 .flags = IP_NAT_HELPER_F_ALWAYS,
432 .tuple = { .src = { .ip = 0,
433 .u = { .tcp = { .port =
434 __constant_htons(PPTP_CONTROL_PORT) }
439 .protonum = IPPROTO_TCP
443 .mask = { .src = { .ip = 0,
444 .u = { .tcp = { .port = 0xFFFF } }
452 .expect = pptp_nat_expected
456 static int __init init(void)
458 DEBUGP("%s: registering NAT helper\n", __FILE__);
459 if (ip_nat_helper_register(&pptp_tcp_helper)) {
460 printk(KERN_ERR "Unable to register NAT application helper "
465 printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION);
469 static void __exit fini(void)
471 DEBUGP("cleanup_module\n" );
472 ip_nat_helper_unregister(&pptp_tcp_helper);
473 printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION);