2 * ip_conntrack_pptp.c - Version 2.0
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
12 * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
14 * Development of this code funded by Astaro AG (http://www.astaro.com/)
17 * - We blindly assume that control connections are always
18 * established in PNS->PAC direction. This is a violation
21 * TODO: - finish support for multiple calls within one session
22 * (needs expect reservations in newnat)
23 * - testing of incoming PPTP calls
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)
44 #include <linux/config.h>
45 #include <linux/module.h>
46 #include <linux/netfilter.h>
48 #include <net/checksum.h>
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>
56 #define IP_CT_PPTP_VERSION "2.1"
58 MODULE_LICENSE("GPL");
59 MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
60 MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP");
62 DECLARE_LOCK(ip_pptp_lock);
65 #include "ip_conntrack_pptp_priv.h"
66 #define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
68 #define DEBUGP(format, args...)
72 #define MINS * 60 SECS
73 #define HOURS * 60 MINS
74 #define DAYS * 24 HOURS
76 #define PPTP_GRE_TIMEOUT (10 MINS)
77 #define PPTP_GRE_STREAM_TIMEOUT (5 DAYS)
79 static int pptp_expectfn(struct ip_conntrack *ct)
81 struct ip_conntrack *master;
82 struct ip_conntrack_expect *exp;
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;
89 master = master_ct(ct);
91 DEBUGP(" no master!!!\n");
97 DEBUGP("no expectation!!\n");
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);
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);
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;
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,
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);
141 /* timeout GRE data connections */
142 static int pptp_timeout_related(struct ip_conntrack *ct)
144 struct list_head *cur_item, *next;
145 struct ip_conntrack_expect *exp;
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,
154 ip_ct_gre_keymap_destroy(exp);
156 ip_conntrack_unexpect_related(exp);
160 DEBUGP("setting timeout of conntrack %p to 0\n",
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);
172 /* expect GRE connections (PNS->PAC and PAC->PNS direction) */
174 exp_gre(struct ip_conntrack *master,
177 u_int16_t peer_callid)
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)) } }
185 .dst = { .ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip,
186 .u = { .gre = { .key = htonl(ntohs(callid)) } },
187 .protonum = IPPROTO_GRE
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)) } }
194 .dst = { .ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip,
195 .u = { .gre = { .key = htonl(ntohs(peer_callid)) } },
196 .protonum = IPPROTO_GRE
201 for (exp_tuple = exp_tuples; exp_tuple < &exp_tuples[2]; exp_tuple++) {
202 struct ip_conntrack_expect *exp;
204 exp = ip_conntrack_expect_alloc();
208 memcpy(&exp->tuple, exp_tuple, sizeof(exp->tuple));
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;
218 exp->expectfn = pptp_expectfn;
220 exp->help.exp_pptp_info.pac_call_id = ntohs(callid);
221 exp->help.exp_pptp_info.pns_call_id = ntohs(peer_callid);
223 DEBUGP("calling expect_related ");
224 DUMP_TUPLE_RAW(&exp->tuple);
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);
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);
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");
254 pptp_inbound_pkt(struct sk_buff *skb,
256 unsigned int ctlhoff,
258 struct ip_conntrack *ct)
260 struct PptpControlHeader _ctlh, *ctlh;
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;
267 ctlh = skb_header_pointer(skb, ctlhoff, sizeof(_ctlh), &_ctlh);
268 if (unlikely(!ctlh)) {
269 DEBUGP("error during skb_header_pointer\n");
273 reqlen = datalen - sizeof(struct pptp_pkt_hdr) - sizeof(_ctlh);
274 pptpReq = skb_header_pointer(skb, ctlhoff+sizeof(_ctlh),
276 if (unlikely(!pptpReq)) {
277 DEBUGP("error during skb_header_pointer\n");
281 msg = ntohs(ctlh->messageType);
282 DEBUGP("inbound control message %s\n", strMName[msg]);
285 case PPTP_START_SESSION_REPLY:
286 if (reqlen < sizeof(_pptpReq.srep)) {
287 DEBUGP("%s: short packet\n", strMName[msg]);
291 /* server confirms new control session */
292 if (info->sstate < PPTP_SESSION_REQUESTED) {
293 DEBUGP("%s without START_SESS_REQUEST\n",
297 if (pptpReq->srep.resultCode == PPTP_START_OK)
298 info->sstate = PPTP_SESSION_CONFIRMED;
300 info->sstate = PPTP_SESSION_ERROR;
303 case PPTP_STOP_SESSION_REPLY:
304 if (reqlen < sizeof(_pptpReq.strep)) {
305 DEBUGP("%s: short packet\n", strMName[msg]);
309 /* server confirms end of control session */
310 if (info->sstate > PPTP_SESSION_STOPREQ) {
311 DEBUGP("%s without STOP_SESS_REQUEST\n",
315 if (pptpReq->strep.resultCode == PPTP_STOP_OK)
316 info->sstate = PPTP_SESSION_NONE;
318 info->sstate = PPTP_SESSION_ERROR;
321 case PPTP_OUT_CALL_REPLY:
322 if (reqlen < sizeof(_pptpReq.ocack)) {
323 DEBUGP("%s: short packet\n", strMName[msg]);
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]);
332 if (info->cstate != PPTP_CALL_OUT_REQ &&
333 info->cstate != PPTP_CALL_OUT_CONF) {
334 DEBUGP("%s without OUTCALL_REQ\n", strMName[msg]);
337 if (pptpReq->ocack.resultCode != PPTP_OUTCALL_CONNECT) {
338 info->cstate = PPTP_CALL_NONE;
342 cid = &pptpReq->ocack.callID;
343 pcid = &pptpReq->ocack.peersCallID;
345 info->pac_call_id = ntohs(*cid);
347 if (htons(info->pns_call_id) != *pcid) {
348 DEBUGP("%s for unknown callid %u\n",
349 strMName[msg], ntohs(*pcid));
353 DEBUGP("%s, CID=%X, PCID=%X\n", strMName[msg],
354 ntohs(*cid), ntohs(*pcid));
356 info->cstate = PPTP_CALL_OUT_CONF;
358 seq = ntohl(tcph->seq) + sizeof(struct pptp_pkt_hdr)
359 + sizeof(struct PptpControlHeader)
360 + ((void *)pcid - (void *)pptpReq);
362 if (exp_gre(ct, seq, *cid, *pcid) != 0)
363 printk("ip_conntrack_pptp: error during exp_gre\n");
366 case PPTP_IN_CALL_REQUEST:
367 if (reqlen < sizeof(_pptpReq.icack)) {
368 DEBUGP("%s: short packet\n", strMName[msg]);
372 /* server tells us about incoming call request */
373 if (info->sstate != PPTP_SESSION_CONFIRMED) {
374 DEBUGP("%s but no session\n", strMName[msg]);
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);
383 case PPTP_IN_CALL_CONNECT:
384 if (reqlen < sizeof(_pptpReq.iccon)) {
385 DEBUGP("%s: short packet\n", strMName[msg]);
389 /* server tells us about incoming call established */
390 if (info->sstate != PPTP_SESSION_CONFIRMED) {
391 DEBUGP("%s but no session\n", strMName[msg]);
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",
401 pcid = &pptpReq->iccon.peersCallID;
402 cid = &info->pac_call_id;
404 if (info->pns_call_id != ntohs(*pcid)) {
405 DEBUGP("%s for unknown CallID %u\n",
406 strMName[msg], ntohs(*cid));
410 DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid));
411 info->cstate = PPTP_CALL_IN_CONF;
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);
418 if (exp_gre(ct, seq, *cid, *pcid) != 0)
419 printk("ip_conntrack_pptp: error during exp_gre\n");
423 case PPTP_CALL_DISCONNECT_NOTIFY:
424 if (reqlen < sizeof(_pptpReq.disc)) {
425 DEBUGP("%s: short packet\n", strMName[msg]);
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;
434 /* untrack this call id, unexpect GRE packets */
435 pptp_timeout_related(ct);
438 case PPTP_WAN_ERROR_NOTIFY:
441 case PPTP_ECHO_REQUEST:
442 case PPTP_ECHO_REPLY:
443 /* I don't have to explain these ;) */
446 DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)
447 ? strMName[msg]:strMName[0], msg);
456 pptp_outbound_pkt(struct sk_buff *skb,
458 unsigned int ctlhoff,
460 struct ip_conntrack *ct)
462 struct PptpControlHeader _ctlh, *ctlh;
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;
468 ctlh = skb_header_pointer(skb, ctlhoff, sizeof(_ctlh), &_ctlh);
472 reqlen = datalen - sizeof(struct pptp_pkt_hdr) - sizeof(_ctlh);
473 pptpReq = skb_header_pointer(skb, ctlhoff+sizeof(_ctlh), reqlen,
478 msg = ntohs(ctlh->messageType);
479 DEBUGP("outbound control message %s\n", strMName[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",
488 info->sstate = PPTP_SESSION_REQUESTED;
490 case PPTP_STOP_SESSION_REQUEST:
491 /* client requests end of control session */
492 info->sstate = PPTP_SESSION_STOPREQ;
495 case PPTP_OUT_CALL_REQUEST:
496 if (reqlen < sizeof(_pptpReq.ocreq)) {
497 DEBUGP("%s: short packet\n", strMName[msg]);
501 /* client initiating connection to server */
502 if (info->sstate != PPTP_SESSION_CONFIRMED) {
503 DEBUGP("%s but no session\n",
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);
513 case PPTP_IN_CALL_REPLY:
514 if (reqlen < sizeof(_pptpReq.icack)) {
515 DEBUGP("%s: short packet\n", strMName[msg]);
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",
526 if (pptpReq->icack.resultCode != PPTP_INCALL_ACCEPT) {
527 info->cstate = PPTP_CALL_NONE;
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));
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);
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");
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;
553 case PPTP_SET_LINK_INFO:
555 case PPTP_ECHO_REQUEST:
556 case PPTP_ECHO_REPLY:
557 /* I don't have to explain these ;) */
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 */
570 /* track caller id inside control connection, call expect_related */
572 conntrack_pptp_help(struct sk_buff *skb,
573 struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
576 struct pptp_pkt_hdr _pptph, *pptph;
578 struct tcphdr _tcph, *tcph;
579 u_int32_t tcplen = skb->len - skb->nh.iph->ihl * 4;
582 int dir = CTINFO2DIR(ctinfo);
583 struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
584 unsigned int nexthdr_off;
586 int oldsstate, oldcstate;
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);
596 nexthdr_off = skb->nh.iph->ihl*4;
597 tcph = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_tcph),
602 /* not a complete TCP header? */
603 if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
604 DEBUGP("tcplen = %u\n", tcplen);
609 datalen = tcplen - tcph->doff * 4;
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 :(( */
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;
624 /* untrack this call id, unexpect GRE packets */
625 pptp_timeout_related(ct);
628 nexthdr_off += tcph->doff*4;
629 pptph = skb_header_pointer(skb, skb->nh.iph->ihl*4 + tcph->doff*4,
630 sizeof(_pptph), &_pptph);
632 DEBUGP("no full PPTP header, can't track\n");
636 datalimit = (void *) pptph + datalen;
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");
645 oldsstate = info->sstate;
646 oldcstate = info->cstate;
648 LOCK_BH(&ip_pptp_lock);
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);
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);
666 /* control protocol helper */
667 static struct ip_conntrack_helper pptp = {
668 .list = { NULL, NULL },
670 .flags = IP_CT_HELPER_F_REUSE_EXPECT,
674 .tuple = { .src = { .ip = 0,
675 .u = { .tcp = { .port =
676 __constant_htons(PPTP_CONTROL_PORT) } }
680 .protonum = IPPROTO_TCP
683 .mask = { .src = { .ip = 0,
684 .u = { .tcp = { .port = 0xffff } }
691 .help = conntrack_pptp_help
694 /* ip_conntrack_pptp initialization */
695 static int __init init(void)
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);
706 printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION);
710 static void __exit fini(void)
712 ip_conntrack_helper_unregister(&pptp);
713 printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION);
719 EXPORT_SYMBOL(ip_pptp_lock);