1/*
2 * ip_nat_pptp.c	- Version 1.11
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-2002 by Harald Welte <laforge@gnumonks.org>
13 *
14 * Development of this code funded by Astaro AG (http://www.astaro.com/)
15 *
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)
20 * 	 - Support other NAT scenarios than SNAT of PNS
21 * 
22 */
23
24#include <linux/config.h>
25#include <linux/module.h>
26#include <linux/ip.h>
27#include <linux/tcp.h>
28#include <net/tcp.h>
29#include <linux/netfilter_ipv4/ip_nat.h>
30#include <linux/netfilter_ipv4/ip_nat_rule.h>
31#include <linux/netfilter_ipv4/ip_nat_helper.h>
32#include <linux/netfilter_ipv4/ip_nat_pptp.h>
33#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
34#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
35#include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
36
37MODULE_LICENSE("GPL");
38MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
39MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
40
41
42#define DEBUGP(format, args...)
43
44static unsigned int
45pptp_nat_expected(struct sk_buff **pskb,
46		  unsigned int hooknum,
47		  struct ip_conntrack *ct,
48		  struct ip_nat_info *info)
49{
50	struct ip_conntrack *master = master_ct(ct);
51	struct ip_nat_multi_range mr;
52	struct ip_ct_pptp_master *ct_pptp_info;
53	struct ip_nat_pptp *nat_pptp_info;
54	u_int32_t newsrcip, newdstip, newcid;
55	int ret;
56
57	IP_NF_ASSERT(info);
58	IP_NF_ASSERT(master);
59	IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
60
61	DEBUGP("we have a connection!\n");
62
63	LOCK_BH(&ip_pptp_lock);
64	ct_pptp_info = &master->help.ct_pptp_info;
65	nat_pptp_info = &master->nat.help.nat_pptp_info;
66
67	/* need to alter GRE tuple because conntrack expectfn() used 'wrong'
68	 * (unmanipulated) values */
69	if (hooknum == NF_IP_PRE_ROUTING) {
70		DEBUGP("completing tuples with NAT info \n");
71		/* we can do this, since we're unconfirmed */
72		if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key ==
73			htonl(ct_pptp_info->pac_call_id)) {	
74			/* assume PNS->PAC */
75			ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
76				htonl(nat_pptp_info->pns_call_id);
77//			ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.gre.key =
78//				htonl(nat_pptp_info->pac_call_id);
79			ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
80				htonl(nat_pptp_info->pns_call_id);
81		} else {
82			/* assume PAC->PNS */
83			DEBUGP("WRONG DIRECTION\n");
84			ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key =
85				htonl(nat_pptp_info->pac_call_id);
86			ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key =
87				htonl(nat_pptp_info->pns_call_id);
88		}
89	}
90
91	if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
92		newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
93		newcid = htonl(master->nat.help.nat_pptp_info.pac_call_id);
94
95		mr.rangesize = 1;
96		mr.range[0].flags = IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED;
97		mr.range[0].min_ip = mr.range[0].max_ip = newdstip;
98		mr.range[0].min = mr.range[0].max = 
99			((union ip_conntrack_manip_proto ) { newcid }); 
100		DEBUGP("change dest ip to %u.%u.%u.%u\n", 
101			NIPQUAD(newdstip));
102		DEBUGP("change dest key to 0x%x\n", ntohl(newcid));
103		ret = ip_nat_setup_info(ct, &mr, hooknum);
104	} else {
105		newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
106		/* nat_multi_range is in network byte order, and GRE tuple
107		 * is 32 bits, not 16 like callID */
108		newcid = htonl(master->help.ct_pptp_info.pns_call_id);
109
110		mr.rangesize = 1;
111		mr.range[0].flags = IP_NAT_RANGE_MAP_IPS
112			            |IP_NAT_RANGE_PROTO_SPECIFIED;
113		mr.range[0].min_ip = mr.range[0].max_ip = newsrcip;
114		mr.range[0].min = mr.range[0].max = 
115			((union ip_conntrack_manip_proto ) { newcid });
116		DEBUGP("change src ip to %u.%u.%u.%u\n", 
117			NIPQUAD(newsrcip));
118		DEBUGP("change 'src' key to 0x%x\n", ntohl(newcid));
119		ret = ip_nat_setup_info(ct, &mr, hooknum);
120	}
121
122	UNLOCK_BH(&ip_pptp_lock);
123
124	return ret;
125
126}
127
128/* outbound packets == from PNS to PAC */
129static inline unsigned int
130pptp_outbound_pkt(struct tcphdr *tcph, struct pptp_pkt_hdr *pptph,
131		  size_t datalen,
132		  struct ip_conntrack *ct,
133		  enum ip_conntrack_info ctinfo,
134		  struct ip_conntrack_expect *exp)
135
136{
137	struct PptpControlHeader *ctlh;
138	union pptp_ctrl_union pptpReq;
139	struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
140	struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
141
142	u_int16_t msg, *cid = NULL, new_callid;
143
144	ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
145	pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
146
147	new_callid = htons(ct_pptp_info->pns_call_id);
148	
149	switch (msg = ntohs(ctlh->messageType)) {
150		case PPTP_OUT_CALL_REQUEST:
151			cid = &pptpReq.ocreq->callID;
152
153			/* save original call ID in nat_info */
154			nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id;
155
156			new_callid = tcph->source;
157			/* save new call ID in ct info */
158			ct_pptp_info->pns_call_id = ntohs(new_callid);
159			break;
160		case PPTP_IN_CALL_REPLY:
161			cid = &pptpReq.icreq->callID;
162			break;
163		case PPTP_CALL_CLEAR_REQUEST:
164			cid = &pptpReq.clrreq->callID;
165			break;
166		case PPTP_CALL_DISCONNECT_NOTIFY:
167			cid = &pptpReq.disc->callID;
168			break;
169
170		default:
171			DEBUGP("unknown outbound packet 0x%04x:%s\n", msg,
172			      (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
173			/* fall through */
174
175		case PPTP_SET_LINK_INFO:
176			/* only need to NAT in case PAC is behind NAT box */
177		case PPTP_START_SESSION_REQUEST:
178		case PPTP_START_SESSION_REPLY:
179		case PPTP_STOP_SESSION_REQUEST:
180		case PPTP_STOP_SESSION_REPLY:
181		case PPTP_ECHO_REQUEST:
182		case PPTP_ECHO_REPLY:
183			/* no need to alter packet */
184			return NF_ACCEPT;
185	}
186
187	IP_NF_ASSERT(cid);
188
189	DEBUGP("altering call id from 0x%04x to 0x%04x\n",
190		ntohs(*cid), ntohs(new_callid));
191	/* mangle packet */
192	tcph->check = ip_nat_cheat_check(*cid^0xFFFF, 
193					 new_callid, tcph->check);
194	*cid = new_callid;
195
196	return NF_ACCEPT;
197}
198
199/* inbound packets == from PAC to PNS */
200static inline unsigned int
201pptp_inbound_pkt(struct tcphdr *tcph, struct pptp_pkt_hdr *pptph,
202		 size_t datalen,
203		 struct ip_conntrack *ct,
204		 enum ip_conntrack_info ctinfo,
205		 struct ip_conntrack_expect *oldexp)
206{
207	struct PptpControlHeader *ctlh;
208	union pptp_ctrl_union pptpReq;
209	struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
210	struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
211
212	u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL;
213	u_int32_t old_dst_ip;
214
215	struct ip_conntrack_tuple t;
216
217	ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
218	pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh));
219
220	new_pcid = htons(nat_pptp_info->pns_call_id);
221
222	switch (msg = ntohs(ctlh->messageType)) {
223	case PPTP_OUT_CALL_REPLY:
224		pcid = &pptpReq.ocack->peersCallID;	
225		cid = &pptpReq.ocack->callID;
226		if (!oldexp) {
227			DEBUGP("outcall but no expectation\n");
228			break;
229		}
230		old_dst_ip = oldexp->tuple.dst.ip;
231		t = oldexp->tuple;
232
233		/* save original PAC call ID in nat_info */
234		nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
235
236		/* store new callID in ct_info, so conntrack works */
237		//ct_pptp_info->pac_call_id = ntohs(tcph->source);
238		//new_cid = htons(ct_pptp_info->pac_call_id);
239
240		/* alter expectation */
241		if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) {
242			/* expectation for PNS->PAC direction */
243			t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id);
244			t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id);
245		} else {
246			/* expectation for PAC->PNS direction */
247			t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
248			DEBUGP("EXPECTATION IN WRONG DIRECTION!!!\n");
249		}
250
251		if (!ip_conntrack_change_expect(oldexp, &t)) {
252			DEBUGP("successfully changed expect\n");
253		} else {
254			DEBUGP("can't change expect\n");
255		}
256		ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig, &t);
257		/* reply keymap */
258		t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
259		t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
260		t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id);
261		t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id);
262		ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, &t);
263
264		break;
265	case PPTP_IN_CALL_CONNECT:
266		pcid = &pptpReq.iccon->peersCallID;
267		if (!oldexp)
268			break;
269		old_dst_ip = oldexp->tuple.dst.ip;
270		t = oldexp->tuple;
271
272		/* alter expectation, no need for callID */
273		if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) {
274			/* expectation for PNS->PAC direction */
275			t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
276		} else {
277			/* expectation for PAC->PNS direction */
278			t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
279		}
280
281		if (!ip_conntrack_change_expect(oldexp, &t)) {
282			DEBUGP("successfully changed expect\n");
283		} else {
284			DEBUGP("can't change expect\n");
285		}
286		break;
287	case PPTP_IN_CALL_REQUEST:
288		/* only need to nat in case PAC is behind NAT box */
289		break;
290	case PPTP_WAN_ERROR_NOTIFY:
291		pcid = &pptpReq.wanerr->peersCallID;
292		break;
293	default:
294		DEBUGP("unknown inbound packet %s\n",
295			(msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
296		/* fall through */
297
298	case PPTP_START_SESSION_REQUEST:
299	case PPTP_START_SESSION_REPLY:
300	case PPTP_STOP_SESSION_REQUEST:
301	case PPTP_ECHO_REQUEST:
302	case PPTP_ECHO_REPLY:
303		/* no need to alter packet */
304		return NF_ACCEPT;
305	}
306
307	/* mangle packet */
308	IP_NF_ASSERT(pcid);
309	DEBUGP("altering peer call id from 0x%04x to 0x%04x\n",
310		ntohs(*pcid), ntohs(new_pcid));
311	tcph->check = ip_nat_cheat_check(*pcid^0xFFFF, 
312					 new_pcid, tcph->check);
313	*pcid = new_pcid;
314
315	if (new_cid) {
316		IP_NF_ASSERT(cid);
317		DEBUGP("altering call id from 0x%04x to 0x%04x\n",
318			ntohs(*cid), ntohs(new_cid));
319		tcph->check = ip_nat_cheat_check(*cid^0xFFFF,
320						new_cid, tcph->check);
321		*cid = new_cid;
322	}
323
324	/* great, at least we don't need to resize packets */
325	return NF_ACCEPT;
326}
327
328
329static unsigned int tcp_help(struct ip_conntrack *ct,
330			     struct ip_conntrack_expect *exp,
331			     struct ip_nat_info *info,
332			     enum ip_conntrack_info ctinfo,
333			     unsigned int hooknum, struct sk_buff **pskb)
334{
335	struct iphdr *iph = (*pskb)->nh.iph;
336	struct tcphdr *tcph = (void *) iph + iph->ihl*4;
337	unsigned int datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4;
338	struct pptp_pkt_hdr *pptph;
339	void *datalimit;
340
341	int dir;
342
343	DEBUGP("entering\n");
344
345	/* Only mangle things once: original direction in POST_ROUTING
346	   and reply direction on PRE_ROUTING. */
347	dir = CTINFO2DIR(ctinfo);
348	if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
349	      || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
350		DEBUGP("Not touching dir %s at hook %s\n",
351		       dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
352		       hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
353		       : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
354		       : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
355		return NF_ACCEPT;
356	}
357
358	/* if packet is too small, just skip it */
359	if (datalen < sizeof(struct pptp_pkt_hdr)+
360		      sizeof(struct PptpControlHeader)) {
361		DEBUGP("pptp packet too short\n");
362		return NF_ACCEPT;	
363	}
364
365
366	pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4);
367	datalimit = (void *) pptph + datalen;
368
369	LOCK_BH(&ip_pptp_lock);
370
371	if (dir == IP_CT_DIR_ORIGINAL) {
372		/* reuqests sent by client to server (PNS->PAC) */
373		pptp_outbound_pkt(tcph, pptph, datalen, ct, ctinfo, exp);
374	} else {
375		/* response from the server to the client (PAC->PNS) */
376		pptp_inbound_pkt(tcph, pptph, datalen, ct, ctinfo, exp);
377	}
378
379	UNLOCK_BH(&ip_pptp_lock);
380
381	return NF_ACCEPT;
382}
383
384/* nat helper struct for control connection */
385static struct ip_nat_helper pptp_tcp_helper = { 
386	{ NULL, NULL },
387	"pptp", IP_NAT_HELPER_F_ALWAYS, THIS_MODULE,
388	{ { 0, { tcp: { port: __constant_htons(PPTP_CONTROL_PORT) } } },
389	  { 0, { 0 }, IPPROTO_TCP } },
390	{ { 0, { tcp: { port: 0xFFFF } } },
391	  { 0, { 0 }, 0xFFFF } },
392	tcp_help, pptp_nat_expected };
393
394			  
395static int __init init(void)
396{
397	DEBUGP("init_module\n" );
398
399        if (ip_nat_helper_register(&pptp_tcp_helper))
400		return -EIO;
401
402        return 0;
403}
404
405static void __exit fini(void)
406{
407	DEBUGP("cleanup_module\n" );
408        ip_nat_helper_unregister(&pptp_tcp_helper);
409}
410
411module_init(init);
412module_exit(fini);
413