X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=kernel%2Flinux%2Fnet%2Fipv4%2Fnetfilter%2Fip_conntrack_pptp.c;h=0afe69fddeadc1b4c0f626fe1632d5649d6d1022;hb=57a096f051259ceaefd5977f30d269884e1dd248;hp=aafc2fe8df144a5ab8b478e3da00c99c544de71b;hpb=a69849c98808437716b821267cd97529c56f45b0;p=bcm963xx.git diff --git a/kernel/linux/net/ipv4/netfilter/ip_conntrack_pptp.c b/kernel/linux/net/ipv4/netfilter/ip_conntrack_pptp.c index aafc2fe8..0afe69fd 100755 --- a/kernel/linux/net/ipv4/netfilter/ip_conntrack_pptp.c +++ b/kernel/linux/net/ipv4/netfilter/ip_conntrack_pptp.c @@ -1,5 +1,5 @@ /* - * ip_conntrack_pptp.c - Version 1.9 + * ip_conntrack_pptp.c - Version 2.0 * * Connection tracking support for PPTP (Point to Point Tunneling Protocol). * PPTP is a a protocol for creating virtual private networks. @@ -9,7 +9,7 @@ * GRE is defined in RFC 1701 and RFC 1702. Documentation of * PPTP can be found in RFC 2637 * - * (C) 2000-2003 by Harald Welte + * (C) 2000-2005 by Harald Welte * * Development of this code funded by Astaro AG (http://www.astaro.com/) * @@ -32,6 +32,12 @@ * 2002-02-10 - Version 1.6 * - move to C99 style initializers * - remove second expectation if first arrives + * 2004-10-22 - Version 2.0 + * - merge Mandrake's 2.6.x port with recent 2.6.x API changes + * - fix lots of linear skb assumptions from Mandrake's port + * 2005-06-10 - Version 2.1 + * - use ip_conntrack_expect_free() instead of kfree() on the + * expect's (which are from the slab for quite some time) * */ @@ -47,7 +53,7 @@ #include #include -#define IP_CT_PPTP_VERSION "1.9" +#define IP_CT_PPTP_VERSION "2.1" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte "); @@ -57,8 +63,7 @@ DECLARE_LOCK(ip_pptp_lock); #if 0 #include "ip_conntrack_pptp_priv.h" -#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ - ": " format, ## args) +#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args) #else #define DEBUGP(format, args...) #endif @@ -156,6 +161,8 @@ static int pptp_timeout_related(struct ip_conntrack *ct) exp->sibling); exp->sibling->proto.gre.timeout = 0; exp->sibling->proto.gre.stream_timeout = 0; + /* refresh_acct will not modify counters if skb == NULL */ + //ip_ct_refresh_acct(exp->sibling, 0, NULL, 0); ip_ct_refresh(exp->sibling, 0); } @@ -169,129 +176,154 @@ exp_gre(struct ip_conntrack *master, u_int16_t callid, u_int16_t peer_callid) { - struct ip_conntrack_expect exp; struct ip_conntrack_tuple inv_tuple; - - memset(&exp, 0, sizeof(exp)); - /* tuple in original direction, PNS->PAC */ - exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; - exp.tuple.src.u.gre.key = htonl(ntohs(peer_callid)); - exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; - exp.tuple.dst.u.gre.key = htonl(ntohs(callid)); - exp.tuple.dst.u.gre.protocol = __constant_htons(GRE_PROTOCOL_PPTP); - exp.tuple.dst.u.gre.version = GRE_VERSION_PPTP; - exp.tuple.dst.protonum = IPPROTO_GRE; - - exp.mask.src.ip = 0xffffffff; - exp.mask.src.u.all = 0; - exp.mask.dst.u.all = 0; - exp.mask.dst.u.gre.key = 0xffffffff; - exp.mask.dst.u.gre.version = 0xff; - exp.mask.dst.u.gre.protocol = 0xffff; - exp.mask.dst.ip = 0xffffffff; - exp.mask.dst.protonum = 0xffff; + struct ip_conntrack_tuple exp_tuples[] = { + /* tuple in original direction, PNS->PAC */ + { .src = { .ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip, + .u = { .gre = { .key = htonl(ntohs(peer_callid)) } } + }, + .dst = { .ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip, + .u = { .gre = { .key = htonl(ntohs(callid)) } }, + .protonum = IPPROTO_GRE + }, + }, + /* tuple in reply direction, PAC->PNS */ + { .src = { .ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip, + .u = { .gre = { .key = htonl(ntohs(callid)) } } + }, + .dst = { .ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip, + .u = { .gre = { .key = htonl(ntohs(peer_callid)) } }, + .protonum = IPPROTO_GRE + }, + } + }, *exp_tuple; + + for (exp_tuple = exp_tuples; exp_tuple < &exp_tuples[2]; exp_tuple++) { + struct ip_conntrack_expect *exp; + + exp = ip_conntrack_expect_alloc(); + if (exp == NULL) + return 1; + + memcpy(&exp->tuple, exp_tuple, sizeof(exp->tuple)); + + exp->mask.src.ip = 0xffffffff; + exp->mask.src.u.all = 0; + exp->mask.dst.u.all = 0; + exp->mask.dst.u.gre.key = 0xffffffff; + exp->mask.dst.ip = 0xffffffff; + exp->mask.dst.protonum = 0xffff; - exp.seq = seq; - exp.expectfn = pptp_expectfn; + exp->seq = seq; + exp->expectfn = pptp_expectfn; - exp.help.exp_pptp_info.pac_call_id = ntohs(callid); - exp.help.exp_pptp_info.pns_call_id = ntohs(peer_callid); + exp->help.exp_pptp_info.pac_call_id = ntohs(callid); + exp->help.exp_pptp_info.pns_call_id = ntohs(peer_callid); - DEBUGP("calling expect_related "); - DUMP_TUPLE_RAW(&exp.tuple); - - /* Add GRE keymap entries */ - if (ip_ct_gre_keymap_add(&exp, &exp.tuple, 0) != 0) - return 1; - - invert_tuplepr(&inv_tuple, &exp.tuple); - if (ip_ct_gre_keymap_add(&exp, &inv_tuple, 1) != 0) { - ip_ct_gre_keymap_destroy(&exp); - return 1; - } + DEBUGP("calling expect_related "); + DUMP_TUPLE_RAW(&exp->tuple); - if (ip_conntrack_expect_related(&exp, master) != 0) { - ip_ct_gre_keymap_destroy(&exp); - DEBUGP("cannot expect_related()\n"); - return 1; - } - - /* tuple in reply direction, PAC->PNS */ - exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; - exp.tuple.src.u.gre.key = htonl(ntohs(callid)); - exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; - exp.tuple.dst.u.gre.key = htonl(ntohs(peer_callid)); + /* Add GRE keymap entries */ + if (ip_ct_gre_keymap_add(exp, &exp->tuple, 0) != 0) { + //ip_conntrack_expect_free(exp); + ip_conntrack_expect_put(exp); + return 1; + } - DEBUGP("calling expect_related "); - DUMP_TUPLE_RAW(&exp.tuple); - - /* Add GRE keymap entries */ - ip_ct_gre_keymap_add(&exp, &exp.tuple, 0); - invert_tuplepr(&inv_tuple, &exp.tuple); - ip_ct_gre_keymap_add(&exp, &inv_tuple, 1); - /* FIXME: cannot handle error correctly, since we need to free - * the above keymap :( */ + invert_tuplepr(&inv_tuple, &exp->tuple); + if (ip_ct_gre_keymap_add(exp, &inv_tuple, 1) != 0) { + ip_ct_gre_keymap_destroy(exp); + //ip_conntrack_expect_free(exp); + ip_conntrack_expect_put(exp); + return 1; + } - if (ip_conntrack_expect_related(&exp, master) != 0) { - /* free the second pair of keypmaps */ - ip_ct_gre_keymap_destroy(&exp); - DEBUGP("cannot expect_related():\n"); - return 1; + if (ip_conntrack_expect_related(exp, master) != 0) { + ip_ct_gre_keymap_destroy(exp); + //ip_conntrack_expect_free(exp); + ip_conntrack_expect_put(exp); + DEBUGP("cannot expect_related()\n"); + return 1; + } } return 0; } static inline int -pptp_inbound_pkt(struct tcphdr *tcph, - struct pptp_pkt_hdr *pptph, +pptp_inbound_pkt(struct sk_buff *skb, + struct tcphdr *tcph, + unsigned int ctlhoff, size_t datalen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) + struct ip_conntrack *ct) { - struct PptpControlHeader *ctlh; - union pptp_ctrl_union pptpReq; - + struct PptpControlHeader _ctlh, *ctlh; + unsigned int reqlen; + union pptp_ctrl_union _pptpReq, *pptpReq; struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; u_int16_t msg, *cid, *pcid; u_int32_t seq; - ctlh = (struct PptpControlHeader *) - ((char *) pptph + sizeof(struct pptp_pkt_hdr)); - pptpReq.rawreq = (void *) - ((char *) ctlh + sizeof(struct PptpControlHeader)); + ctlh = skb_header_pointer(skb, ctlhoff, sizeof(_ctlh), &_ctlh); + if (unlikely(!ctlh)) { + DEBUGP("error during skb_header_pointer\n"); + return NF_ACCEPT; + } + + reqlen = datalen - sizeof(struct pptp_pkt_hdr) - sizeof(_ctlh); + pptpReq = skb_header_pointer(skb, ctlhoff+sizeof(_ctlh), + reqlen, &_pptpReq); + if (unlikely(!pptpReq)) { + DEBUGP("error during skb_header_pointer\n"); + return NF_ACCEPT; + } msg = ntohs(ctlh->messageType); DEBUGP("inbound control message %s\n", strMName[msg]); switch (msg) { case PPTP_START_SESSION_REPLY: + if (reqlen < sizeof(_pptpReq.srep)) { + DEBUGP("%s: short packet\n", strMName[msg]); + break; + } + /* server confirms new control session */ if (info->sstate < PPTP_SESSION_REQUESTED) { DEBUGP("%s without START_SESS_REQUEST\n", strMName[msg]); break; } - if (pptpReq.srep->resultCode == PPTP_START_OK) + if (pptpReq->srep.resultCode == PPTP_START_OK) info->sstate = PPTP_SESSION_CONFIRMED; else info->sstate = PPTP_SESSION_ERROR; break; case PPTP_STOP_SESSION_REPLY: + if (reqlen < sizeof(_pptpReq.strep)) { + DEBUGP("%s: short packet\n", strMName[msg]); + break; + } + /* server confirms end of control session */ if (info->sstate > PPTP_SESSION_STOPREQ) { DEBUGP("%s without STOP_SESS_REQUEST\n", strMName[msg]); break; } - if (pptpReq.strep->resultCode == PPTP_STOP_OK) + if (pptpReq->strep.resultCode == PPTP_STOP_OK) info->sstate = PPTP_SESSION_NONE; else info->sstate = PPTP_SESSION_ERROR; break; case PPTP_OUT_CALL_REPLY: + if (reqlen < sizeof(_pptpReq.ocack)) { + DEBUGP("%s: short packet\n", strMName[msg]); + break; + } + /* server accepted call, we now expect GRE frames */ if (info->sstate != PPTP_SESSION_CONFIRMED) { DEBUGP("%s but no session\n", strMName[msg]); @@ -302,13 +334,13 @@ pptp_inbound_pkt(struct tcphdr *tcph, DEBUGP("%s without OUTCALL_REQ\n", strMName[msg]); break; } - if (pptpReq.ocack->resultCode != PPTP_OUTCALL_CONNECT) { + if (pptpReq->ocack.resultCode != PPTP_OUTCALL_CONNECT) { info->cstate = PPTP_CALL_NONE; break; } - cid = &pptpReq.ocack->callID; - pcid = &pptpReq.ocack->peersCallID; + cid = &pptpReq->ocack.callID; + pcid = &pptpReq->ocack.peersCallID; info->pac_call_id = ntohs(*cid); @@ -323,24 +355,37 @@ pptp_inbound_pkt(struct tcphdr *tcph, info->cstate = PPTP_CALL_OUT_CONF; - seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph); + seq = ntohl(tcph->seq) + sizeof(struct pptp_pkt_hdr) + + sizeof(struct PptpControlHeader) + + ((void *)pcid - (void *)pptpReq); + if (exp_gre(ct, seq, *cid, *pcid) != 0) printk("ip_conntrack_pptp: error during exp_gre\n"); break; case PPTP_IN_CALL_REQUEST: + if (reqlen < sizeof(_pptpReq.icack)) { + DEBUGP("%s: short packet\n", strMName[msg]); + break; + } + /* server tells us about incoming call request */ if (info->sstate != PPTP_SESSION_CONFIRMED) { DEBUGP("%s but no session\n", strMName[msg]); break; } - pcid = &pptpReq.icack->peersCallID; + pcid = &pptpReq->icack.peersCallID; DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid)); info->cstate = PPTP_CALL_IN_REQ; - info->pac_call_id= ntohs(*pcid); + info->pac_call_id = ntohs(*pcid); break; case PPTP_IN_CALL_CONNECT: + if (reqlen < sizeof(_pptpReq.iccon)) { + DEBUGP("%s: short packet\n", strMName[msg]); + break; + } + /* server tells us about incoming call established */ if (info->sstate != PPTP_SESSION_CONFIRMED) { DEBUGP("%s but no session\n", strMName[msg]); @@ -353,7 +398,7 @@ pptp_inbound_pkt(struct tcphdr *tcph, break; } - pcid = &pptpReq.iccon->peersCallID; + pcid = &pptpReq->iccon.peersCallID; cid = &info->pac_call_id; if (info->pns_call_id != ntohs(*pcid)) { @@ -366,15 +411,23 @@ pptp_inbound_pkt(struct tcphdr *tcph, info->cstate = PPTP_CALL_IN_CONF; /* we expect a GRE connection from PAC to PNS */ - seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph); + seq = ntohl(tcph->seq) + sizeof(struct pptp_pkt_hdr) + + sizeof(struct PptpControlHeader) + + ((void *)pcid - (void *)pptpReq); + if (exp_gre(ct, seq, *cid, *pcid) != 0) printk("ip_conntrack_pptp: error during exp_gre\n"); break; case PPTP_CALL_DISCONNECT_NOTIFY: + if (reqlen < sizeof(_pptpReq.disc)) { + DEBUGP("%s: short packet\n", strMName[msg]); + break; + } + /* server confirms disconnect */ - cid = &pptpReq.disc->callID; + cid = &pptpReq->disc.callID; DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid)); info->cstate = PPTP_CALL_NONE; @@ -400,19 +453,27 @@ pptp_inbound_pkt(struct tcphdr *tcph, } static inline int -pptp_outbound_pkt(struct tcphdr *tcph, - struct pptp_pkt_hdr *pptph, +pptp_outbound_pkt(struct sk_buff *skb, + struct tcphdr *tcph, + unsigned int ctlhoff, size_t datalen, - struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo) + struct ip_conntrack *ct) { - struct PptpControlHeader *ctlh; - union pptp_ctrl_union pptpReq; + struct PptpControlHeader _ctlh, *ctlh; + unsigned int reqlen; + union pptp_ctrl_union _pptpReq, *pptpReq; struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; u_int16_t msg, *cid, *pcid; - ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph)); - pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh)); + ctlh = skb_header_pointer(skb, ctlhoff, sizeof(_ctlh), &_ctlh); + if (!ctlh) + return NF_ACCEPT; + + reqlen = datalen - sizeof(struct pptp_pkt_hdr) - sizeof(_ctlh); + pptpReq = skb_header_pointer(skb, ctlhoff+sizeof(_ctlh), reqlen, + &_pptpReq); + if (!pptpReq) + return NF_ACCEPT; msg = ntohs(ctlh->messageType); DEBUGP("outbound control message %s\n", strMName[msg]); @@ -432,6 +493,11 @@ pptp_outbound_pkt(struct tcphdr *tcph, break; case PPTP_OUT_CALL_REQUEST: + if (reqlen < sizeof(_pptpReq.ocreq)) { + DEBUGP("%s: short packet\n", strMName[msg]); + /* FIXME: break; */ + } + /* client initiating connection to server */ if (info->sstate != PPTP_SESSION_CONFIRMED) { DEBUGP("%s but no session\n", @@ -440,11 +506,16 @@ pptp_outbound_pkt(struct tcphdr *tcph, } info->cstate = PPTP_CALL_OUT_REQ; /* track PNS call id */ - cid = &pptpReq.ocreq->callID; + cid = &pptpReq->ocreq.callID; DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid)); info->pns_call_id = ntohs(*cid); break; case PPTP_IN_CALL_REPLY: + if (reqlen < sizeof(_pptpReq.icack)) { + DEBUGP("%s: short packet\n", strMName[msg]); + break; + } + /* client answers incoming call */ if (info->cstate != PPTP_CALL_IN_REQ && info->cstate != PPTP_CALL_IN_REP) { @@ -452,11 +523,11 @@ pptp_outbound_pkt(struct tcphdr *tcph, strMName[msg]); break; } - if (pptpReq.icack->resultCode != PPTP_INCALL_ACCEPT) { + if (pptpReq->icack.resultCode != PPTP_INCALL_ACCEPT) { info->cstate = PPTP_CALL_NONE; break; } - pcid = &pptpReq.icack->peersCallID; + pcid = &pptpReq->icack.peersCallID; if (info->pac_call_id != ntohs(*pcid)) { DEBUGP("%s for unknown call %u\n", strMName[msg], ntohs(*pcid)); @@ -465,7 +536,7 @@ pptp_outbound_pkt(struct tcphdr *tcph, DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*pcid)); /* part two of the three-way handshake */ info->cstate = PPTP_CALL_IN_REP; - info->pns_call_id = ntohs(pptpReq.icack->callID); + info->pns_call_id = ntohs(pptpReq->icack.callID); break; case PPTP_CALL_CLEAR_REQUEST: @@ -502,14 +573,15 @@ conntrack_pptp_help(struct sk_buff *skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { - struct pptp_pkt_hdr *pptph; + struct pptp_pkt_hdr _pptph, *pptph; - struct tcphdr *tcph = (void *) skb->nh.iph + skb->nh.iph->ihl * 4; + struct tcphdr _tcph, *tcph; u_int32_t tcplen = skb->len - skb->nh.iph->ihl * 4; - u_int32_t datalen = tcplen - tcph->doff * 4; + u_int32_t datalen; void *datalimit; int dir = CTINFO2DIR(ctinfo); struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; + unsigned int nexthdr_off; int oldsstate, oldcstate; int ret; @@ -521,12 +593,21 @@ conntrack_pptp_help(struct sk_buff *skb, return NF_ACCEPT; } + nexthdr_off = skb->nh.iph->ihl*4; + tcph = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_tcph), + &_tcph); + if (!tcph) + return NF_ACCEPT; + /* not a complete TCP header? */ if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { DEBUGP("tcplen = %u\n", tcplen); return NF_ACCEPT; } + + datalen = tcplen - tcph->doff * 4; + /* checksum invalid? */ if (tcp_v4_check(tcph, tcplen, skb->nh.iph->saddr, skb->nh.iph->daddr, csum_partial((char *) tcph, tcplen, 0))) { @@ -544,18 +625,18 @@ conntrack_pptp_help(struct sk_buff *skb, pptp_timeout_related(ct); } - - pptph = (struct pptp_pkt_hdr *) ((void *) tcph + tcph->doff * 4); - datalimit = (void *) pptph + datalen; - - /* not a full pptp packet header? */ - if ((void *) pptph+sizeof(*pptph) >= datalimit) { + nexthdr_off += tcph->doff*4; + pptph = skb_header_pointer(skb, skb->nh.iph->ihl*4 + tcph->doff*4, + sizeof(_pptph), &_pptph); + if (!pptph) { DEBUGP("no full PPTP header, can't track\n"); return NF_ACCEPT; } - + + datalimit = (void *) pptph + datalen; + /* if it's not a control message we can't do anything with it */ - if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || + if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { DEBUGP("not a control packet\n"); return NF_ACCEPT; @@ -566,14 +647,15 @@ conntrack_pptp_help(struct sk_buff *skb, LOCK_BH(&ip_pptp_lock); + nexthdr_off += sizeof(_pptph); /* FIXME: We just blindly assume that the control connection is always * established from PNS->PAC. However, RFC makes no guarantee */ if (dir == IP_CT_DIR_ORIGINAL) /* client -> server (PNS -> PAC) */ - ret = pptp_outbound_pkt(tcph, pptph, datalen, ct, ctinfo); + ret = pptp_outbound_pkt(skb, tcph, nexthdr_off, datalen, ct); else /* server -> client (PAC -> PNS) */ - ret = pptp_inbound_pkt(tcph, pptph, datalen, ct, ctinfo); + ret = pptp_inbound_pkt(skb, tcph, nexthdr_off, datalen, ct); DEBUGP("sstate: %d->%d, cstate: %d->%d\n", oldsstate, info->sstate, oldcstate, info->cstate); UNLOCK_BH(&ip_pptp_lock); @@ -615,9 +697,8 @@ static int __init init(void) int retcode; DEBUGP(__FILE__ ": registering helper\n"); - return 0; if ((retcode = ip_conntrack_helper_register(&pptp))) { - printk(KERN_ERR "Unable to register conntrack application " + printk(KERN_ERR "Unable to register conntrack application " "helper for pptp: %d\n", retcode); return -EIO; } @@ -628,7 +709,6 @@ static int __init init(void) static void __exit fini(void) { - return 0; ip_conntrack_helper_unregister(&pptp); printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION); }