This commit was manufactured by cvs2svn to create branch
[linux-2.6.git] / net / ipv4 / netfilter / ip_nat_pptp.c
1 /*
2  * ip_nat_pptp.c        - Version 2.0
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-2004 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  *
21  * Changes:
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
34  * 
35  */
36
37 #include <linux/config.h>
38 #include <linux/module.h>
39 #include <linux/ip.h>
40 #include <linux/tcp.h>
41 #include <net/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>
49
50 #define IP_NAT_PPTP_VERSION "2.0"
51
52 MODULE_LICENSE("GPL");
53 MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
54 MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP");
55
56
57 #if 0
58 #include "ip_conntrack_pptp_priv.h"
59 #define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \
60                                        ": " format, ## args)
61 #else
62 #define DEBUGP(format, args...)
63 #endif
64
65 static unsigned int
66 pptp_nat_expected(struct sk_buff **pskb,
67                   unsigned int hooknum,
68                   struct ip_conntrack *ct,
69                   struct ip_nat_info *info)
70 {
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;
76         int ret;
77
78         IP_NF_ASSERT(info);
79         IP_NF_ASSERT(master);
80         IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
81
82         DEBUGP("we have a connection!\n");
83
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;
87
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)) {     
95                         /* assume PNS->PAC */
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);
102                 } else {
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);
110                 }
111         } else {
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);
117                 }
118                 else {
119                         /* assume PAC->PNS */
120                         newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
121                         newcid = htonl(ct_pptp_info->pac_call_id);
122                 }
123         }
124
125         mr.rangesize = 1;
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", 
131                 NIPQUAD(newip));
132         DEBUGP("change key to 0x%x\n", ntohl(newcid));
133         ret = ip_nat_setup_info(ct, &mr, hooknum);
134
135         UNLOCK_BH(&ip_pptp_lock);
136
137         return ret;
138
139 }
140
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)
147
148 {
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);
153
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;
158
159         u_int16_t msg, *cid = NULL, new_callid;
160
161         /* FIXME: size checks !!! */
162         ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
163         pptpReq = (void *) ((void *) ctlh + sizeof(*ctlh));
164
165         new_callid = htons(ct_pptp_info->pns_call_id);
166         
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 */
174
175                         /* save original call ID in nat_info */
176                         nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id;
177
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;
181
182                         /* save new call ID in ct info */
183                         ct_pptp_info->pns_call_id = ntohs(new_callid);
184                         break;
185                 case PPTP_IN_CALL_REPLY:
186                         cid = &pptpReq->icreq.callID;
187                         break;
188                 case PPTP_CALL_CLEAR_REQUEST:
189                         cid = &pptpReq->clrreq.callID;
190                         break;
191                 default:
192                         DEBUGP("unknown outbound packet 0x%04x:%s\n", msg,
193                               (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
194                         /* fall through */
195
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 */
205                         return NF_ACCEPT;
206         }
207
208         IP_NF_ASSERT(cid);
209
210         DEBUGP("altering call id from 0x%04x to 0x%04x\n",
211                 ntohs(*cid), ntohs(new_callid));
212
213         /* mangle packet */
214         ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)cid - (void *)pptph,
215                                  sizeof(new_callid), (char *)&new_callid,
216                                  sizeof(new_callid));
217
218         return NF_ACCEPT;
219 }
220
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)
227 {
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);
232
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;
237
238         u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL;
239         u_int32_t old_dst_ip;
240
241         struct ip_conntrack_tuple t, inv_t;
242         struct ip_conntrack_tuple *orig_t, *reply_t;
243
244         /* FIXME: size checks !!! */
245         ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph));
246         pptpReq = (void *) ((void *) ctlh + sizeof(*ctlh));
247
248         new_pcid = htons(nat_pptp_info->pns_call_id);
249
250         switch (msg = ntohs(ctlh->messageType)) {
251         case PPTP_OUT_CALL_REPLY:
252                 pcid = &pptpReq->ocack.peersCallID;     
253                 cid = &pptpReq->ocack.callID;
254                 if (!oldexp) {
255                         DEBUGP("outcall but no expectation\n");
256                         break;
257                 }
258                 old_dst_ip = oldexp->tuple.dst.ip;
259                 t = oldexp->tuple;
260                 invert_tuplepr(&inv_t, &t);
261
262                 /* save original PAC call ID in nat_info */
263                 nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id;
264
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);
276                 } else {
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);
284                 }
285
286                 if (!ip_conntrack_change_expect(oldexp, &t)) {
287                         DEBUGP("successfully changed expect\n");
288                 } else {
289                         DEBUGP("can't change expect\n");
290                 }
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);
293                 break;
294         case PPTP_IN_CALL_CONNECT:
295                 pcid = &pptpReq->iccon.peersCallID;
296                 if (!oldexp)
297                         break;
298                 old_dst_ip = oldexp->tuple.dst.ip;
299                 t = oldexp->tuple;
300
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;
305                 } else {
306                         /* expectation for PAC->PNS direction */
307                         t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
308                 }
309
310                 if (!ip_conntrack_change_expect(oldexp, &t)) {
311                         DEBUGP("successfully changed expect\n");
312                 } else {
313                         DEBUGP("can't change expect\n");
314                 }
315                 break;
316         case PPTP_IN_CALL_REQUEST:
317                 /* only need to nat in case PAC is behind NAT box */
318                 break;
319         case PPTP_WAN_ERROR_NOTIFY:
320                 pcid = &pptpReq->wanerr.peersCallID;
321                 break;
322         case PPTP_CALL_DISCONNECT_NOTIFY:
323                 pcid = &pptpReq->disc.callID;
324                 break;
325
326         default:
327                 DEBUGP("unknown inbound packet %s\n",
328                         (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
329                 /* fall through */
330
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 */
338                 return NF_ACCEPT;
339         }
340
341         /* mangle packet */
342         IP_NF_ASSERT(pcid);
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, 
347                                  sizeof(new_pcid));
348
349         if (new_cid) {
350                 IP_NF_ASSERT(cid);
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, 
356                                          sizeof(new_cid));
357         }
358
359         /* great, at least we don't need to resize packets */
360         return NF_ACCEPT;
361 }
362
363
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)
369 {
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;
374
375         int dir;
376
377         DEBUGP("entering\n");
378
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" : "???");
392                 return NF_ACCEPT;
393         }
394
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");
399                 return NF_ACCEPT;       
400         }
401
402         pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4);
403
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");
408                 return NF_ACCEPT;
409         }
410
411         LOCK_BH(&ip_pptp_lock);
412
413         if (dir == IP_CT_DIR_ORIGINAL) {
414                 /* reuqests sent by client to server (PNS->PAC) */
415                 pptp_outbound_pkt(pskb, ct, ctinfo, exp);
416         } else {
417                 /* response from the server to the client (PAC->PNS) */
418                 pptp_inbound_pkt(pskb, ct, ctinfo, exp);
419         }
420
421         UNLOCK_BH(&ip_pptp_lock);
422
423         return NF_ACCEPT;
424 }
425
426 /* nat helper struct for control connection */
427 static struct ip_nat_helper pptp_tcp_helper = { 
428         .list = { NULL, NULL },
429         .name = "pptp", 
430         .flags = IP_NAT_HELPER_F_ALWAYS, 
431         .me = THIS_MODULE,
432         .tuple = { .src = { .ip = 0, 
433                             .u = { .tcp = { .port = 
434                                         __constant_htons(PPTP_CONTROL_PORT) } 
435                                  } 
436                           },
437                    .dst = { .ip = 0, 
438                             .u = { .all = 0 }, 
439                             .protonum = IPPROTO_TCP 
440                           } 
441                  },
442
443         .mask = { .src = { .ip = 0, 
444                            .u = { .tcp = { .port = 0xFFFF } } 
445                          },
446                   .dst = { .ip = 0, 
447                            .u = { .all = 0 }, 
448                            .protonum = 0xFFFF 
449                          } 
450                 },
451         .help = tcp_help, 
452         .expect = pptp_nat_expected 
453 };
454
455                           
456 static int __init init(void)
457 {
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 "
461                                 "for pptp\n");
462                 return -EIO;
463         }
464
465         printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION);
466         return 0;
467 }
468
469 static void __exit fini(void)
470 {
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);
474 }
475
476 module_init(init);
477 module_exit(fini);