finally in sync with archive
[bcm963xx.git] / kernel / linux / 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 "%s:%s: " format, __FILE__, \
60                                        __FUNCTION__, ## 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                 if (oldexp->proto.gre.keymap_orig)
292                         ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig,
293                                                 &t);
294                 if (oldexp->proto.gre.keymap_reply)
295                         ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, 
296                                                 &inv_t);
297                 break;
298         case PPTP_IN_CALL_CONNECT:
299                 pcid = &pptpReq->iccon.peersCallID;
300                 if (!oldexp)
301                         break;
302                 old_dst_ip = oldexp->tuple.dst.ip;
303                 t = oldexp->tuple;
304
305                 /* alter expectation, no need for callID */
306                 if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) {
307                         /* expectation for PNS->PAC direction */
308                         t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
309                 } else {
310                         /* expectation for PAC->PNS direction */
311                         t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
312                 }
313
314                 if (!ip_conntrack_change_expect(oldexp, &t)) {
315                         DEBUGP("successfully changed expect\n");
316                 } else {
317                         DEBUGP("can't change expect\n");
318                 }
319                 break;
320         case PPTP_IN_CALL_REQUEST:
321                 /* only need to nat in case PAC is behind NAT box */
322                 break;
323         case PPTP_WAN_ERROR_NOTIFY:
324                 pcid = &pptpReq->wanerr.peersCallID;
325                 break;
326         case PPTP_CALL_DISCONNECT_NOTIFY:
327                 pcid = &pptpReq->disc.callID;
328                 break;
329
330         default:
331                 DEBUGP("unknown inbound packet %s\n",
332                         (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]);
333                 /* fall through */
334
335         case PPTP_START_SESSION_REQUEST:
336         case PPTP_START_SESSION_REPLY:
337         case PPTP_STOP_SESSION_REQUEST:
338         case PPTP_STOP_SESSION_REPLY:
339         case PPTP_ECHO_REQUEST:
340         case PPTP_ECHO_REPLY:
341                 /* no need to alter packet */
342                 return NF_ACCEPT;
343         }
344
345         /* mangle packet */
346         IP_NF_ASSERT(pcid);
347         DEBUGP("altering peer call id from 0x%04x to 0x%04x\n",
348                 ntohs(*pcid), ntohs(new_pcid));
349         ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)pcid - (void *)pptph,
350                                  sizeof(new_pcid), (char *)&new_pcid, 
351                                  sizeof(new_pcid));
352
353         if (new_cid) {
354                 IP_NF_ASSERT(cid);
355                 DEBUGP("altering call id from 0x%04x to 0x%04x\n",
356                         ntohs(*cid), ntohs(new_cid));
357                 ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, 
358                                          (void *)cid - (void *)pptph, 
359                                          sizeof(new_cid), (char *)&new_cid, 
360                                          sizeof(new_cid));
361         }
362
363         /* great, at least we don't need to resize packets */
364         return NF_ACCEPT;
365 }
366
367
368 static unsigned int tcp_help(struct ip_conntrack *ct,
369                              struct ip_conntrack_expect *exp,
370                              struct ip_nat_info *info,
371                              enum ip_conntrack_info ctinfo,
372                              unsigned int hooknum, struct sk_buff **pskb)
373 {
374         struct iphdr *iph = (*pskb)->nh.iph;
375         struct tcphdr *tcph = (void *) iph + iph->ihl*4;
376         unsigned int datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4;
377         struct pptp_pkt_hdr *pptph;
378
379         int dir;
380
381         DEBUGP("entering\n");
382
383         /* Only mangle things once: DST for original direction
384            and SRC for reply direction. */
385         dir = CTINFO2DIR(ctinfo);
386         if (!((HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
387              && dir == IP_CT_DIR_ORIGINAL)
388               || (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST
389                   && dir == IP_CT_DIR_REPLY))) {
390                 DEBUGP("Not touching dir %s at hook %s\n",
391                        dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
392                        hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
393                        : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
394                        : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT"
395                        : hooknum == NF_IP_LOCAL_IN ? "INPUT" : "???");
396                 return NF_ACCEPT;
397         }
398
399         /* if packet is too small, just skip it */
400         if (datalen < sizeof(struct pptp_pkt_hdr)+
401                       sizeof(struct PptpControlHeader)) {
402                 DEBUGP("pptp packet too short\n");
403                 return NF_ACCEPT;       
404         }
405
406         pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4);
407
408         /* if it's not a control message, we can't handle it */
409         if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
410             ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
411                 DEBUGP("not a pptp control packet\n");
412                 return NF_ACCEPT;
413         }
414
415         LOCK_BH(&ip_pptp_lock);
416
417         if (dir == IP_CT_DIR_ORIGINAL) {
418                 /* reuqests sent by client to server (PNS->PAC) */
419                 pptp_outbound_pkt(pskb, ct, ctinfo, exp);
420         } else {
421                 /* response from the server to the client (PAC->PNS) */
422                 pptp_inbound_pkt(pskb, ct, ctinfo, exp);
423         }
424
425         UNLOCK_BH(&ip_pptp_lock);
426
427         return NF_ACCEPT;
428 }
429
430 /* nat helper struct for control connection */
431 static struct ip_nat_helper pptp_tcp_helper = { 
432         .list = { NULL, NULL },
433         .name = "pptp", 
434         .flags = IP_NAT_HELPER_F_ALWAYS, 
435         .me = THIS_MODULE,
436         .tuple = { .src = { .ip = 0, 
437                             .u = { .tcp = { .port = 
438                                         __constant_htons(PPTP_CONTROL_PORT) } 
439                                  } 
440                           },
441                    .dst = { .ip = 0, 
442                             .u = { .all = 0 }, 
443                             .protonum = IPPROTO_TCP 
444                           } 
445                  },
446
447         .mask = { .src = { .ip = 0, 
448                            .u = { .tcp = { .port = 0xFFFF } } 
449                          },
450                   .dst = { .ip = 0, 
451                            .u = { .all = 0 }, 
452                            .protonum = 0xFFFF 
453                          } 
454                 },
455         .help = tcp_help, 
456         .expect = pptp_nat_expected 
457 };
458
459                           
460 static int __init init(void)
461 {
462         DEBUGP("%s: registering NAT helper\n", __FILE__);
463         if (ip_nat_helper_register(&pptp_tcp_helper)) {
464                 printk(KERN_ERR "Unable to register NAT application helper "
465                                 "for pptp\n");
466                 return -EIO;
467         }
468
469         printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION);
470         return 0;
471 }
472
473 static void __exit fini(void)
474 {
475         DEBUGP("cleanup_module\n" );
476         ip_nat_helper_unregister(&pptp_tcp_helper);
477         printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION);
478 }
479
480 module_init(init);
481 module_exit(fini);