This commit was manufactured by cvs2svn to create branch
[linux-2.6.git] / net / ipv4 / netfilter / ip_conntrack_pptp.c
1 /*
2  * ip_conntrack_pptp.c  - Version 3.0
3  *
4  * Connection tracking 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  * Limitations:
17  *       - We blindly assume that control connections are always
18  *         established in PNS->PAC direction.  This is a violation
19  *         of RFFC2673
20  *       - We can only support one single call within each session
21  *
22  * TODO:
23  *       - testing of incoming PPTP calls 
24  *
25  * Changes: 
26  *      2002-02-05 - Version 1.3
27  *        - Call ip_conntrack_unexpect_related() from 
28  *          pptp_timeout_related() to destroy expectations in case
29  *          CALL_DISCONNECT_NOTIFY or tcp fin packet was seen
30  *          (Philip Craig <philipc@snapgear.com>)
31  *        - Add Version information at module loadtime
32  *      2002-02-10 - Version 1.6
33  *        - move to C99 style initializers
34  *        - remove second expectation if first arrives
35  *      2004-10-22 - Version 2.0
36  *        - merge Mandrake's 2.6.x port with recent 2.6.x API changes
37  *        - fix lots of linear skb assumptions from Mandrake's port
38  *      2005-06-10 - Version 2.1
39  *        - use ip_conntrack_expect_free() instead of kfree() on the
40  *          expect's (which are from the slab for quite some time)
41  *      2005-06-10 - Version 3.0
42  *        - port helper to post-2.6.11 API changes,
43  *          funded by Oxcoda NetBox Blue (http://www.netboxblue.com/)
44  *
45  */
46
47 #include <linux/config.h>
48 #include <linux/module.h>
49 #include <linux/netfilter.h>
50 #include <linux/ip.h>
51 #include <net/checksum.h>
52 #include <net/tcp.h>
53
54 #include <linux/netfilter_ipv4/lockhelp.h>
55 #include <linux/netfilter_ipv4/ip_conntrack.h>
56 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
57 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
58 #include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
59 #include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
60
61 #define IP_CT_PPTP_VERSION "3.0"
62
63 MODULE_LICENSE("GPL");
64 MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
65 MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP");
66
67 DECLARE_LOCK(ip_pptp_lock);
68
69 int
70 (*ip_nat_pptp_hook_outbound)(struct sk_buff **pskb,
71                           struct ip_conntrack *ct,
72                           enum ip_conntrack_info ctinfo,
73                           struct PptpControlHeader *ctlh,
74                           union pptp_ctrl_union *pptpReq);
75
76 int
77 (*ip_nat_pptp_hook_inbound)(struct sk_buff **pskb,
78                           struct ip_conntrack *ct,
79                           enum ip_conntrack_info ctinfo,
80                           struct PptpControlHeader *ctlh,
81                           union pptp_ctrl_union *pptpReq);
82
83 int
84 (*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *expect_orig,
85                             struct ip_conntrack_expect *expect_reply);
86
87 void
88 (*ip_nat_pptp_hook_expectfn)(struct ip_conntrack *ct,
89                              struct ip_conntrack_expect *exp);
90
91 #if 0
92 #include "ip_conntrack_pptp_priv.h"
93 #define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
94 #else
95 #define DEBUGP(format, args...)
96 #endif
97
98 #define SECS *HZ
99 #define MINS * 60 SECS
100 #define HOURS * 60 MINS
101 #define DAYS * 24 HOURS
102
103 #define PPTP_GRE_TIMEOUT                (10 MINS)
104 #define PPTP_GRE_STREAM_TIMEOUT         (5 DAYS)
105
106 static void pptp_expectfn(struct ip_conntrack *ct,
107                          struct ip_conntrack_expect *exp)
108 {
109         DEBUGP("increasing timeouts\n");
110
111         /* increase timeout of GRE data channel conntrack entry */
112         ct->proto.gre.timeout = PPTP_GRE_TIMEOUT;
113         ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT;
114
115         /* Can you see how rusty this code is, compared with the pre-2.6.11
116          * one? That's what happened to my shiny newnat of 2002 ;( -HW */
117
118         if (!ip_nat_pptp_hook_expectfn) {
119                 struct ip_conntrack_tuple inv_t;
120                 struct ip_conntrack_expect *exp_other;
121
122                 /* obviously this tuple inversion only works until you do NAT */
123                 invert_tuplepr(&inv_t, &exp->tuple);
124                 DEBUGP("trying to unexpect other dir: ");
125                 DUMP_TUPLE(&inv_t);
126         
127                 exp_other = __ip_conntrack_exp_find(&inv_t);
128                 if (exp_other) {
129                         /* delete other expectation.  */
130                         DEBUGP("found\n");
131                         ip_conntrack_unexpect_related(exp_other);
132                 } else {
133                         DEBUGP("not found\n");
134                 }
135         } else {
136                 /* we need more than simple inversion */
137                 ip_nat_pptp_hook_expectfn(ct, exp);
138         }
139 }
140
141 static int timeout_ct_or_exp(const struct ip_conntrack_tuple *t)
142 {
143         struct ip_conntrack_tuple_hash *h;
144         struct ip_conntrack_expect *exp;
145
146         DEBUGP("trying to timeout ct or exp for tuple ");
147         DUMP_TUPLE(t);
148
149         h = __ip_conntrack_find(t, NULL);
150         if (h)  {
151                 struct ip_conntrack *sibling = 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                 /* refresh_acct will not modify counters if skb == NULL */
156                 ip_ct_refresh_acct(sibling, 0, NULL, 0);
157                 return 1;
158         } else {
159                 exp = __ip_conntrack_exp_find(t);
160                 if (exp) {
161                         DEBUGP("unexpect_related of expect %p\n", exp);
162                         ip_conntrack_unexpect_related(exp);
163                         return 1;
164                 }
165         }
166
167         return 0;
168 }
169
170
171 /* timeout GRE data connections */
172 static int pptp_timeout_related(struct ip_conntrack *ct)
173 {
174         struct ip_conntrack_tuple t;
175         int ret;
176
177         /* Since ct->sibling_list has literally rusted away in 2.6.11, 
178          * we now need another way to find out about our sibling
179          * contrack and expects... -HW */
180
181         /* try original (pns->pac) tuple */
182         memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t));
183         t.dst.protonum = IPPROTO_GRE;
184         t.src.u.gre.key = htons(ct->help.ct_pptp_info.pns_call_id);
185         t.dst.u.gre.key = htons(ct->help.ct_pptp_info.pac_call_id);
186
187         ret = timeout_ct_or_exp(&t);
188
189         /* try reply (pac->pns) tuple */
190         memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t));
191         t.dst.protonum = IPPROTO_GRE;
192         t.src.u.gre.key = htons(ct->help.ct_pptp_info.pac_call_id);
193         t.dst.u.gre.key = htons(ct->help.ct_pptp_info.pns_call_id);
194
195         ret += timeout_ct_or_exp(&t);
196
197         return ret;
198 }
199
200 /* expect GRE connections (PNS->PAC and PAC->PNS direction) */
201 static inline int
202 exp_gre(struct ip_conntrack *master,
203         u_int32_t seq,
204         u_int16_t callid,
205         u_int16_t peer_callid)
206 {
207         struct ip_conntrack_tuple inv_tuple;
208         struct ip_conntrack_tuple exp_tuples[] = {
209                 /* tuple in original direction, PNS->PAC */
210                 { .src = { .ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip,
211                            .u = { .gre = { .key = peer_callid } }
212                          },
213                   .dst = { .ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip,
214                            .u = { .gre = { .key = callid } },
215                            .protonum = IPPROTO_GRE
216                          },
217                  },
218                 /* tuple in reply direction, PAC->PNS */
219                 { .src = { .ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip,
220                            .u = { .gre = { .key = callid } }
221                          },
222                   .dst = { .ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip,
223                            .u = { .gre = { .key = peer_callid } },
224                            .protonum = IPPROTO_GRE
225                          },
226                  }
227         };
228
229         struct ip_conntrack_expect *exp_orig, *exp_reply;
230
231         exp_orig = ip_conntrack_expect_alloc();
232         if (exp_orig == NULL)
233                 return 1;
234
235         exp_reply = ip_conntrack_expect_alloc();
236         if (exp_reply == NULL) {
237                 ip_conntrack_expect_free(exp_orig);
238                 return 1;
239         }
240
241         memcpy(&exp_orig->tuple, &exp_tuples[0], sizeof(exp_orig->tuple));
242
243         exp_orig->mask.src.ip = 0xffffffff;
244         exp_orig->mask.src.u.all = 0;
245         exp_orig->mask.dst.u.all = 0;
246         exp_orig->mask.dst.u.gre.key = 0xffff;
247         exp_orig->mask.dst.ip = 0xffffffff;
248         exp_orig->mask.dst.protonum = 0xff;
249                 
250         exp_orig->master = master;
251         exp_orig->expectfn = pptp_expectfn;
252
253         exp_orig->dir = IP_CT_DIR_ORIGINAL;
254
255         /* both expectations are identical apart from tuple */
256         memcpy(exp_reply, exp_orig, sizeof(*exp_reply));
257         memcpy(&exp_reply->tuple, &exp_tuples[1], sizeof(exp_reply->tuple));
258
259         exp_reply->dir = !exp_orig->dir;
260
261         if (ip_nat_pptp_hook_exp_gre)
262                 return ip_nat_pptp_hook_exp_gre(exp_orig, exp_reply);
263         else {
264
265                 DEBUGP("calling expect_related PNS->PAC");
266                 DUMP_TUPLE(&exp_orig->tuple);
267
268                 if (ip_conntrack_expect_related(exp_orig) != 0) {
269                         ip_conntrack_expect_free(exp_orig);
270                         ip_conntrack_expect_free(exp_reply);
271                         DEBUGP("cannot expect_related()\n");
272                         return 1;
273                 }
274
275                 DEBUGP("calling expect_related PAC->PNS");
276                 DUMP_TUPLE(&exp_reply->tuple);
277
278                 if (ip_conntrack_expect_related(exp_reply) != 0) {
279                         ip_conntrack_unexpect_related(exp_orig);
280                         ip_conntrack_expect_free(exp_reply);
281                         DEBUGP("cannot expect_related()\n");
282                         return 1;
283                 }
284
285                 /* Add GRE keymap entries */
286                 if (ip_ct_gre_keymap_add(master, &exp_reply->tuple, 0) != 0) {
287                         ip_conntrack_unexpect_related(exp_orig);
288                         ip_conntrack_unexpect_related(exp_reply);
289                         DEBUGP("cannot keymap_add() exp\n");
290                         return 1;
291                 }
292
293                 invert_tuplepr(&inv_tuple, &exp_reply->tuple);
294                 if (ip_ct_gre_keymap_add(master, &inv_tuple, 1) != 0) {
295                         ip_conntrack_unexpect_related(exp_orig);
296                         ip_conntrack_unexpect_related(exp_reply);
297                         ip_ct_gre_keymap_destroy(master);
298                         DEBUGP("cannot keymap_add() exp_inv\n");
299                         return 1;
300                 }
301         
302         }
303
304         return 0;
305 }
306
307 static inline int 
308 pptp_inbound_pkt(struct sk_buff **pskb,
309                  struct tcphdr *tcph,
310                  unsigned int ctlhoff,
311                  size_t datalen,
312                  struct ip_conntrack *ct,
313                  enum ip_conntrack_info ctinfo)
314 {
315         struct PptpControlHeader _ctlh, *ctlh;
316         unsigned int reqlen;
317         union pptp_ctrl_union _pptpReq, *pptpReq;
318         struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
319         u_int16_t msg, *cid, *pcid;
320         u_int32_t seq;  
321
322         ctlh = skb_header_pointer(*pskb, ctlhoff, sizeof(_ctlh), &_ctlh);
323         if (unlikely(!ctlh)) {
324                 DEBUGP("error during skb_header_pointer\n");
325                 return NF_ACCEPT;
326         }
327
328         reqlen = datalen - sizeof(struct pptp_pkt_hdr) - sizeof(_ctlh);
329         pptpReq = skb_header_pointer(*pskb, ctlhoff+sizeof(_ctlh),
330                                      reqlen, &_pptpReq);
331         if (unlikely(!pptpReq)) {
332                 DEBUGP("error during skb_header_pointer\n");
333                 return NF_ACCEPT;
334         }
335
336         msg = ntohs(ctlh->messageType);
337         DEBUGP("inbound control message %s\n", strMName[msg]);
338
339         switch (msg) {
340         case PPTP_START_SESSION_REPLY:
341                 if (reqlen < sizeof(_pptpReq.srep)) {
342                         DEBUGP("%s: short packet\n", strMName[msg]);
343                         break;
344                 }
345
346                 /* server confirms new control session */
347                 if (info->sstate < PPTP_SESSION_REQUESTED) {
348                         DEBUGP("%s without START_SESS_REQUEST\n",
349                                 strMName[msg]);
350                         break;
351                 }
352                 if (pptpReq->srep.resultCode == PPTP_START_OK)
353                         info->sstate = PPTP_SESSION_CONFIRMED;
354                 else 
355                         info->sstate = PPTP_SESSION_ERROR;
356                 break;
357
358         case PPTP_STOP_SESSION_REPLY:
359                 if (reqlen < sizeof(_pptpReq.strep)) {
360                         DEBUGP("%s: short packet\n", strMName[msg]);
361                         break;
362                 }
363
364                 /* server confirms end of control session */
365                 if (info->sstate > PPTP_SESSION_STOPREQ) {
366                         DEBUGP("%s without STOP_SESS_REQUEST\n",
367                                 strMName[msg]);
368                         break;
369                 }
370                 if (pptpReq->strep.resultCode == PPTP_STOP_OK)
371                         info->sstate = PPTP_SESSION_NONE;
372                 else
373                         info->sstate = PPTP_SESSION_ERROR;
374                 break;
375
376         case PPTP_OUT_CALL_REPLY:
377                 if (reqlen < sizeof(_pptpReq.ocack)) {
378                         DEBUGP("%s: short packet\n", strMName[msg]);
379                         break;
380                 }
381
382                 /* server accepted call, we now expect GRE frames */
383                 if (info->sstate != PPTP_SESSION_CONFIRMED) {
384                         DEBUGP("%s but no session\n", strMName[msg]);
385                         break;
386                 }
387                 if (info->cstate != PPTP_CALL_OUT_REQ &&
388                     info->cstate != PPTP_CALL_OUT_CONF) {
389                         DEBUGP("%s without OUTCALL_REQ\n", strMName[msg]);
390                         break;
391                 }
392                 if (pptpReq->ocack.resultCode != PPTP_OUTCALL_CONNECT) {
393                         info->cstate = PPTP_CALL_NONE;
394                         break;
395                 }
396
397                 cid = &pptpReq->ocack.callID;
398                 pcid = &pptpReq->ocack.peersCallID;
399
400                 info->pac_call_id = ntohs(*cid);
401                 
402                 if (htons(info->pns_call_id) != *pcid) {
403                         DEBUGP("%s for unknown callid %u\n",
404                                 strMName[msg], ntohs(*pcid));
405                         break;
406                 }
407
408                 DEBUGP("%s, CID=%X, PCID=%X\n", strMName[msg], 
409                         ntohs(*cid), ntohs(*pcid));
410                 
411                 info->cstate = PPTP_CALL_OUT_CONF;
412
413                 seq = ntohl(tcph->seq) + sizeof(struct pptp_pkt_hdr)
414                                        + sizeof(struct PptpControlHeader)
415                                        + ((void *)pcid - (void *)pptpReq);
416                         
417                 if (exp_gre(ct, seq, *cid, *pcid) != 0)
418                         printk("ip_conntrack_pptp: error during exp_gre\n");
419                 break;
420
421         case PPTP_IN_CALL_REQUEST:
422                 if (reqlen < sizeof(_pptpReq.icack)) {
423                         DEBUGP("%s: short packet\n", strMName[msg]);
424                         break;
425                 }
426
427                 /* server tells us about incoming call request */
428                 if (info->sstate != PPTP_SESSION_CONFIRMED) {
429                         DEBUGP("%s but no session\n", strMName[msg]);
430                         break;
431                 }
432                 pcid = &pptpReq->icack.peersCallID;
433                 DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
434                 info->cstate = PPTP_CALL_IN_REQ;
435                 info->pac_call_id = ntohs(*pcid);
436                 break;
437
438         case PPTP_IN_CALL_CONNECT:
439                 if (reqlen < sizeof(_pptpReq.iccon)) {
440                         DEBUGP("%s: short packet\n", strMName[msg]);
441                         break;
442                 }
443
444                 /* server tells us about incoming call established */
445                 if (info->sstate != PPTP_SESSION_CONFIRMED) {
446                         DEBUGP("%s but no session\n", strMName[msg]);
447                         break;
448                 }
449                 if (info->sstate != PPTP_CALL_IN_REP
450                     && info->sstate != PPTP_CALL_IN_CONF) {
451                         DEBUGP("%s but never sent IN_CALL_REPLY\n",
452                                 strMName[msg]);
453                         break;
454                 }
455
456                 pcid = &pptpReq->iccon.peersCallID;
457                 cid = &info->pac_call_id;
458
459                 if (info->pns_call_id != ntohs(*pcid)) {
460                         DEBUGP("%s for unknown CallID %u\n", 
461                                 strMName[msg], ntohs(*cid));
462                         break;
463                 }
464
465                 DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
466                 info->cstate = PPTP_CALL_IN_CONF;
467
468                 /* we expect a GRE connection from PAC to PNS */
469                 seq = ntohl(tcph->seq) + sizeof(struct pptp_pkt_hdr)
470                                        + sizeof(struct PptpControlHeader)
471                                        + ((void *)pcid - (void *)pptpReq);
472                         
473                 if (exp_gre(ct, seq, *cid, *pcid) != 0)
474                         printk("ip_conntrack_pptp: error during exp_gre\n");
475
476                 break;
477
478         case PPTP_CALL_DISCONNECT_NOTIFY:
479                 if (reqlen < sizeof(_pptpReq.disc)) {
480                         DEBUGP("%s: short packet\n", strMName[msg]);
481                         break;
482                 }
483
484                 /* server confirms disconnect */
485                 cid = &pptpReq->disc.callID;
486                 DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid));
487                 info->cstate = PPTP_CALL_NONE;
488
489                 /* untrack this call id, unexpect GRE packets */
490                 pptp_timeout_related(ct);
491                 break;
492
493         case PPTP_WAN_ERROR_NOTIFY:
494                 break;
495
496         case PPTP_ECHO_REQUEST:
497         case PPTP_ECHO_REPLY:
498                 /* I don't have to explain these ;) */
499                 break;
500         default:
501                 DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)
502                         ? strMName[msg]:strMName[0], msg);
503                 break;
504         }
505
506
507         if (ip_nat_pptp_hook_inbound)
508                 return ip_nat_pptp_hook_inbound(pskb, ct, ctinfo, ctlh,
509                                                 pptpReq);
510
511         return NF_ACCEPT;
512
513 }
514
515 static inline int
516 pptp_outbound_pkt(struct sk_buff **pskb,
517                   struct tcphdr *tcph,
518                   unsigned int ctlhoff,
519                   size_t datalen,
520                   struct ip_conntrack *ct,
521                   enum ip_conntrack_info ctinfo)
522 {
523         struct PptpControlHeader _ctlh, *ctlh;
524         unsigned int reqlen;
525         union pptp_ctrl_union _pptpReq, *pptpReq;
526         struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
527         u_int16_t msg, *cid, *pcid;
528
529         ctlh = skb_header_pointer(*pskb, ctlhoff, sizeof(_ctlh), &_ctlh);
530         if (!ctlh)
531                 return NF_ACCEPT;
532         
533         reqlen = datalen - sizeof(struct pptp_pkt_hdr) - sizeof(_ctlh);
534         pptpReq = skb_header_pointer(*pskb, ctlhoff+sizeof(_ctlh), reqlen, 
535                                      &_pptpReq);
536         if (!pptpReq)
537                 return NF_ACCEPT;
538
539         msg = ntohs(ctlh->messageType);
540         DEBUGP("outbound control message %s\n", strMName[msg]);
541
542         switch (msg) {
543         case PPTP_START_SESSION_REQUEST:
544                 /* client requests for new control session */
545                 if (info->sstate != PPTP_SESSION_NONE) {
546                         DEBUGP("%s but we already have one",
547                                 strMName[msg]);
548                 }
549                 info->sstate = PPTP_SESSION_REQUESTED;
550                 break;
551         case PPTP_STOP_SESSION_REQUEST:
552                 /* client requests end of control session */
553                 info->sstate = PPTP_SESSION_STOPREQ;
554                 break;
555
556         case PPTP_OUT_CALL_REQUEST:
557                 if (reqlen < sizeof(_pptpReq.ocreq)) {
558                         DEBUGP("%s: short packet\n", strMName[msg]);
559                         /* FIXME: break; */
560                 }
561
562                 /* client initiating connection to server */
563                 if (info->sstate != PPTP_SESSION_CONFIRMED) {
564                         DEBUGP("%s but no session\n",
565                                 strMName[msg]);
566                         break;
567                 }
568                 info->cstate = PPTP_CALL_OUT_REQ;
569                 /* track PNS call id */
570                 cid = &pptpReq->ocreq.callID;
571                 DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid));
572                 info->pns_call_id = ntohs(*cid);
573                 break;
574         case PPTP_IN_CALL_REPLY:
575                 if (reqlen < sizeof(_pptpReq.icack)) {
576                         DEBUGP("%s: short packet\n", strMName[msg]);
577                         break;
578                 }
579
580                 /* client answers incoming call */
581                 if (info->cstate != PPTP_CALL_IN_REQ
582                     && info->cstate != PPTP_CALL_IN_REP) {
583                         DEBUGP("%s without incall_req\n", 
584                                 strMName[msg]);
585                         break;
586                 }
587                 if (pptpReq->icack.resultCode != PPTP_INCALL_ACCEPT) {
588                         info->cstate = PPTP_CALL_NONE;
589                         break;
590                 }
591                 pcid = &pptpReq->icack.peersCallID;
592                 if (info->pac_call_id != ntohs(*pcid)) {
593                         DEBUGP("%s for unknown call %u\n", 
594                                 strMName[msg], ntohs(*pcid));
595                         break;
596                 }
597                 DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*pcid));
598                 /* part two of the three-way handshake */
599                 info->cstate = PPTP_CALL_IN_REP;
600                 info->pns_call_id = ntohs(pptpReq->icack.callID);
601                 break;
602
603         case PPTP_CALL_CLEAR_REQUEST:
604                 /* client requests hangup of call */
605                 if (info->sstate != PPTP_SESSION_CONFIRMED) {
606                         DEBUGP("CLEAR_CALL but no session\n");
607                         break;
608                 }
609                 /* FUTURE: iterate over all calls and check if
610                  * call ID is valid.  We don't do this without newnat,
611                  * because we only know about last call */
612                 info->cstate = PPTP_CALL_CLEAR_REQ;
613                 break;
614         case PPTP_SET_LINK_INFO:
615                 break;
616         case PPTP_ECHO_REQUEST:
617         case PPTP_ECHO_REPLY:
618                 /* I don't have to explain these ;) */
619                 break;
620         default:
621                 DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)? 
622                         strMName[msg]:strMName[0], msg);
623                 /* unknown: no need to create GRE masq table entry */
624                 break;
625         }
626         
627         if (ip_nat_pptp_hook_outbound)
628                 return ip_nat_pptp_hook_outbound(pskb, ct, ctinfo, ctlh,
629                                                  pptpReq);
630
631         return NF_ACCEPT;
632 }
633
634
635 /* track caller id inside control connection, call expect_related */
636 static int 
637 conntrack_pptp_help(struct sk_buff **pskb,
638                     struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
639
640 {
641         struct pptp_pkt_hdr _pptph, *pptph;
642         
643         struct tcphdr _tcph, *tcph;
644         u_int32_t tcplen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4;
645         u_int32_t datalen;
646         void *datalimit;
647         int dir = CTINFO2DIR(ctinfo);
648         struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
649         unsigned int nexthdr_off;
650
651         int oldsstate, oldcstate;
652         int ret;
653
654         /* don't do any tracking before tcp handshake complete */
655         if (ctinfo != IP_CT_ESTABLISHED 
656             && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
657                 DEBUGP("ctinfo = %u, skipping\n", ctinfo);
658                 return NF_ACCEPT;
659         }
660         
661         nexthdr_off = (*pskb)->nh.iph->ihl*4;
662         tcph = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, sizeof(_tcph),
663                                   &_tcph);
664         if (!tcph)
665                 return NF_ACCEPT;
666
667         /* not a complete TCP header? */
668         if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
669                 DEBUGP("tcplen = %u\n", tcplen);
670                 return NF_ACCEPT;
671         }
672
673
674         datalen = tcplen - tcph->doff * 4;
675
676         /* checksum invalid? */
677         if (tcp_v4_check(tcph, tcplen, (*pskb)->nh.iph->saddr,
678                          (*pskb)->nh.iph->daddr,
679                          csum_partial((char *) tcph, tcplen, 0))) {
680                 DEBUGP(" bad csum\n");
681                 /* W2K PPTP server sends TCP packets with wrong checksum :(( */
682                 //return NF_ACCEPT;
683         }
684
685         if (tcph->fin || tcph->rst) {
686                 DEBUGP("RST/FIN received, timeouting GRE\n");
687                 /* can't do this after real newnat */
688                 info->cstate = PPTP_CALL_NONE;
689
690                 /* untrack this call id, unexpect GRE packets */
691                 pptp_timeout_related(ct);
692         }
693
694         nexthdr_off += tcph->doff*4;
695         pptph = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4 + tcph->doff*4,
696                                    sizeof(_pptph), &_pptph);
697         if (!pptph) {
698                 DEBUGP("no full PPTP header, can't track\n");
699                 return NF_ACCEPT;
700         }
701
702         datalimit = (void *) pptph + datalen;
703
704         /* if it's not a control message we can't do anything with it */
705         if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
706             ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
707                 DEBUGP("not a control packet\n");
708                 return NF_ACCEPT;
709         }
710
711         oldsstate = info->sstate;
712         oldcstate = info->cstate;
713
714         LOCK_BH(&ip_pptp_lock);
715
716         nexthdr_off += sizeof(_pptph);
717         /* FIXME: We just blindly assume that the control connection is always
718          * established from PNS->PAC.  However, RFC makes no guarantee */
719         if (dir == IP_CT_DIR_ORIGINAL)
720                 /* client -> server (PNS -> PAC) */
721                 ret = pptp_outbound_pkt(pskb, tcph, nexthdr_off, datalen, ct,
722                                         ctinfo);
723         else
724                 /* server -> client (PAC -> PNS) */
725                 ret = pptp_inbound_pkt(pskb, tcph, nexthdr_off, datalen, ct,
726                                        ctinfo);
727         DEBUGP("sstate: %d->%d, cstate: %d->%d\n",
728                 oldsstate, info->sstate, oldcstate, info->cstate);
729         UNLOCK_BH(&ip_pptp_lock);
730
731         return ret;
732 }
733
734 /* control protocol helper */
735 static struct ip_conntrack_helper pptp = { 
736         .list = { NULL, NULL },
737         .name = "pptp", 
738         .me = THIS_MODULE,
739         .max_expected = 2,
740         .timeout = 5 * 60,
741         .tuple = { .src = { .ip = 0, 
742                             .u = { .tcp = { .port =  
743                                     __constant_htons(PPTP_CONTROL_PORT) } } 
744                           }, 
745                    .dst = { .ip = 0, 
746                             .u = { .all = 0 },
747                             .protonum = IPPROTO_TCP
748                           } 
749                  },
750         .mask = { .src = { .ip = 0, 
751                            .u = { .tcp = { .port = 0xffff } } 
752                          }, 
753                   .dst = { .ip = 0, 
754                            .u = { .all = 0 },
755                            .protonum = 0xff 
756                          } 
757                 },
758         .help = conntrack_pptp_help
759 };
760
761 /* ip_conntrack_pptp initialization */
762 static int __init init(void)
763 {
764         int retcode;
765
766         DEBUGP(" registering helper\n");
767         if ((retcode = ip_conntrack_helper_register(&pptp))) {
768                 printk(KERN_ERR "Unable to register conntrack application "
769                                 "helper for pptp: %d\n", retcode);
770                 return -EIO;
771         }
772
773         printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION);
774         return 0;
775 }
776
777 static void __exit fini(void)
778 {
779         ip_conntrack_helper_unregister(&pptp);
780         printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION);
781 }
782
783 module_init(init);
784 module_exit(fini);
785
786 EXPORT_SYMBOL(ip_pptp_lock);
787 EXPORT_SYMBOL(ip_nat_pptp_hook_outbound);
788 EXPORT_SYMBOL(ip_nat_pptp_hook_inbound);
789 EXPORT_SYMBOL(ip_nat_pptp_hook_exp_gre);
790 EXPORT_SYMBOL(ip_nat_pptp_hook_expectfn);