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